1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include <linux/magic.h>
15 #include "alloc-util.h"
16 #include "blkid-util.h"
18 #include "chase-symlinks.h"
20 #include "devnum-util.h"
21 #include "dirent-util.h"
22 #include "dissect-image.h"
24 #include "efi-loader.h"
33 #include "glyph-util.h"
34 #include "main-func.h"
36 #include "mount-util.h"
39 #include "parse-argument.h"
40 #include "parse-util.h"
41 #include "pretty-print.h"
42 #include "random-util.h"
44 #include "stat-util.h"
45 #include "stdio-util.h"
46 #include "string-table.h"
47 #include "string-util.h"
49 #include "sync-util.h"
50 #include "terminal-util.h"
51 #include "tmpfile-util.h"
52 #include "tmpfile-util-label.h"
53 #include "tpm2-util.h"
54 #include "umask-util.h"
60 static char *arg_esp_path
= NULL
;
61 static char *arg_xbootldr_path
= NULL
;
62 static bool arg_print_esp_path
= false;
63 static bool arg_print_dollar_boot_path
= false;
64 static bool arg_touch_variables
= true;
65 static PagerFlags arg_pager_flags
= 0;
66 static bool arg_graceful
= false;
67 static bool arg_quiet
= false;
68 static int arg_make_entry_directory
= false; /* tri-state: < 0 for automatic logic */
69 static sd_id128_t arg_machine_id
= SD_ID128_NULL
;
70 static char *arg_install_layout
= NULL
;
72 ARG_ENTRY_TOKEN_MACHINE_ID
,
73 ARG_ENTRY_TOKEN_OS_IMAGE_ID
,
74 ARG_ENTRY_TOKEN_OS_ID
,
75 ARG_ENTRY_TOKEN_LITERAL
,
77 } arg_entry_token_type
= ARG_ENTRY_TOKEN_AUTO
;
78 static char *arg_entry_token
= NULL
;
79 static JsonFormatFlags arg_json_format_flags
= JSON_FORMAT_OFF
;
80 static bool arg_arch_all
= false;
81 static char *arg_root
= NULL
;
82 static char *arg_image
= NULL
;
84 ARG_INSTALL_SOURCE_IMAGE
,
85 ARG_INSTALL_SOURCE_HOST
,
86 ARG_INSTALL_SOURCE_AUTO
,
87 } arg_install_source
= ARG_INSTALL_SOURCE_AUTO
;
89 STATIC_DESTRUCTOR_REGISTER(arg_esp_path
, freep
);
90 STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path
, freep
);
91 STATIC_DESTRUCTOR_REGISTER(arg_install_layout
, freep
);
92 STATIC_DESTRUCTOR_REGISTER(arg_entry_token
, freep
);
93 STATIC_DESTRUCTOR_REGISTER(arg_root
, freep
);
94 STATIC_DESTRUCTOR_REGISTER(arg_image
, freep
);
96 static const char *arg_dollar_boot_path(void) {
97 /* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */
98 return arg_xbootldr_path
?: arg_esp_path
;
101 static int acquire_esp(
102 bool unprivileged_mode
,
105 uint64_t *ret_pstart
,
107 sd_id128_t
*ret_uuid
,
113 /* Find the ESP, and log about errors. Note that find_esp_and_warn() will log in all error cases on
114 * its own, except for ENOKEY (which is good, we want to show our own message in that case,
115 * suggesting use of --esp-path=) and EACCESS (only when we request unprivileged mode; in this case
116 * we simply eat up the error here, so that --list and --status work too, without noise about
119 r
= find_esp_and_warn(arg_root
, arg_esp_path
, unprivileged_mode
, &np
, ret_part
, ret_pstart
, ret_psize
, ret_uuid
, ret_devid
);
122 return log_full_errno(arg_quiet
? LOG_DEBUG
: LOG_INFO
, r
,
123 "Couldn't find EFI system partition, skipping.");
125 return log_error_errno(r
,
126 "Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n"
127 "Alternatively, use --esp-path= to specify path to mount point.");
132 free_and_replace(arg_esp_path
, np
);
133 log_debug("Using EFI System Partition at %s.", arg_esp_path
);
138 static int acquire_xbootldr(
139 bool unprivileged_mode
,
140 sd_id128_t
*ret_uuid
,
146 r
= find_xbootldr_and_warn(arg_root
, arg_xbootldr_path
, unprivileged_mode
, &np
, ret_uuid
, ret_devid
);
148 log_debug_errno(r
, "Didn't find an XBOOTLDR partition, using the ESP as $BOOT.");
149 arg_xbootldr_path
= mfree(arg_xbootldr_path
);
152 *ret_uuid
= SD_ID128_NULL
;
160 free_and_replace(arg_xbootldr_path
, np
);
161 log_debug("Using XBOOTLDR partition at %s as $BOOT.", arg_xbootldr_path
);
166 static int load_etc_machine_id(void) {
169 r
= sd_id128_get_machine(&arg_machine_id
);
170 if (IN_SET(r
, -ENOENT
, -ENOMEDIUM
)) /* Not set or empty */
173 return log_error_errno(r
, "Failed to get machine-id: %m");
175 log_debug("Loaded machine ID %s from /etc/machine-id.", SD_ID128_TO_STRING(arg_machine_id
));
179 static int load_etc_machine_info(void) {
180 /* systemd v250 added support to store the kernel-install layout setting and the machine ID to use
181 * for setting up the ESP in /etc/machine-info. The newer /etc/kernel/entry-token file, as well as
182 * the $layout field in /etc/kernel/install.conf are better replacements for this though, hence this
183 * has been deprecated and is only returned for compatibility. */
184 _cleanup_free_
char *s
= NULL
, *layout
= NULL
;
187 r
= parse_env_file(NULL
, "/etc/machine-info",
188 "KERNEL_INSTALL_LAYOUT", &layout
,
189 "KERNEL_INSTALL_MACHINE_ID", &s
);
193 return log_error_errno(r
, "Failed to parse /etc/machine-info: %m");
197 log_notice("Read $KERNEL_INSTALL_MACHINE_ID from /etc/machine-info. "
198 "Please move it to /etc/kernel/entry-token.");
200 r
= sd_id128_from_string(s
, &arg_machine_id
);
202 return log_error_errno(r
, "Failed to parse KERNEL_INSTALL_MACHINE_ID=%s in /etc/machine-info: %m", s
);
204 log_debug("Loaded KERNEL_INSTALL_MACHINE_ID=%s from KERNEL_INSTALL_MACHINE_ID in /etc/machine-info.",
205 SD_ID128_TO_STRING(arg_machine_id
));
208 if (!isempty(layout
)) {
210 log_notice("Read $KERNEL_INSTALL_LAYOUT from /etc/machine-info. "
211 "Please move it to the layout= setting of /etc/kernel/install.conf.");
213 log_debug("KERNEL_INSTALL_LAYOUT=%s is specified in /etc/machine-info.", layout
);
214 free_and_replace(arg_install_layout
, layout
);
220 static int load_etc_kernel_install_conf(void) {
221 _cleanup_free_
char *layout
= NULL
;
224 r
= parse_env_file(NULL
, "/etc/kernel/install.conf",
229 return log_error_errno(r
, "Failed to parse /etc/kernel/install.conf: %m");
231 if (!isempty(layout
)) {
232 log_debug("layout=%s is specified in /etc/machine-info.", layout
);
233 free_and_replace(arg_install_layout
, layout
);
239 static int settle_entry_token(void) {
242 switch (arg_entry_token_type
) {
244 case ARG_ENTRY_TOKEN_AUTO
: {
245 _cleanup_free_
char *buf
= NULL
;
246 r
= read_one_line_file("/etc/kernel/entry-token", &buf
);
247 if (r
< 0 && r
!= -ENOENT
)
248 return log_error_errno(r
, "Failed to read /etc/kernel/entry-token: %m");
251 free_and_replace(arg_entry_token
, buf
);
252 arg_entry_token_type
= ARG_ENTRY_TOKEN_LITERAL
;
253 } else if (sd_id128_is_null(arg_machine_id
)) {
254 _cleanup_free_
char *id
= NULL
, *image_id
= NULL
;
256 r
= parse_os_release(NULL
,
257 "IMAGE_ID", &image_id
,
260 return log_error_errno(r
, "Failed to load /etc/os-release: %m");
262 if (!isempty(image_id
)) {
263 free_and_replace(arg_entry_token
, image_id
);
264 arg_entry_token_type
= ARG_ENTRY_TOKEN_OS_IMAGE_ID
;
265 } else if (!isempty(id
)) {
266 free_and_replace(arg_entry_token
, id
);
267 arg_entry_token_type
= ARG_ENTRY_TOKEN_OS_ID
;
269 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No machine ID set, and /etc/os-release carries no ID=/IMAGE_ID= fields.");
271 r
= free_and_strdup_warn(&arg_entry_token
, SD_ID128_TO_STRING(arg_machine_id
));
275 arg_entry_token_type
= ARG_ENTRY_TOKEN_MACHINE_ID
;
281 case ARG_ENTRY_TOKEN_MACHINE_ID
:
282 if (sd_id128_is_null(arg_machine_id
))
283 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No machine ID set.");
285 r
= free_and_strdup_warn(&arg_entry_token
, SD_ID128_TO_STRING(arg_machine_id
));
291 case ARG_ENTRY_TOKEN_OS_IMAGE_ID
: {
292 _cleanup_free_
char *buf
= NULL
;
294 r
= parse_os_release(NULL
, "IMAGE_ID", &buf
);
296 return log_error_errno(r
, "Failed to load /etc/os-release: %m");
299 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "IMAGE_ID= field not set in /etc/os-release.");
301 free_and_replace(arg_entry_token
, buf
);
305 case ARG_ENTRY_TOKEN_OS_ID
: {
306 _cleanup_free_
char *buf
= NULL
;
308 r
= parse_os_release(NULL
, "ID", &buf
);
310 return log_error_errno(r
, "Failed to load /etc/os-release: %m");
313 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "ID= field not set in /etc/os-release.");
315 free_and_replace(arg_entry_token
, buf
);
319 case ARG_ENTRY_TOKEN_LITERAL
:
320 assert(!isempty(arg_entry_token
)); /* already filled in by command line parser */
324 if (isempty(arg_entry_token
) || !string_is_safe(arg_entry_token
))
325 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Selected entry token not valid: %s", arg_entry_token
);
327 log_debug("Using entry token: %s", arg_entry_token
);
331 static bool use_boot_loader_spec_type1(void) {
332 /* If the layout is not specified, or if it is set explicitly to "bls" we assume Boot Loader
333 * Specification Type #1 is the chosen format for our boot loader entries */
334 return !arg_install_layout
|| streq(arg_install_layout
, "bls");
337 static int settle_make_entry_directory(void) {
340 r
= load_etc_machine_id();
344 r
= load_etc_machine_info();
348 r
= load_etc_kernel_install_conf();
352 r
= settle_entry_token();
356 bool layout_type1
= use_boot_loader_spec_type1();
357 if (arg_make_entry_directory
< 0) { /* Automatic mode */
359 if (arg_entry_token
== ARG_ENTRY_TOKEN_MACHINE_ID
) {
360 r
= path_is_temporary_fs("/etc/machine-id");
362 return log_debug_errno(r
, "Couldn't determine whether /etc/machine-id is on a temporary file system: %m");
364 arg_make_entry_directory
= r
== 0;
366 arg_make_entry_directory
= true;
368 arg_make_entry_directory
= false;
371 if (arg_make_entry_directory
> 0 && !layout_type1
)
372 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
373 "KERNEL_INSTALL_LAYOUT=%s is configured, but Boot Loader Specification Type #1 entry directory creation was requested.",
379 /* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */
380 static int get_file_version(int fd
, char **v
) {
390 if (fstat(fd
, &st
) < 0)
391 return log_error_errno(errno
, "Failed to stat EFI binary: %m");
393 r
= stat_verify_regular(&st
);
395 return log_error_errno(r
, "EFI binary is not a regular file: %m");
397 if (st
.st_size
< 27 || file_offset_beyond_memory_size(st
.st_size
)) {
402 buf
= mmap(NULL
, st
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
403 if (buf
== MAP_FAILED
)
404 return log_error_errno(errno
, "Failed to memory map EFI binary: %m");
406 s
= mempmem_safe(buf
, st
.st_size
- 8, "#### LoaderInfo: ", 17);
410 e
= memmem_safe(s
, st
.st_size
- (s
- buf
), " ####", 5);
411 if (!e
|| e
- s
< 3) {
412 r
= log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Malformed version string.");
416 x
= strndup(s
, e
- s
);
424 (void) munmap(buf
, st
.st_size
);
429 static const char *get_efi_arch(void) {
430 /* Detect EFI firmware architecture of the running system. On mixed mode systems, it could be 32bit
431 * while the kernel is running in 64bit. */
434 _cleanup_free_
char *platform_size
= NULL
;
437 r
= read_one_line_file("/sys/firmware/efi/fw_platform_size", &platform_size
);
439 return EFI_MACHINE_TYPE_NAME
;
441 log_warning_errno(r
, "Error reading EFI firmware word size, assuming '%u': %m", __WORDSIZE
);
442 return EFI_MACHINE_TYPE_NAME
;
445 if (streq(platform_size
, "64"))
446 return EFI_MACHINE_TYPE_NAME
;
447 if (streq(platform_size
, "32"))
451 "Unknown EFI firmware word size '%s', using default word size '%u' instead.",
456 return EFI_MACHINE_TYPE_NAME
;
459 static int enumerate_binaries(
460 const char *esp_path
,
466 _cleanup_closedir_
DIR *d
= NULL
;
475 p
= prefix_roota(esp_path
, path
);
481 return log_error_errno(errno
, "Failed to read \"%s\": %m", p
);
484 FOREACH_DIRENT(de
, d
, break) {
485 _cleanup_free_
char *v
= NULL
;
486 _cleanup_close_
int fd
= -1;
488 if (!endswith_no_case(de
->d_name
, ".efi"))
491 if (prefix
&& !startswith_no_case(de
->d_name
, prefix
))
494 fd
= openat(dirfd(d
), de
->d_name
, O_RDONLY
|O_CLOEXEC
);
496 return log_error_errno(errno
, "Failed to open \"%s/%s\" for reading: %m", p
, de
->d_name
);
498 r
= get_file_version(fd
, &v
);
502 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 */
504 *is_first
? "File:" : " ",
505 special_glyph(SPECIAL_GLYPH_TREE_BRANCH
), *previous
);
507 *previous
= mfree(*previous
);
510 /* Do not output this entry immediately, but store what should be printed in a state
511 * variable, because we only will know the tree glyph to print (branch or final edge) once we
512 * read one more entry */
514 r
= asprintf(previous
, "/%s/%s (%s%s%s)", path
, de
->d_name
, ansi_highlight(), v
, ansi_normal());
516 r
= asprintf(previous
, "/%s/%s", path
, de
->d_name
);
526 static int status_binaries(const char *esp_path
, sd_id128_t partition
) {
527 _cleanup_free_
char *last
= NULL
;
528 bool is_first
= true;
531 printf("%sAvailable Boot Loaders on ESP:%s\n", ansi_underline(), ansi_normal());
534 printf(" ESP: Cannot find or access mount point of ESP.\n\n");
538 printf(" ESP: %s", esp_path
);
539 if (!sd_id128_is_null(partition
))
540 printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR
")", SD_ID128_FORMAT_VAL(partition
));
543 r
= enumerate_binaries(esp_path
, "EFI/systemd", NULL
, &last
, &is_first
);
549 k
= enumerate_binaries(esp_path
, "EFI/BOOT", "boot", &last
, &is_first
);
555 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 */
557 is_first
? "File:" : " ",
558 special_glyph(SPECIAL_GLYPH_TREE_RIGHT
), last
);
560 if (r
== 0 && !arg_quiet
)
561 log_info("systemd-boot not installed in ESP.");
562 if (k
== 0 && !arg_quiet
)
563 log_info("No default/fallback boot loader installed in ESP.");
569 static int print_efi_option(uint16_t id
, int *n_printed
, bool in_order
) {
570 _cleanup_free_
char *title
= NULL
;
571 _cleanup_free_
char *path
= NULL
;
572 sd_id128_t partition
;
578 r
= efi_get_boot_option(id
, &title
, &partition
, &path
, &active
);
580 return log_error_errno(r
, "Failed to read boot option %u: %m", id
);
582 /* print only configured entries with partition information */
583 if (!path
|| sd_id128_is_null(partition
)) {
584 log_debug("Ignoring boot entry %u without partition information.", id
);
588 efi_tilt_backslashes(path
);
590 if (*n_printed
== 0) /* Print section title before first entry */
591 printf("%sBoot Loaders Listed in EFI Variables:%s\n", ansi_underline(), ansi_normal());
593 printf(" Title: %s%s%s\n", ansi_highlight(), strna(title
), ansi_normal());
594 printf(" ID: 0x%04X\n", id
);
595 printf(" Status: %sactive%s\n", active
? "" : "in", in_order
? ", boot-order" : "");
596 printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR
"\n",
597 SD_ID128_FORMAT_VAL(partition
));
598 printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT
), path
);
605 static int status_variables(void) {
606 _cleanup_free_
uint16_t *options
= NULL
, *order
= NULL
;
607 int n_options
, n_order
, n_printed
= 0;
609 n_options
= efi_get_boot_options(&options
);
610 if (n_options
== -ENOENT
)
611 return log_error_errno(n_options
,
612 "Failed to access EFI variables, efivarfs"
613 " needs to be available at /sys/firmware/efi/efivars/.");
615 return log_error_errno(n_options
, "Failed to read EFI boot entries: %m");
617 n_order
= efi_get_boot_order(&order
);
618 if (n_order
== -ENOENT
)
620 else if (n_order
< 0)
621 return log_error_errno(n_order
, "Failed to read EFI boot order: %m");
623 /* print entries in BootOrder first */
624 for (int i
= 0; i
< n_order
; i
++)
625 (void) print_efi_option(order
[i
], &n_printed
, /* in_order= */ true);
627 /* print remaining entries */
628 for (int i
= 0; i
< n_options
; i
++) {
629 for (int j
= 0; j
< n_order
; j
++)
630 if (options
[i
] == order
[j
])
633 (void) print_efi_option(options
[i
], &n_printed
, /* in_order= */ false);
640 printf("No boot loaders listed in EFI Variables.\n\n");
645 static int boot_config_load_and_select(
647 const char *esp_path
,
649 const char *xbootldr_path
,
650 dev_t xbootldr_devid
) {
654 /* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would
655 * find the same entries twice. */
656 bool same
= esp_path
&& xbootldr_path
&& devnum_set_and_equal(esp_devid
, xbootldr_devid
);
658 r
= boot_config_load(config
, esp_path
, same
? NULL
: xbootldr_path
);
663 _cleanup_strv_free_
char **efi_entries
= NULL
;
665 r
= efi_loader_get_entries(&efi_entries
);
666 if (r
== -ENOENT
|| ERRNO_IS_NOT_SUPPORTED(r
))
667 log_debug_errno(r
, "Boot loader reported no entries.");
669 log_warning_errno(r
, "Failed to determine entries reported by boot loader, ignoring: %m");
671 (void) boot_config_augment_from_loader(config
, efi_entries
, /* only_auto= */ false);
674 return boot_config_select_special_entries(config
, /* skip_efivars= */ !!arg_root
);
677 static int status_entries(
678 const BootConfig
*config
,
679 const char *esp_path
,
680 sd_id128_t esp_partition_uuid
,
681 const char *xbootldr_path
,
682 sd_id128_t xbootldr_partition_uuid
) {
684 sd_id128_t dollar_boot_partition_uuid
;
685 const char *dollar_boot_path
;
689 assert(esp_path
|| xbootldr_path
);
692 dollar_boot_path
= xbootldr_path
;
693 dollar_boot_partition_uuid
= xbootldr_partition_uuid
;
695 dollar_boot_path
= esp_path
;
696 dollar_boot_partition_uuid
= esp_partition_uuid
;
699 printf("%sBoot Loader Entries:%s\n"
700 " $BOOT: %s", ansi_underline(), ansi_normal(), dollar_boot_path
);
701 if (!sd_id128_is_null(dollar_boot_partition_uuid
))
702 printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR
")",
703 SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid
));
706 if (config
->default_entry
< 0)
707 printf("%zu entries, no entry could be determined as default.\n", config
->n_entries
);
709 printf("%sDefault Boot Loader Entry:%s\n", ansi_underline(), ansi_normal());
712 boot_config_default_entry(config
),
713 /* show_as_default= */ false,
714 /* show_as_selected= */ false,
715 /* show_discovered= */ false);
717 /* < 0 is already logged by the function itself, let's just emit an extra warning if
718 the default entry is broken */
719 printf("\nWARNING: default boot entry is broken\n");
725 static int compare_product(const char *a
, const char *b
) {
734 return x
< y
? -1 : x
> y
? 1 : 0;
736 return strncmp(a
, b
, x
);
739 static int compare_version(const char *a
, const char *b
) {
743 a
+= strcspn(a
, " ");
745 b
+= strcspn(b
, " ");
748 return strverscmp_improved(a
, b
);
751 static int version_check(int fd_from
, const char *from
, int fd_to
, const char *to
) {
752 _cleanup_free_
char *a
= NULL
, *b
= NULL
;
755 assert(fd_from
>= 0);
760 r
= get_file_version(fd_from
, &a
);
764 return log_notice_errno(SYNTHETIC_ERRNO(EREMOTE
),
765 "Source file \"%s\" does not carry version information!",
768 r
= get_file_version(fd_to
, &b
);
771 if (r
== 0 || compare_product(a
, b
) != 0)
772 return log_notice_errno(SYNTHETIC_ERRNO(EREMOTE
),
773 "Skipping \"%s\", since it's owned by another boot loader.",
776 r
= compare_version(a
, b
);
777 log_debug("Comparing versions: \"%s\" %s \"%s", a
, comparison_operator(r
), b
);
779 return log_warning_errno(SYNTHETIC_ERRNO(ESTALE
),
780 "Skipping \"%s\", since newer boot loader version in place already.", to
);
782 return log_info_errno(SYNTHETIC_ERRNO(ESTALE
),
783 "Skipping \"%s\", since same boot loader version in place already.", to
);
788 static int copy_file_with_version_check(const char *from
, const char *to
, bool force
) {
789 _cleanup_close_
int fd_from
= -1, fd_to
= -1;
790 _cleanup_free_
char *t
= NULL
;
793 fd_from
= open(from
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
795 return log_error_errno(errno
, "Failed to open \"%s\" for reading: %m", from
);
798 fd_to
= open(to
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
801 return log_error_errno(errno
, "Failed to open \"%s\" for reading: %m", to
);
803 r
= version_check(fd_from
, from
, fd_to
, to
);
807 if (lseek(fd_from
, 0, SEEK_SET
) == (off_t
) -1)
808 return log_error_errno(errno
, "Failed to seek in \"%s\": %m", from
);
810 fd_to
= safe_close(fd_to
);
814 r
= tempfn_random(to
, NULL
, &t
);
818 RUN_WITH_UMASK(0000) {
819 fd_to
= open(t
, O_WRONLY
|O_CREAT
|O_CLOEXEC
|O_EXCL
|O_NOFOLLOW
, 0644);
821 return log_error_errno(errno
, "Failed to open \"%s\" for writing: %m", t
);
824 r
= copy_bytes(fd_from
, fd_to
, UINT64_MAX
, COPY_REFLINK
);
827 return log_error_errno(r
, "Failed to copy data from \"%s\" to \"%s\": %m", from
, t
);
830 (void) copy_times(fd_from
, fd_to
, 0);
832 r
= fsync_full(fd_to
);
834 (void) unlink_noerrno(t
);
835 return log_error_errno(r
, "Failed to copy data from \"%s\" to \"%s\": %m", from
, t
);
838 if (renameat(AT_FDCWD
, t
, AT_FDCWD
, to
) < 0) {
839 (void) unlink_noerrno(t
);
840 return log_error_errno(errno
, "Failed to rename \"%s\" to \"%s\": %m", t
, to
);
843 log_info("Copied \"%s\" to \"%s\".", from
, to
);
848 static int mkdir_one(const char *prefix
, const char *suffix
) {
849 _cleanup_free_
char *p
= NULL
;
851 p
= path_join(prefix
, suffix
);
852 if (mkdir(p
, 0700) < 0) {
854 return log_error_errno(errno
, "Failed to create \"%s\": %m", p
);
856 log_info("Created \"%s\".", p
);
861 static const char *const esp_subdirs
[] = {
862 /* The directories to place in the ESP */
870 static const char *const dollar_boot_subdirs
[] = {
871 /* The directories to place in the XBOOTLDR partition or the ESP, depending what exists */
873 "loader/entries", /* Type #1 entries */
875 "EFI/Linux", /* Type #2 entries */
879 static int create_subdirs(const char *root
, const char * const *subdirs
) {
882 STRV_FOREACH(i
, subdirs
) {
883 r
= mkdir_one(root
, *i
);
891 static int copy_one_file(const char *esp_path
, const char *name
, bool force
) {
892 char *root
= IN_SET(arg_install_source
, ARG_INSTALL_SOURCE_AUTO
, ARG_INSTALL_SOURCE_IMAGE
) ? arg_root
: NULL
;
893 _cleanup_free_
char *source_path
= NULL
, *dest_path
= NULL
, *p
= NULL
, *q
= NULL
;
898 dest_name
= strdupa_safe(name
);
899 s
= endswith_no_case(dest_name
, ".signed");
903 p
= path_join(BOOTLIBDIR
, name
);
907 r
= chase_symlinks(p
, root
, CHASE_PREFIX_ROOT
, &source_path
, NULL
);
908 /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
909 if (r
== -ENOENT
&& root
&& arg_install_source
== ARG_INSTALL_SOURCE_AUTO
)
910 r
= chase_symlinks(p
, NULL
, CHASE_PREFIX_ROOT
, &source_path
, NULL
);
912 return log_error_errno(r
,
913 "Failed to resolve path %s%s%s: %m",
915 root
? " under directory " : "",
918 q
= path_join("/EFI/systemd/", dest_name
);
922 r
= chase_symlinks(q
, esp_path
, CHASE_PREFIX_ROOT
| CHASE_NONEXISTENT
, &dest_path
, NULL
);
924 return log_error_errno(r
, "Failed to resolve path %s under directory %s: %m", q
, esp_path
);
926 /* Note that if this fails we do the second copy anyway, but return this error code,
927 * so we stash it away in a separate variable. */
928 ret
= copy_file_with_version_check(source_path
, dest_path
, force
);
930 e
= startswith(dest_name
, "systemd-boot");
932 _cleanup_free_
char *default_dest_path
= NULL
;
935 /* Create the EFI default boot loader name (specified for removable devices) */
936 v
= strjoina("/EFI/BOOT/BOOT", e
);
937 ascii_strupper(strrchr(v
, '/') + 1);
939 r
= chase_symlinks(v
, esp_path
, CHASE_PREFIX_ROOT
| CHASE_NONEXISTENT
, &default_dest_path
, NULL
);
941 return log_error_errno(r
, "Failed to resolve path %s under directory %s: %m", v
, esp_path
);
943 r
= copy_file_with_version_check(source_path
, default_dest_path
, force
);
944 if (r
< 0 && ret
== 0)
951 static int install_binaries(const char *esp_path
, const char *arch
, bool force
) {
952 char *root
= IN_SET(arg_install_source
, ARG_INSTALL_SOURCE_AUTO
, ARG_INSTALL_SOURCE_IMAGE
) ? arg_root
: NULL
;
953 _cleanup_closedir_
DIR *d
= NULL
;
954 _cleanup_free_
char *path
= NULL
;
957 r
= chase_symlinks_and_opendir(BOOTLIBDIR
, root
, CHASE_PREFIX_ROOT
, &path
, &d
);
958 /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
959 if (r
== -ENOENT
&& root
&& arg_install_source
== ARG_INSTALL_SOURCE_AUTO
)
960 r
= chase_symlinks_and_opendir(BOOTLIBDIR
, NULL
, CHASE_PREFIX_ROOT
, &path
, &d
);
962 return log_error_errno(r
, "Failed to open boot loader directory %s%s: %m", strempty(root
), BOOTLIBDIR
);
964 const char *suffix
= strjoina(arch
, ".efi");
965 const char *suffix_signed
= strjoina(arch
, ".efi.signed");
967 FOREACH_DIRENT(de
, d
, return log_error_errno(errno
, "Failed to read \"%s\": %m", path
)) {
970 if (!endswith_no_case(de
->d_name
, suffix
) && !endswith_no_case(de
->d_name
, suffix_signed
))
973 /* skip the .efi file, if there's a .signed version of it */
974 if (endswith_no_case(de
->d_name
, ".efi")) {
975 _cleanup_free_
const char *s
= strjoin(de
->d_name
, ".signed");
978 if (faccessat(dirfd(d
), s
, F_OK
, 0) >= 0)
982 k
= copy_one_file(esp_path
, de
->d_name
, force
);
983 /* Don't propagate an error code if no update necessary, installed version already equal or
984 * newer version, or other boot loader in place. */
985 if (arg_graceful
&& IN_SET(k
, -ESTALE
, -EREMOTE
))
994 static bool same_entry(uint16_t id
, sd_id128_t uuid
, const char *path
) {
995 _cleanup_free_
char *opath
= NULL
;
999 r
= efi_get_boot_option(id
, NULL
, &ouuid
, &opath
, NULL
);
1002 if (!sd_id128_equal(uuid
, ouuid
))
1005 /* Some motherboards convert the path to uppercase under certain circumstances
1006 * (e.g. after booting into the Boot Menu in the ASUS ROG STRIX B350-F GAMING),
1007 * so use case-insensitive checking */
1008 if (!strcaseeq_ptr(path
, opath
))
1014 static int find_slot(sd_id128_t uuid
, const char *path
, uint16_t *id
) {
1015 _cleanup_free_
uint16_t *options
= NULL
;
1017 int n
= efi_get_boot_options(&options
);
1021 /* find already existing systemd-boot entry */
1022 for (int i
= 0; i
< n
; i
++)
1023 if (same_entry(options
[i
], uuid
, path
)) {
1028 /* find free slot in the sorted BootXXXX variable list */
1029 for (int i
= 0; i
< n
; i
++)
1030 if (i
!= options
[i
]) {
1035 /* use the next one */
1042 static int insert_into_order(uint16_t slot
, bool first
) {
1043 _cleanup_free_
uint16_t *order
= NULL
;
1047 n
= efi_get_boot_order(&order
);
1049 /* no entry, add us */
1050 return efi_set_boot_order(&slot
, 1);
1052 /* are we the first and only one? */
1053 if (n
== 1 && order
[0] == slot
)
1056 /* are we already in the boot order? */
1057 for (int i
= 0; i
< n
; i
++) {
1058 if (order
[i
] != slot
)
1061 /* we do not require to be the first one, all is fine */
1065 /* move us to the first slot */
1066 memmove(order
+ 1, order
, i
* sizeof(uint16_t));
1068 return efi_set_boot_order(order
, n
);
1072 t
= reallocarray(order
, n
+ 1, sizeof(uint16_t));
1077 /* add us to the top or end of the list */
1079 memmove(order
+ 1, order
, n
* sizeof(uint16_t));
1084 return efi_set_boot_order(order
, n
+ 1);
1087 static int remove_from_order(uint16_t slot
) {
1088 _cleanup_free_
uint16_t *order
= NULL
;
1091 n
= efi_get_boot_order(&order
);
1095 for (int i
= 0; i
< n
; i
++) {
1096 if (order
[i
] != slot
)
1100 memmove(order
+ i
, order
+ i
+1, (n
- i
) * sizeof(uint16_t));
1101 return efi_set_boot_order(order
, n
- 1);
1107 static int install_variables(const char *esp_path
,
1108 uint32_t part
, uint64_t pstart
, uint64_t psize
,
1109 sd_id128_t uuid
, const char *path
,
1116 log_info("Acting on %s, skipping EFI variable setup.",
1117 arg_image
? "image" : "root directory");
1121 if (!is_efi_boot()) {
1122 log_warning("Not booted with EFI, skipping EFI variable setup.");
1126 p
= prefix_roota(esp_path
, path
);
1127 if (access(p
, F_OK
) < 0) {
1128 if (errno
== ENOENT
)
1131 return log_error_errno(errno
, "Cannot access \"%s\": %m", p
);
1134 r
= find_slot(uuid
, path
, &slot
);
1136 return log_error_errno(r
,
1138 "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" :
1139 "Failed to determine current boot order: %m");
1141 if (first
|| r
== 0) {
1142 r
= efi_add_boot_option(slot
, "Linux Boot Manager",
1143 part
, pstart
, psize
,
1146 return log_error_errno(r
, "Failed to create EFI Boot variable entry: %m");
1148 log_info("Created EFI boot entry \"Linux Boot Manager\".");
1151 return insert_into_order(slot
, first
);
1154 static int remove_boot_efi(const char *esp_path
) {
1155 _cleanup_closedir_
DIR *d
= NULL
;
1159 p
= prefix_roota(esp_path
, "/EFI/BOOT");
1162 if (errno
== ENOENT
)
1165 return log_error_errno(errno
, "Failed to open directory \"%s\": %m", p
);
1168 FOREACH_DIRENT(de
, d
, break) {
1169 _cleanup_close_
int fd
= -1;
1170 _cleanup_free_
char *v
= NULL
;
1172 if (!endswith_no_case(de
->d_name
, ".efi"))
1175 if (!startswith_no_case(de
->d_name
, "boot"))
1178 fd
= openat(dirfd(d
), de
->d_name
, O_RDONLY
|O_CLOEXEC
);
1180 return log_error_errno(errno
, "Failed to open \"%s/%s\" for reading: %m", p
, de
->d_name
);
1182 r
= get_file_version(fd
, &v
);
1185 if (r
> 0 && startswith(v
, "systemd-boot ")) {
1186 r
= unlinkat(dirfd(d
), de
->d_name
, 0);
1188 return log_error_errno(errno
, "Failed to remove \"%s/%s\": %m", p
, de
->d_name
);
1190 log_info("Removed \"%s/%s\".", p
, de
->d_name
);
1199 static int rmdir_one(const char *prefix
, const char *suffix
) {
1202 p
= prefix_roota(prefix
, suffix
);
1204 bool ignore
= IN_SET(errno
, ENOENT
, ENOTEMPTY
);
1206 log_full_errno(ignore
? LOG_DEBUG
: LOG_ERR
, errno
,
1207 "Failed to remove directory \"%s\": %m", p
);
1211 log_info("Removed \"%s\".", p
);
1216 static int remove_subdirs(const char *root
, const char *const *subdirs
) {
1219 /* We use recursion here to destroy the directories in reverse order. Which should be safe given how
1220 * short the array is. */
1222 if (!subdirs
[0]) /* A the end of the list */
1225 r
= remove_subdirs(root
, subdirs
+ 1);
1226 q
= rmdir_one(root
, subdirs
[0]);
1228 return r
< 0 ? r
: q
;
1231 static int remove_entry_directory(const char *root
) {
1233 assert(arg_make_entry_directory
>= 0);
1235 if (!arg_make_entry_directory
|| !arg_entry_token
)
1238 return rmdir_one(root
, arg_entry_token
);
1241 static int remove_binaries(const char *esp_path
) {
1245 p
= prefix_roota(esp_path
, "/EFI/systemd");
1246 r
= rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
1248 q
= remove_boot_efi(esp_path
);
1249 if (q
< 0 && r
== 0)
1255 static int remove_file(const char *root
, const char *file
) {
1261 p
= prefix_roota(root
, file
);
1262 if (unlink(p
) < 0) {
1263 log_full_errno(errno
== ENOENT
? LOG_DEBUG
: LOG_ERR
, errno
,
1264 "Failed to unlink file \"%s\": %m", p
);
1266 return errno
== ENOENT
? 0 : -errno
;
1269 log_info("Removed \"%s\".", p
);
1273 static int remove_variables(sd_id128_t uuid
, const char *path
, bool in_order
) {
1277 if (arg_root
|| !is_efi_boot())
1280 r
= find_slot(uuid
, path
, &slot
);
1284 r
= efi_remove_boot_option(slot
);
1289 return remove_from_order(slot
);
1294 static int remove_loader_variables(void) {
1297 /* Remove all persistent loader variables we define */
1300 EFI_LOADER_VARIABLE(LoaderConfigTimeout
),
1301 EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot
),
1302 EFI_LOADER_VARIABLE(LoaderEntryDefault
),
1303 EFI_LOADER_VARIABLE(LoaderEntryOneShot
),
1304 EFI_LOADER_VARIABLE(LoaderSystemToken
)){
1308 q
= efi_set_variable(var
, NULL
, 0);
1312 log_warning_errno(q
, "Failed to remove EFI variable %s: %m", var
);
1316 log_info("Removed EFI variable %s.", var
);
1322 static int install_loader_config(const char *esp_path
) {
1323 _cleanup_(unlink_and_freep
) char *t
= NULL
;
1324 _cleanup_fclose_
FILE *f
= NULL
;
1328 assert(arg_make_entry_directory
>= 0);
1330 p
= prefix_roota(esp_path
, "/loader/loader.conf");
1331 if (access(p
, F_OK
) >= 0) /* Silently skip creation if the file already exists (early check) */
1334 r
= fopen_tmpfile_linkable(p
, O_WRONLY
|O_CLOEXEC
, &t
, &f
);
1336 return log_error_errno(r
, "Failed to open \"%s\" for writing: %m", p
);
1338 fprintf(f
, "#timeout 3\n"
1339 "#console-mode keep\n");
1341 if (arg_make_entry_directory
) {
1342 assert(arg_entry_token
);
1343 fprintf(f
, "default %s-*\n", arg_entry_token
);
1346 r
= flink_tmpfile(f
, t
, p
);
1348 return 0; /* Silently skip creation if the file exists now (recheck) */
1350 return log_error_errno(r
, "Failed to move \"%s\" into place: %m", p
);
1356 static int install_loader_specification(const char *root
) {
1357 _cleanup_(unlink_and_freep
) char *t
= NULL
;
1358 _cleanup_fclose_
FILE *f
= NULL
;
1359 _cleanup_free_
char *p
= NULL
;
1362 p
= path_join(root
, "/loader/entries.srel");
1366 if (access(p
, F_OK
) >= 0) /* Silently skip creation if the file already exists (early check) */
1369 r
= fopen_tmpfile_linkable(p
, O_WRONLY
|O_CLOEXEC
, &t
, &f
);
1371 return log_error_errno(r
, "Failed to open \"%s\" for writing: %m", p
);
1373 fprintf(f
, "type1\n");
1375 r
= flink_tmpfile(f
, t
, p
);
1377 return 0; /* Silently skip creation if the file exists now (recheck) */
1379 return log_error_errno(r
, "Failed to move \"%s\" into place: %m", p
);
1385 static int install_entry_directory(const char *root
) {
1387 assert(arg_make_entry_directory
>= 0);
1389 if (!arg_make_entry_directory
)
1392 assert(arg_entry_token
);
1393 return mkdir_one(root
, arg_entry_token
);
1396 static int install_entry_token(void) {
1399 assert(arg_make_entry_directory
>= 0);
1400 assert(arg_entry_token
);
1402 /* Let's save the used entry token in /etc/kernel/entry-token if we used it to create the entry
1403 * directory, or if anything else but the machine ID */
1405 if (!arg_make_entry_directory
&& arg_entry_token_type
== ARG_ENTRY_TOKEN_MACHINE_ID
)
1408 r
= write_string_file("/etc/kernel/entry-token", arg_entry_token
, WRITE_STRING_FILE_CREATE
|WRITE_STRING_FILE_ATOMIC
|WRITE_STRING_FILE_MKDIR_0755
);
1410 return log_error_errno(r
, "Failed to write entry token '%s' to /etc/kernel/entry-token", arg_entry_token
);
1415 static int help(int argc
, char *argv
[], void *userdata
) {
1416 _cleanup_free_
char *link
= NULL
;
1419 r
= terminal_urlify_man("bootctl", "1", &link
);
1423 printf("%1$s [OPTIONS...] COMMAND ...\n"
1424 "\n%5$sControl EFI firmware boot settings and manage boot loader.%6$s\n"
1425 "\n%3$sGeneric EFI Firmware/Boot Loader Commands:%4$s\n"
1426 " status Show status of installed boot loader and EFI variables\n"
1427 " reboot-to-firmware [BOOL]\n"
1428 " Query or set reboot-to-firmware EFI flag\n"
1429 " systemd-efi-options [STRING]\n"
1430 " Query or set system options string in EFI variable\n"
1431 "\n%3$sBoot Loader Specification Commands:%4$s\n"
1432 " list List boot loader entries\n"
1433 " set-default ID Set default boot loader entry\n"
1434 " set-oneshot ID Set default boot loader entry, for next boot only\n"
1435 " set-timeout SECONDS Set the menu timeout\n"
1436 " set-timeout-oneshot SECONDS\n"
1437 " Set the menu timeout for the next boot only\n"
1438 "\n%3$ssystemd-boot Commands:%4$s\n"
1439 " install Install systemd-boot to the ESP and EFI variables\n"
1440 " update Update systemd-boot in the ESP and EFI variables\n"
1441 " remove Remove systemd-boot from the ESP and EFI variables\n"
1442 " is-installed Test whether systemd-boot is installed in the ESP\n"
1443 " random-seed Initialize random seed in ESP and EFI variables\n"
1444 "\n%3$sOptions:%4$s\n"
1445 " -h --help Show this help\n"
1446 " --version Print version\n"
1447 " --esp-path=PATH Path to the EFI System Partition (ESP)\n"
1448 " --boot-path=PATH Path to the $BOOT partition\n"
1449 " --root=PATH Operate on an alternate filesystem root\n"
1450 " --image=PATH Operate on disk image as filesystem root\n"
1451 " --install-source=auto|image|host\n"
1452 " Where to pick files when using --root=/--image=\n"
1453 " -p --print-esp-path Print path to the EFI System Partition\n"
1454 " -x --print-boot-path Print path to the $BOOT partition\n"
1455 " --no-variables Don't touch EFI variables\n"
1456 " --no-pager Do not pipe output into a pager\n"
1457 " --graceful Don't fail when the ESP cannot be found or EFI\n"
1458 " variables cannot be written\n"
1459 " -q --quiet Suppress output\n"
1460 " --make-entry-directory=yes|no|auto\n"
1461 " Create $BOOT/ENTRY-TOKEN/ directory\n"
1462 " --entry-token=machine-id|os-id|os-image-id|auto|literal:…\n"
1463 " Entry token to use for this installation\n"
1464 " --json=pretty|short|off\n"
1465 " Generate JSON output\n"
1466 " --all-architectures\n"
1467 " Install all supported EFI architectures\n"
1468 "\nSee the %2$s for details.\n",
1469 program_invocation_short_name
,
1479 static int parse_argv(int argc
, char *argv
[]) {
1481 ARG_ESP_PATH
= 0x100,
1490 ARG_MAKE_ENTRY_DIRECTORY
,
1496 static const struct option options
[] = {
1497 { "help", no_argument
, NULL
, 'h' },
1498 { "version", no_argument
, NULL
, ARG_VERSION
},
1499 { "esp-path", required_argument
, NULL
, ARG_ESP_PATH
},
1500 { "path", required_argument
, NULL
, ARG_ESP_PATH
}, /* Compatibility alias */
1501 { "boot-path", required_argument
, NULL
, ARG_BOOT_PATH
},
1502 { "root", required_argument
, NULL
, ARG_ROOT
},
1503 { "image", required_argument
, NULL
, ARG_IMAGE
},
1504 { "install-source", required_argument
, NULL
, ARG_INSTALL_SOURCE
},
1505 { "print-esp-path", no_argument
, NULL
, 'p' },
1506 { "print-path", no_argument
, NULL
, 'p' }, /* Compatibility alias */
1507 { "print-boot-path", no_argument
, NULL
, 'x' },
1508 { "no-variables", no_argument
, NULL
, ARG_NO_VARIABLES
},
1509 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1510 { "graceful", no_argument
, NULL
, ARG_GRACEFUL
},
1511 { "quiet", no_argument
, NULL
, 'q' },
1512 { "make-entry-directory", required_argument
, NULL
, ARG_MAKE_ENTRY_DIRECTORY
},
1513 { "make-machine-id-directory", required_argument
, NULL
, ARG_MAKE_ENTRY_DIRECTORY
}, /* Compatibility alias */
1514 { "entry-token", required_argument
, NULL
, ARG_ENTRY_TOKEN
},
1515 { "json", required_argument
, NULL
, ARG_JSON
},
1516 { "all-architectures", no_argument
, NULL
, ARG_ARCH_ALL
},
1526 while ((c
= getopt_long(argc
, argv
, "hpx", options
, NULL
)) >= 0)
1530 help(0, NULL
, NULL
);
1537 r
= free_and_strdup(&arg_esp_path
, optarg
);
1543 r
= free_and_strdup(&arg_xbootldr_path
, optarg
);
1549 r
= parse_path_argument(optarg
, /* suppress_root= */ true, &arg_root
);
1555 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_image
);
1560 case ARG_INSTALL_SOURCE
:
1561 if (streq(optarg
, "auto"))
1562 arg_install_source
= ARG_INSTALL_SOURCE_AUTO
;
1563 else if (streq(optarg
, "image"))
1564 arg_install_source
= ARG_INSTALL_SOURCE_IMAGE
;
1565 else if (streq(optarg
, "host"))
1566 arg_install_source
= ARG_INSTALL_SOURCE_HOST
;
1568 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1569 "Unexpected parameter for --install-source=: %s", optarg
);
1574 if (arg_print_dollar_boot_path
)
1575 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1576 "--print-boot-path/-x cannot be combined with --print-esp-path/-p");
1577 arg_print_esp_path
= true;
1581 if (arg_print_esp_path
)
1582 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1583 "--print-boot-path/-x cannot be combined with --print-esp-path/-p");
1584 arg_print_dollar_boot_path
= true;
1587 case ARG_NO_VARIABLES
:
1588 arg_touch_variables
= false;
1592 arg_pager_flags
|= PAGER_DISABLE
;
1596 arg_graceful
= true;
1603 case ARG_ENTRY_TOKEN
: {
1606 if (streq(optarg
, "machine-id")) {
1607 arg_entry_token_type
= ARG_ENTRY_TOKEN_MACHINE_ID
;
1608 arg_entry_token
= mfree(arg_entry_token
);
1609 } else if (streq(optarg
, "os-image-id")) {
1610 arg_entry_token_type
= ARG_ENTRY_TOKEN_OS_IMAGE_ID
;
1611 arg_entry_token
= mfree(arg_entry_token
);
1612 } else if (streq(optarg
, "os-id")) {
1613 arg_entry_token_type
= ARG_ENTRY_TOKEN_OS_ID
;
1614 arg_entry_token
= mfree(arg_entry_token
);
1615 } else if ((e
= startswith(optarg
, "literal:"))) {
1616 arg_entry_token_type
= ARG_ENTRY_TOKEN_LITERAL
;
1618 r
= free_and_strdup_warn(&arg_entry_token
, e
);
1622 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1623 "Unexpected parameter for --entry-token=: %s", optarg
);
1628 case ARG_MAKE_ENTRY_DIRECTORY
:
1629 if (streq(optarg
, "auto")) /* retained for backwards compatibility */
1630 arg_make_entry_directory
= -1; /* yes if machine-id is permanent */
1632 r
= parse_boolean_argument("--make-entry-directory=", optarg
, &b
);
1636 arg_make_entry_directory
= b
;
1641 r
= parse_json_argument(optarg
, &arg_json_format_flags
);
1647 arg_arch_all
= true;
1654 assert_not_reached();
1657 if ((arg_root
|| arg_image
) && argv
[optind
] && !STR_IN_SET(argv
[optind
], "status", "list",
1658 "install", "update", "remove", "is-installed", "random-seed"))
1659 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1660 "Options --root= and --image= are not supported with verb %s.",
1663 if (arg_root
&& arg_image
)
1664 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Please specify either --root= or --image=, the combination of both is not supported.");
1666 if (arg_install_source
!= ARG_INSTALL_SOURCE_AUTO
&& !arg_root
&& !arg_image
)
1667 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "--install-from-host is only supported with --root= or --image=.");
1672 static void read_efi_var(const char *variable
, char **ret
) {
1675 r
= efi_get_variable_string(variable
, ret
);
1676 if (r
< 0 && r
!= -ENOENT
)
1677 log_warning_errno(r
, "Failed to read EFI variable %s: %m", variable
);
1680 static void print_yes_no_line(bool first
, bool good
, const char *name
) {
1682 first
? " Features: " : " ",
1683 COLOR_MARK_BOOL(good
),
1687 static int are_we_installed(const char *esp_path
) {
1690 /* Tests whether systemd-boot is installed. It's not obvious what to use as check here: we could
1691 * check EFI variables, we could check what binary /EFI/BOOT/BOOT*.EFI points to, or whether the
1692 * loader entries directory exists. Here we opted to check whether /EFI/systemd/ is non-empty, which
1693 * should be a suitable and very minimal check for a number of reasons:
1695 * → The check is architecture independent (i.e. we check if any systemd-boot loader is installed,
1696 * not a specific one.)
1698 * → It doesn't assume we are the only boot loader (i.e doesn't check if we own the main
1699 * /EFI/BOOT/BOOT*.EFI fallback binary.
1701 * → It specifically checks for systemd-boot, not for other boot loaders (which a check for
1702 * /boot/loader/entries would do). */
1704 _cleanup_free_
char *p
= path_join(esp_path
, "/EFI/systemd/");
1708 log_debug("Checking whether %s contains any files%s", p
, special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
1709 r
= dir_is_empty(p
, /* ignore_hidden_or_backup= */ false);
1710 if (r
< 0 && r
!= -ENOENT
)
1711 return log_error_errno(r
, "Failed to check whether %s contains any files: %m", p
);
1716 static int verb_status(int argc
, char *argv
[], void *userdata
) {
1717 sd_id128_t esp_uuid
= SD_ID128_NULL
, xbootldr_uuid
= SD_ID128_NULL
;
1718 dev_t esp_devid
= 0, xbootldr_devid
= 0;
1721 r
= acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL
, NULL
, NULL
, &esp_uuid
, &esp_devid
);
1722 if (arg_print_esp_path
) {
1723 if (r
== -EACCES
) /* If we couldn't acquire the ESP path, log about access errors (which is the only
1724 * error the find_esp_and_warn() won't log on its own) */
1725 return log_error_errno(r
, "Failed to determine ESP location: %m");
1732 r
= acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, &xbootldr_uuid
, &xbootldr_devid
);
1733 if (arg_print_dollar_boot_path
) {
1735 return log_error_errno(r
, "Failed to determine XBOOTLDR partition: %m");
1739 const char *path
= arg_dollar_boot_path();
1741 return log_error_errno(SYNTHETIC_ERRNO(EACCES
), "Failed to determine XBOOTLDR location: %m");
1746 if (arg_print_esp_path
|| arg_print_dollar_boot_path
)
1749 r
= 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just
1750 * show what we can show */
1752 pager_open(arg_pager_flags
);
1754 if (!arg_root
&& is_efi_boot()) {
1755 static const struct {
1758 } loader_flags
[] = {
1759 { EFI_LOADER_FEATURE_BOOT_COUNTING
, "Boot counting" },
1760 { EFI_LOADER_FEATURE_CONFIG_TIMEOUT
, "Menu timeout control" },
1761 { EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT
, "One-shot menu timeout control" },
1762 { EFI_LOADER_FEATURE_ENTRY_DEFAULT
, "Default entry control" },
1763 { EFI_LOADER_FEATURE_ENTRY_ONESHOT
, "One-shot entry control" },
1764 { EFI_LOADER_FEATURE_XBOOTLDR
, "Support for XBOOTLDR partition" },
1765 { EFI_LOADER_FEATURE_RANDOM_SEED
, "Support for passing random seed to OS" },
1766 { EFI_LOADER_FEATURE_LOAD_DRIVER
, "Load drop-in drivers" },
1767 { EFI_LOADER_FEATURE_SORT_KEY
, "Support Type #1 sort-key field" },
1768 { EFI_LOADER_FEATURE_SAVED_ENTRY
, "Support @saved pseudo-entry" },
1769 { EFI_LOADER_FEATURE_DEVICETREE
, "Support Type #1 devicetree field" },
1771 static const struct {
1775 { EFI_STUB_FEATURE_REPORT_BOOT_PARTITION
, "Stub sets ESP information" },
1776 { EFI_STUB_FEATURE_PICK_UP_CREDENTIALS
, "Picks up credentials from boot partition" },
1777 { EFI_STUB_FEATURE_PICK_UP_SYSEXTS
, "Picks up system extension images from boot partition" },
1778 { EFI_STUB_FEATURE_THREE_PCRS
, "Measures kernel+command line+sysexts" },
1780 _cleanup_free_
char *fw_type
= NULL
, *fw_info
= NULL
, *loader
= NULL
, *loader_path
= NULL
, *stub
= NULL
;
1781 sd_id128_t loader_part_uuid
= SD_ID128_NULL
;
1782 uint64_t loader_features
= 0, stub_features
= 0;
1786 read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareType
), &fw_type
);
1787 read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareInfo
), &fw_info
);
1788 read_efi_var(EFI_LOADER_VARIABLE(LoaderInfo
), &loader
);
1789 read_efi_var(EFI_LOADER_VARIABLE(StubInfo
), &stub
);
1790 read_efi_var(EFI_LOADER_VARIABLE(LoaderImageIdentifier
), &loader_path
);
1791 (void) efi_loader_get_features(&loader_features
);
1792 (void) efi_stub_get_features(&stub_features
);
1795 efi_tilt_backslashes(loader_path
);
1797 k
= efi_loader_get_device_part_uuid(&loader_part_uuid
);
1798 if (k
< 0 && k
!= -ENOENT
)
1799 r
= log_warning_errno(k
, "Failed to read EFI variable LoaderDevicePartUUID: %m");
1801 SecureBootMode secure
= efi_get_secure_boot_mode();
1802 printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
1803 printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type
), strna(fw_info
), ansi_normal());
1804 printf(" Firmware Arch: %s\n", get_efi_arch());
1805 printf(" Secure Boot: %sd (%s)\n",
1806 enable_disable(IN_SET(secure
, SECURE_BOOT_USER
, SECURE_BOOT_DEPLOYED
)),
1807 secure_boot_mode_to_string(secure
));
1810 printf(" TPM2 Support: %s%s%s\n",
1811 FLAGS_SET(s
, TPM2_SUPPORT_FIRMWARE
|TPM2_SUPPORT_DRIVER
) ? ansi_highlight_green() :
1812 (s
& (TPM2_SUPPORT_FIRMWARE
|TPM2_SUPPORT_DRIVER
)) != 0 ? ansi_highlight_red() : ansi_highlight_yellow(),
1813 FLAGS_SET(s
, TPM2_SUPPORT_FIRMWARE
|TPM2_SUPPORT_DRIVER
) ? "yes" :
1814 (s
& TPM2_SUPPORT_FIRMWARE
) ? "firmware only, driver unavailable" :
1815 (s
& TPM2_SUPPORT_DRIVER
) ? "driver only, firmware unavailable" : "no",
1818 k
= efi_get_reboot_to_firmware();
1820 printf(" Boot into FW: %sactive%s\n", ansi_highlight_yellow(), ansi_normal());
1822 printf(" Boot into FW: supported\n");
1823 else if (k
== -EOPNOTSUPP
)
1824 printf(" Boot into FW: not supported\n");
1827 printf(" Boot into FW: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
1831 printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
1832 printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader
), ansi_normal());
1834 for (size_t i
= 0; i
< ELEMENTSOF(loader_flags
); i
++)
1835 print_yes_no_line(i
== 0, FLAGS_SET(loader_features
, loader_flags
[i
].flag
), loader_flags
[i
].name
);
1837 sd_id128_t bootloader_esp_uuid
;
1838 bool have_bootloader_esp_uuid
= efi_loader_get_device_part_uuid(&bootloader_esp_uuid
) >= 0;
1840 print_yes_no_line(false, have_bootloader_esp_uuid
, "Boot loader sets ESP information");
1841 if (have_bootloader_esp_uuid
&& !sd_id128_is_null(esp_uuid
) &&
1842 !sd_id128_equal(esp_uuid
, bootloader_esp_uuid
))
1843 printf("WARNING: The boot loader reports a different ESP UUID than detected ("SD_ID128_UUID_FORMAT_STR
" vs. "SD_ID128_UUID_FORMAT_STR
")!\n",
1844 SD_ID128_FORMAT_VAL(bootloader_esp_uuid
),
1845 SD_ID128_FORMAT_VAL(esp_uuid
));
1848 printf(" Stub: %s\n", stub
);
1849 for (size_t i
= 0; i
< ELEMENTSOF(stub_flags
); i
++)
1850 print_yes_no_line(i
== 0, FLAGS_SET(stub_features
, stub_flags
[i
].flag
), stub_flags
[i
].name
);
1852 if (!sd_id128_is_null(loader_part_uuid
))
1853 printf(" ESP: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR
"\n",
1854 SD_ID128_FORMAT_VAL(loader_part_uuid
));
1856 printf(" ESP: n/a\n");
1857 printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT
), strna(loader_path
));
1860 printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
1861 have
= access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderRandomSeed
)), F_OK
) >= 0;
1862 printf(" Passed to OS: %s\n", yes_no(have
));
1863 have
= access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken
)), F_OK
) >= 0;
1864 printf(" System Token: %s\n", have
? "set" : "not set");
1867 _cleanup_free_
char *p
= NULL
;
1869 p
= path_join(arg_esp_path
, "/loader/random-seed");
1873 have
= access(p
, F_OK
) >= 0;
1874 printf(" Exists: %s\n", yes_no(have
));
1879 printf("%sSystem:%s\n"
1880 "Not booted with EFI\n\n",
1881 ansi_underline(), ansi_normal());
1884 k
= status_binaries(arg_esp_path
, esp_uuid
);
1889 if (!arg_root
&& is_efi_boot()) {
1890 k
= status_variables();
1895 if (arg_esp_path
|| arg_xbootldr_path
) {
1896 _cleanup_(boot_config_free
) BootConfig config
= BOOT_CONFIG_NULL
;
1898 k
= boot_config_load_and_select(&config
,
1899 arg_esp_path
, esp_devid
,
1900 arg_xbootldr_path
, xbootldr_devid
);
1904 k
= status_entries(&config
,
1905 arg_esp_path
, esp_uuid
,
1906 arg_xbootldr_path
, xbootldr_uuid
);
1915 static int verb_list(int argc
, char *argv
[], void *userdata
) {
1916 _cleanup_(boot_config_free
) BootConfig config
= BOOT_CONFIG_NULL
;
1917 dev_t esp_devid
= 0, xbootldr_devid
= 0;
1920 /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two
1921 * things: turn off logging about access errors and turn off potentially privileged device probing.
1922 * Here we're interested in the latter but not the former, hence request the mode, and log about
1925 r
= acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL
, NULL
, NULL
, NULL
, &esp_devid
);
1926 if (r
== -EACCES
) /* We really need the ESP path for this call, hence also log about access errors */
1927 return log_error_errno(r
, "Failed to determine ESP location: %m");
1931 r
= acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, NULL
, &xbootldr_devid
);
1933 return log_error_errno(r
, "Failed to determine XBOOTLDR partition: %m");
1937 r
= boot_config_load_and_select(&config
, arg_esp_path
, esp_devid
, arg_xbootldr_path
, xbootldr_devid
);
1941 if (config
.n_entries
== 0 && FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
1942 log_info("No boot loader entries found.");
1946 pager_open(arg_pager_flags
);
1947 return show_boot_entries(&config
, arg_json_format_flags
);
1950 static int install_random_seed(const char *esp
) {
1951 _cleanup_(unlink_and_freep
) char *tmp
= NULL
;
1952 _cleanup_free_
void *buffer
= NULL
;
1953 _cleanup_free_
char *path
= NULL
;
1954 _cleanup_close_
int fd
= -1;
1955 size_t sz
, token_size
;
1961 path
= path_join(esp
, "/loader/random-seed");
1965 sz
= random_pool_size();
1967 buffer
= malloc(sz
);
1971 r
= crypto_random_bytes(buffer
, sz
);
1973 return log_error_errno(r
, "Failed to acquire random seed: %m");
1975 /* Normally create_subdirs() should already have created everything we need, but in case "bootctl
1976 * random-seed" is called we want to just create the minimum we need for it, and not the full
1978 r
= mkdir_parents(path
, 0755);
1980 return log_error_errno(r
, "Failed to create parent directory for %s: %m", path
);
1982 r
= tempfn_random(path
, "bootctl", &tmp
);
1986 fd
= open(tmp
, O_CREAT
|O_EXCL
|O_NOFOLLOW
|O_NOCTTY
|O_WRONLY
|O_CLOEXEC
, 0600);
1989 return log_error_errno(fd
, "Failed to open random seed file for writing: %m");
1992 n
= write(fd
, buffer
, sz
);
1994 return log_error_errno(errno
, "Failed to write random seed file: %m");
1995 if ((size_t) n
!= sz
)
1996 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Short write while writing random seed file.");
1998 if (rename(tmp
, path
) < 0)
1999 return log_error_errno(r
, "Failed to move random seed file into place: %m");
2003 log_info("Random seed file %s successfully written (%zu bytes).", path
, sz
);
2005 if (!arg_touch_variables
)
2008 if (!is_efi_boot()) {
2009 log_notice("Not booted with EFI, skipping EFI variable setup.");
2014 log_warning("Acting on %s, skipping EFI variable setup.",
2015 arg_image
? "image" : "root directory");
2019 r
= getenv_bool("SYSTEMD_WRITE_SYSTEM_TOKEN");
2022 log_warning_errno(r
, "Failed to parse $SYSTEMD_WRITE_SYSTEM_TOKEN, ignoring.");
2024 if (detect_vm() > 0) {
2025 /* Let's not write a system token if we detect we are running in a VM
2026 * environment. Why? Our default security model for the random seed uses the system
2027 * token as a mechanism to ensure we are not vulnerable to golden master sloppiness
2028 * issues, i.e. that people initialize the random seed file, then copy the image to
2029 * many systems and end up with the same random seed in each that is assumed to be
2030 * valid but in reality is the same for all machines. By storing a system token in
2031 * the EFI variable space we can make sure that even though the random seeds on disk
2032 * are all the same they will be different on each system under the assumption that
2033 * the EFI variable space is maintained separate from the random seed storage. That
2034 * is generally the case on physical systems, as the ESP is stored on persistent
2035 * storage, and the EFI variables in NVRAM. However in virtualized environments this
2036 * is generally not true: the EFI variable set is typically stored along with the
2037 * disk image itself. For example, using the OVMF EFI firmware the EFI variables are
2038 * stored in a file in the ESP itself. */
2040 log_notice("Not installing system token, since we are running in a virtualized environment.");
2043 } else if (r
== 0) {
2044 log_notice("Not writing system token, because $SYSTEMD_WRITE_SYSTEM_TOKEN is set to false.");
2048 r
= efi_get_variable(EFI_LOADER_VARIABLE(LoaderSystemToken
), NULL
, NULL
, &token_size
);
2050 log_debug_errno(r
, "LoaderSystemToken EFI variable is invalid (too short?), replacing.");
2053 return log_error_errno(r
, "Failed to test system token validity: %m");
2055 if (token_size
>= sz
) {
2056 /* Let's avoid writes if we can, and initialize this only once. */
2057 log_debug("System token already written, not updating.");
2061 log_debug("Existing system token size (%zu) does not match our expectations (%zu), replacing.", token_size
, sz
);
2064 r
= crypto_random_bytes(buffer
, sz
);
2066 return log_error_errno(r
, "Failed to acquire random seed: %m");
2068 /* Let's write this variable with an umask in effect, so that unprivileged users can't see the token
2069 * and possibly get identification information or too much insight into the kernel's entropy pool
2071 RUN_WITH_UMASK(0077) {
2072 r
= efi_set_variable(EFI_LOADER_VARIABLE(LoaderSystemToken
), buffer
, sz
);
2075 return log_error_errno(r
, "Failed to write 'LoaderSystemToken' EFI variable: %m");
2078 log_warning_errno(r
, "Unable to write 'LoaderSystemToken' EFI variable (firmware problem?), ignoring: %m");
2080 log_warning_errno(r
, "Unable to write 'LoaderSystemToken' EFI variable, ignoring: %m");
2082 log_info("Successfully initialized system token in EFI variable with %zu bytes.", sz
);
2088 static int sync_everything(void) {
2092 k
= syncfs_path(AT_FDCWD
, arg_esp_path
);
2094 ret
= log_error_errno(k
, "Failed to synchronize the ESP '%s': %m", arg_esp_path
);
2097 if (arg_xbootldr_path
) {
2098 k
= syncfs_path(AT_FDCWD
, arg_xbootldr_path
);
2100 ret
= log_error_errno(k
, "Failed to synchronize $BOOT '%s': %m", arg_xbootldr_path
);
2106 static int verb_install(int argc
, char *argv
[], void *userdata
) {
2107 sd_id128_t uuid
= SD_ID128_NULL
;
2108 uint64_t pstart
= 0, psize
= 0;
2110 bool install
, graceful
;
2113 /* Invoked for both "update" and "install" */
2115 install
= streq(argv
[0], "install");
2116 graceful
= !install
&& arg_graceful
; /* support graceful mode for updates */
2118 r
= acquire_esp(/* unprivileged_mode= */ false, graceful
, &part
, &pstart
, &psize
, &uuid
, NULL
);
2119 if (graceful
&& r
== -ENOKEY
)
2120 return 0; /* If --graceful is specified and we can't find an ESP, handle this cleanly */
2125 /* If we are updating, don't do anything if sd-boot wasn't actually installed. */
2126 r
= are_we_installed(arg_esp_path
);
2130 log_debug("Skipping update because sd-boot is not installed in the ESP.");
2135 r
= acquire_xbootldr(/* unprivileged_mode= */ false, NULL
, NULL
);
2139 r
= settle_make_entry_directory();
2143 const char *arch
= arg_arch_all
? "" : get_efi_arch();
2145 RUN_WITH_UMASK(0002) {
2147 /* Don't create any of these directories when we are just updating. When we update
2148 * we'll drop-in our files (unless there are newer ones already), but we won't create
2149 * the directories for them in the first place. */
2150 r
= create_subdirs(arg_esp_path
, esp_subdirs
);
2154 r
= create_subdirs(arg_dollar_boot_path(), dollar_boot_subdirs
);
2159 r
= install_binaries(arg_esp_path
, arch
, install
);
2164 r
= install_loader_config(arg_esp_path
);
2168 r
= install_entry_directory(arg_dollar_boot_path());
2172 r
= install_entry_token();
2176 r
= install_random_seed(arg_esp_path
);
2181 r
= install_loader_specification(arg_dollar_boot_path());
2186 (void) sync_everything();
2188 if (!arg_touch_variables
)
2192 log_info("Not changing EFI variables with --all-architectures.");
2196 char *path
= strjoina("/EFI/systemd/systemd-boot", arch
, ".efi");
2197 return install_variables(arg_esp_path
, part
, pstart
, psize
, uuid
, path
, install
);
2200 static int verb_remove(int argc
, char *argv
[], void *userdata
) {
2201 sd_id128_t uuid
= SD_ID128_NULL
;
2204 r
= acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false, NULL
, NULL
, NULL
, &uuid
, NULL
);
2208 r
= acquire_xbootldr(/* unprivileged_mode= */ false, NULL
, NULL
);
2212 r
= settle_make_entry_directory();
2216 r
= remove_binaries(arg_esp_path
);
2218 q
= remove_file(arg_esp_path
, "/loader/loader.conf");
2219 if (q
< 0 && r
>= 0)
2222 q
= remove_file(arg_esp_path
, "/loader/random-seed");
2223 if (q
< 0 && r
>= 0)
2226 q
= remove_file(arg_esp_path
, "/loader/entries.srel");
2227 if (q
< 0 && r
>= 0)
2230 q
= remove_subdirs(arg_esp_path
, esp_subdirs
);
2231 if (q
< 0 && r
>= 0)
2234 q
= remove_subdirs(arg_esp_path
, dollar_boot_subdirs
);
2235 if (q
< 0 && r
>= 0)
2238 q
= remove_entry_directory(arg_esp_path
);
2239 if (q
< 0 && r
>= 0)
2242 if (arg_xbootldr_path
) {
2243 /* Remove a subset of these also from the XBOOTLDR partition if it exists */
2245 q
= remove_file(arg_xbootldr_path
, "/loader/entries.srel");
2246 if (q
< 0 && r
>= 0)
2249 q
= remove_subdirs(arg_xbootldr_path
, dollar_boot_subdirs
);
2250 if (q
< 0 && r
>= 0)
2253 q
= remove_entry_directory(arg_xbootldr_path
);
2254 if (q
< 0 && r
>= 0)
2258 (void) sync_everything();
2260 if (!arg_touch_variables
)
2264 log_info("Not changing EFI variables with --all-architectures.");
2268 char *path
= strjoina("/EFI/systemd/systemd-boot", get_efi_arch(), ".efi");
2269 q
= remove_variables(uuid
, path
, true);
2270 if (q
< 0 && r
>= 0)
2273 q
= remove_loader_variables();
2274 if (q
< 0 && r
>= 0)
2280 static int verb_is_installed(int argc
, char *argv
[], void *userdata
) {
2283 r
= acquire_esp(/* privileged_mode= */ false,
2284 /* graceful= */ arg_graceful
,
2285 NULL
, NULL
, NULL
, NULL
, NULL
);
2289 r
= are_we_installed(arg_esp_path
);
2296 return EXIT_SUCCESS
;
2300 return EXIT_FAILURE
;
2304 static int parse_timeout(const char *arg1
, char16_t
**ret_timeout
, size_t *ret_timeout_size
) {
2305 char utf8
[DECIMAL_STR_MAX(usec_t
)];
2311 assert(ret_timeout
);
2312 assert(ret_timeout_size
);
2314 if (streq(arg1
, "menu-force"))
2315 timeout
= USEC_INFINITY
;
2316 else if (streq(arg1
, "menu-hidden"))
2319 r
= parse_time(arg1
, &timeout
, USEC_PER_SEC
);
2321 return log_error_errno(r
, "Failed to parse timeout '%s': %m", arg1
);
2322 if (timeout
!= USEC_INFINITY
&& timeout
> UINT32_MAX
* USEC_PER_SEC
)
2323 log_warning("Timeout is too long and will be treated as 'menu-force' instead.");
2326 xsprintf(utf8
, USEC_FMT
, MIN(timeout
/ USEC_PER_SEC
, UINT32_MAX
));
2328 encoded
= utf8_to_utf16(utf8
, strlen(utf8
));
2332 *ret_timeout
= encoded
;
2333 *ret_timeout_size
= char16_strlen(encoded
) * 2 + 2;
2337 static int parse_loader_entry_target_arg(const char *arg1
, char16_t
**ret_target
, size_t *ret_target_size
) {
2338 char16_t
*encoded
= NULL
;
2343 assert(ret_target_size
);
2345 if (streq(arg1
, "@current")) {
2346 r
= efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntrySelected
), NULL
, (void *) ret_target
, ret_target_size
);
2348 return log_error_errno(r
, "Failed to get EFI variable 'LoaderEntrySelected': %m");
2350 } else if (streq(arg1
, "@oneshot")) {
2351 r
= efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryOneShot
), NULL
, (void *) ret_target
, ret_target_size
);
2353 return log_error_errno(r
, "Failed to get EFI variable 'LoaderEntryOneShot': %m");
2355 } else if (streq(arg1
, "@default")) {
2356 r
= efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryDefault
), NULL
, (void *) ret_target
, ret_target_size
);
2358 return log_error_errno(r
, "Failed to get EFI variable 'LoaderEntryDefault': %m");
2360 } else if (arg1
[0] == '@' && !streq(arg1
, "@saved"))
2361 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Unsupported special entry identifier: %s", arg1
);
2363 encoded
= utf8_to_utf16(arg1
, strlen(arg1
));
2367 *ret_target
= encoded
;
2368 *ret_target_size
= char16_strlen(encoded
) * 2 + 2;
2374 static int verb_set_efivar(int argc
, char *argv
[], void *userdata
) {
2378 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
2379 "Acting on %s, skipping EFI variable setup.",
2380 arg_image
? "image" : "root directory");
2383 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
2384 "Not booted with UEFI.");
2386 if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderInfo
)), F_OK
) < 0) {
2387 if (errno
== ENOENT
) {
2388 log_error_errno(errno
, "Not booted with a supported boot loader.");
2392 return log_error_errno(errno
, "Failed to detect whether boot loader supports '%s' operation: %m", argv
[0]);
2395 if (detect_container() > 0)
2396 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
2397 "'%s' operation not supported in a container.",
2400 if (!arg_touch_variables
)
2401 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2402 "'%s' operation cannot be combined with --no-variables.",
2405 const char *variable
;
2406 int (* arg_parser
)(const char *, char16_t
**, size_t *);
2408 if (streq(argv
[0], "set-default")) {
2409 variable
= EFI_LOADER_VARIABLE(LoaderEntryDefault
);
2410 arg_parser
= parse_loader_entry_target_arg
;
2411 } else if (streq(argv
[0], "set-oneshot")) {
2412 variable
= EFI_LOADER_VARIABLE(LoaderEntryOneShot
);
2413 arg_parser
= parse_loader_entry_target_arg
;
2414 } else if (streq(argv
[0], "set-timeout")) {
2415 variable
= EFI_LOADER_VARIABLE(LoaderConfigTimeout
);
2416 arg_parser
= parse_timeout
;
2417 } else if (streq(argv
[0], "set-timeout-oneshot")) {
2418 variable
= EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot
);
2419 arg_parser
= parse_timeout
;
2421 assert_not_reached();
2423 if (isempty(argv
[1])) {
2424 r
= efi_set_variable(variable
, NULL
, 0);
2425 if (r
< 0 && r
!= -ENOENT
)
2426 return log_error_errno(r
, "Failed to remove EFI variable '%s': %m", variable
);
2428 _cleanup_free_ char16_t
*value
= NULL
;
2429 size_t value_size
= 0;
2431 r
= arg_parser(argv
[1], &value
, &value_size
);
2434 r
= efi_set_variable(variable
, value
, value_size
);
2436 return log_error_errno(r
, "Failed to update EFI variable '%s': %m", variable
);
2442 static int verb_random_seed(int argc
, char *argv
[], void *userdata
) {
2445 r
= find_esp_and_warn(arg_root
, arg_esp_path
, false, &arg_esp_path
, NULL
, NULL
, NULL
, NULL
, NULL
);
2447 /* find_esp_and_warn() doesn't warn about ENOKEY, so let's do that on our own */
2449 return log_error_errno(r
, "Unable to find ESP.");
2451 log_notice("No ESP found, not initializing random seed.");
2457 r
= install_random_seed(arg_esp_path
);
2461 (void) sync_everything();
2465 static int verb_systemd_efi_options(int argc
, char *argv
[], void *userdata
) {
2469 _cleanup_free_
char *line
= NULL
, *new = NULL
;
2471 r
= systemd_efi_options_variable(&line
);
2473 log_debug("No SystemdOptions EFI variable present in cache.");
2475 return log_error_errno(r
, "Failed to read SystemdOptions EFI variable from cache: %m");
2479 r
= systemd_efi_options_efivarfs_if_newer(&new);
2480 if (r
== -ENODATA
) {
2482 log_notice("Note: SystemdOptions EFI variable has been removed since boot.");
2484 log_warning_errno(r
, "Failed to check SystemdOptions EFI variable in efivarfs, ignoring: %m");
2485 else if (new && !streq_ptr(line
, new))
2486 log_notice("Note: SystemdOptions EFI variable has been modified since boot. New value: %s",
2489 r
= efi_set_variable_string(EFI_SYSTEMD_VARIABLE(SystemdOptions
), argv
[1]);
2491 return log_error_errno(r
, "Failed to set SystemdOptions EFI variable: %m");
2497 static int verb_reboot_to_firmware(int argc
, char *argv
[], void *userdata
) {
2501 r
= efi_get_reboot_to_firmware();
2504 return EXIT_SUCCESS
; /* success */
2508 return 1; /* recognizable error #1 */
2510 if (r
== -EOPNOTSUPP
) {
2511 puts("not supported");
2512 return 2; /* recognizable error #2 */
2515 log_error_errno(r
, "Failed to query reboot-to-firmware state: %m");
2516 return 3; /* other kind of error */
2518 r
= parse_boolean(argv
[1]);
2520 return log_error_errno(r
, "Failed to parse argument: %s", argv
[1]);
2522 r
= efi_set_reboot_to_firmware(r
);
2524 return log_error_errno(r
, "Failed to set reboot-to-firmware option: %m");
2530 static int bootctl_main(int argc
, char *argv
[]) {
2531 static const Verb verbs
[] = {
2532 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
2533 { "status", VERB_ANY
, 1, VERB_DEFAULT
, verb_status
},
2534 { "install", VERB_ANY
, 1, 0, verb_install
},
2535 { "update", VERB_ANY
, 1, 0, verb_install
},
2536 { "remove", VERB_ANY
, 1, 0, verb_remove
},
2537 { "is-installed", VERB_ANY
, 1, 0, verb_is_installed
},
2538 { "list", VERB_ANY
, 1, 0, verb_list
},
2539 { "set-default", 2, 2, 0, verb_set_efivar
},
2540 { "set-oneshot", 2, 2, 0, verb_set_efivar
},
2541 { "set-timeout", 2, 2, 0, verb_set_efivar
},
2542 { "set-timeout-oneshot", 2, 2, 0, verb_set_efivar
},
2543 { "random-seed", VERB_ANY
, 1, 0, verb_random_seed
},
2544 { "systemd-efi-options", VERB_ANY
, 2, 0, verb_systemd_efi_options
},
2545 { "reboot-to-firmware", VERB_ANY
, 2, 0, verb_reboot_to_firmware
},
2549 return dispatch_verb(argc
, argv
, verbs
, NULL
);
2552 static int run(int argc
, char *argv
[]) {
2553 _cleanup_(loop_device_unrefp
) LoopDevice
*loop_device
= NULL
;
2554 _cleanup_(decrypted_image_unrefp
) DecryptedImage
*decrypted_image
= NULL
;
2555 _cleanup_(umount_and_rmdir_and_freep
) char *unlink_dir
= NULL
;
2558 log_parse_environment();
2561 /* If we run in a container, automatically turn off EFI file system access */
2562 if (detect_container() > 0)
2563 arg_touch_variables
= false;
2565 r
= parse_argv(argc
, argv
);
2569 /* Open up and mount the image */
2573 r
= mount_image_privately_interactively(
2575 DISSECT_IMAGE_GENERIC_ROOT
|
2576 DISSECT_IMAGE_RELAX_VAR_CHECK
,
2583 arg_root
= strdup(unlink_dir
);
2588 return bootctl_main(argc
, argv
);
2591 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run
);