]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/boot/bootctl.c
Merge pull request #25389 from fbuihuu/update-test-for-opensuse
[thirdparty/systemd.git] / src / boot / bootctl.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
7b4d7cc0 2
0974a682 3#include <ctype.h>
3f6fd1ba 4#include <errno.h>
3f6fd1ba
LP
5#include <getopt.h>
6#include <limits.h>
5fa6c13c 7#include <linux/magic.h>
0974a682 8#include <stdbool.h>
3f6fd1ba 9#include <stdlib.h>
3f6fd1ba 10#include <sys/mman.h>
3f6fd1ba 11#include <unistd.h>
7b4d7cc0 12
dccca82b
LP
13#include "sd-id128.h"
14
b5efdb8a 15#include "alloc-util.h"
3f6fd1ba 16#include "blkid-util.h"
7e87c7d9 17#include "bootspec.h"
d6b4d1c7 18#include "build.h"
80a2381d 19#include "chase-symlinks.h"
175d308c 20#include "copy.h"
7176f06c 21#include "devnum-util.h"
e41256dc 22#include "dirent-util.h"
80a2381d 23#include "dissect-image.h"
7be4b236 24#include "efi-api.h"
0bb2f0f1 25#include "efi-loader.h"
0974a682 26#include "efivars.h"
47fb161e 27#include "env-file.h"
e44c3229 28#include "env-util.h"
9dae4c8a 29#include "escape.h"
3ffd4af2 30#include "fd-util.h"
0d39fa9c 31#include "fileio.h"
e94830c0 32#include "find-esp.h"
175d308c 33#include "fs-util.h"
d8e32c47 34#include "glyph-util.h"
608f8ec9 35#include "main-func.h"
a4a55e9a 36#include "mkdir.h"
80a2381d 37#include "mount-util.h"
f337f903 38#include "os-util.h"
57db6f18 39#include "pager.h"
6a3fff75 40#include "parse-argument.h"
2f2c539c 41#include "parse-util.h"
294bf0c3 42#include "pretty-print.h"
e44c3229 43#include "random-util.h"
c6878637 44#include "rm-rf.h"
175d308c 45#include "stat-util.h"
341890de 46#include "stdio-util.h"
bb682057 47#include "string-table.h"
07630cea 48#include "string-util.h"
2f2c539c 49#include "strv.h"
bf819d3a 50#include "sync-util.h"
7e87c7d9 51#include "terminal-util.h"
e4de7287 52#include "tmpfile-util.h"
47fb161e 53#include "tmpfile-util-label.h"
0ea911d1 54#include "tpm2-util.h"
2f2c539c 55#include "umask-util.h"
d88c96ff 56#include "utf8.h"
2f2c539c
LP
57#include "verbs.h"
58#include "virt.h"
7b4d7cc0 59
d9bdb29b
RH
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)
65
fbf45d22
LP
66static char *arg_esp_path = NULL;
67static char *arg_xbootldr_path = NULL;
68static bool arg_print_esp_path = false;
69static bool arg_print_dollar_boot_path = false;
25579a43 70static bool arg_touch_variables = true;
0221d68a 71static PagerFlags arg_pager_flags = 0;
351de38e 72static bool arg_graceful = false;
14e6e444 73static bool arg_quiet = false;
f337f903 74static int arg_make_entry_directory = false; /* tri-state: < 0 for automatic logic */
47fb161e
ZJS
75static sd_id128_t arg_machine_id = SD_ID128_NULL;
76static char *arg_install_layout = NULL;
f337f903
LP
77static enum {
78 ARG_ENTRY_TOKEN_MACHINE_ID,
79 ARG_ENTRY_TOKEN_OS_IMAGE_ID,
80 ARG_ENTRY_TOKEN_OS_ID,
81 ARG_ENTRY_TOKEN_LITERAL,
82 ARG_ENTRY_TOKEN_AUTO,
83} arg_entry_token_type = ARG_ENTRY_TOKEN_AUTO;
84static char *arg_entry_token = NULL;
0d1506d4 85static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
6e916539 86static bool arg_arch_all = false;
80a2381d
LB
87static char *arg_root = NULL;
88static char *arg_image = NULL;
02d06ba1
LB
89static enum {
90 ARG_INSTALL_SOURCE_IMAGE,
91 ARG_INSTALL_SOURCE_HOST,
92 ARG_INSTALL_SOURCE_AUTO,
93} arg_install_source = ARG_INSTALL_SOURCE_AUTO;
d9bdb29b 94static char *arg_efi_boot_option_description = NULL;
25579a43 95
fbf45d22
LP
96STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
97STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
47fb161e 98STATIC_DESTRUCTOR_REGISTER(arg_install_layout, freep);
f337f903 99STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep);
80a2381d
LB
100STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
101STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
d9bdb29b 102STATIC_DESTRUCTOR_REGISTER(arg_efi_boot_option_description, freep);
fbf45d22
LP
103
104static 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;
107}
608f8ec9 108
d9bdb29b
RH
109static const char *pick_efi_boot_option_description(void) {
110 return arg_efi_boot_option_description ?: "Linux Boot Manager";
111}
112
5caa3167
LP
113static int acquire_esp(
114 bool unprivileged_mode,
e5a8b4b5 115 bool graceful,
5caa3167
LP
116 uint32_t *ret_part,
117 uint64_t *ret_pstart,
118 uint64_t *ret_psize,
f63b5ad9
LP
119 sd_id128_t *ret_uuid,
120 dev_t *ret_devid) {
5caa3167
LP
121
122 char *np;
2f2c539c
LP
123 int r;
124
fbf45d22
LP
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
129 * this). */
5caa3167 130
80a2381d 131 r = find_esp_and_warn(arg_root, arg_esp_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid);
e5a8b4b5
LP
132 if (r == -ENOKEY) {
133 if (graceful)
14e6e444
ZJS
134 return log_full_errno(arg_quiet ? LOG_DEBUG : LOG_INFO, r,
135 "Couldn't find EFI system partition, skipping.");
e5a8b4b5 136
af918182 137 return log_error_errno(r,
5caa3167 138 "Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n"
fbf45d22 139 "Alternatively, use --esp-path= to specify path to mount point.");
e5a8b4b5 140 }
5caa3167
LP
141 if (r < 0)
142 return r;
143
fbf45d22
LP
144 free_and_replace(arg_esp_path, np);
145 log_debug("Using EFI System Partition at %s.", arg_esp_path);
146
957b2423 147 return 0;
fbf45d22 148}
0974a682 149
f63b5ad9
LP
150static int acquire_xbootldr(
151 bool unprivileged_mode,
152 sd_id128_t *ret_uuid,
153 dev_t *ret_devid) {
154
fbf45d22
LP
155 char *np;
156 int r;
5caa3167 157
80a2381d 158 r = find_xbootldr_and_warn(arg_root, arg_xbootldr_path, unprivileged_mode, &np, ret_uuid, ret_devid);
fbf45d22
LP
159 if (r == -ENOKEY) {
160 log_debug_errno(r, "Didn't find an XBOOTLDR partition, using the ESP as $BOOT.");
f63b5ad9
LP
161 arg_xbootldr_path = mfree(arg_xbootldr_path);
162
fbf45d22
LP
163 if (ret_uuid)
164 *ret_uuid = SD_ID128_NULL;
f63b5ad9
LP
165 if (ret_devid)
166 *ret_devid = 0;
fbf45d22
LP
167 return 0;
168 }
169 if (r < 0)
170 return r;
171
172 free_and_replace(arg_xbootldr_path, np);
173 log_debug("Using XBOOTLDR partition at %s as $BOOT.", arg_xbootldr_path);
174
175 return 1;
7b4d7cc0
KS
176}
177
b72676e7
LP
178static int load_etc_machine_id(void) {
179 int r;
180
181 r = sd_id128_get_machine(&arg_machine_id);
182 if (IN_SET(r, -ENOENT, -ENOMEDIUM)) /* Not set or empty */
183 return 0;
184 if (r < 0)
185 return log_error_errno(r, "Failed to get machine-id: %m");
186
187 log_debug("Loaded machine ID %s from /etc/machine-id.", SD_ID128_TO_STRING(arg_machine_id));
188 return 0;
189}
190
191static int load_etc_machine_info(void) {
ea29abec
LP
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. */
47fb161e 196 _cleanup_free_ char *s = NULL, *layout = NULL;
6a3fff75 197 int r;
198
47fb161e
ZJS
199 r = parse_env_file(NULL, "/etc/machine-info",
200 "KERNEL_INSTALL_LAYOUT", &layout,
201 "KERNEL_INSTALL_MACHINE_ID", &s);
b72676e7
LP
202 if (r == -ENOENT)
203 return 0;
204 if (r < 0)
47fb161e
ZJS
205 return log_error_errno(r, "Failed to parse /etc/machine-info: %m");
206
b72676e7 207 if (!isempty(s)) {
14e6e444
ZJS
208 if (!arg_quiet)
209 log_notice("Read $KERNEL_INSTALL_MACHINE_ID from /etc/machine-info. "
210 "Please move it to /etc/kernel/entry-token.");
ea29abec 211
47fb161e
ZJS
212 r = sd_id128_from_string(s, &arg_machine_id);
213 if (r < 0)
214 return log_error_errno(r, "Failed to parse KERNEL_INSTALL_MACHINE_ID=%s in /etc/machine-info: %m", s);
215
b72676e7
LP
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));
47fb161e 218 }
47fb161e
ZJS
219
220 if (!isempty(layout)) {
14e6e444
ZJS
221 if (!arg_quiet)
222 log_notice("Read $KERNEL_INSTALL_LAYOUT from /etc/machine-info. "
223 "Please move it to the layout= setting of /etc/kernel/install.conf.");
ea29abec 224
47fb161e 225 log_debug("KERNEL_INSTALL_LAYOUT=%s is specified in /etc/machine-info.", layout);
b72676e7
LP
226 free_and_replace(arg_install_layout, layout);
227 }
228
229 return 0;
230}
231
232static int load_etc_kernel_install_conf(void) {
233 _cleanup_free_ char *layout = NULL;
234 int r;
235
236 r = parse_env_file(NULL, "/etc/kernel/install.conf",
237 "layout", &layout);
238 if (r == -ENOENT)
239 return 0;
240 if (r < 0)
241 return log_error_errno(r, "Failed to parse /etc/kernel/install.conf: %m");
242
243 if (!isempty(layout)) {
244 log_debug("layout=%s is specified in /etc/machine-info.", layout);
245 free_and_replace(arg_install_layout, layout);
47fb161e 246 }
6a3fff75 247
47fb161e
ZJS
248 return 0;
249}
250
f337f903 251static int settle_entry_token(void) {
47fb161e
ZJS
252 int r;
253
f337f903 254 switch (arg_entry_token_type) {
6a3fff75 255
f337f903
LP
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");
261
262 if (!isempty(buf)) {
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;
267
268 r = parse_os_release(NULL,
269 "IMAGE_ID", &image_id,
270 "ID", &id);
271 if (r < 0)
272 return log_error_errno(r, "Failed to load /etc/os-release: %m");
273
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;
280 } else
281 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No machine ID set, and /etc/os-release carries no ID=/IMAGE_ID= fields.");
282 } else {
283 r = free_and_strdup_warn(&arg_entry_token, SD_ID128_TO_STRING(arg_machine_id));
47fb161e 284 if (r < 0)
f337f903
LP
285 return r;
286
287 arg_entry_token_type = ARG_ENTRY_TOKEN_MACHINE_ID;
47fb161e 288 }
f337f903
LP
289
290 break;
47fb161e
ZJS
291 }
292
f337f903
LP
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.");
296
297 r = free_and_strdup_warn(&arg_entry_token, SD_ID128_TO_STRING(arg_machine_id));
298 if (r < 0)
299 return r;
300
301 break;
302
303 case ARG_ENTRY_TOKEN_OS_IMAGE_ID: {
304 _cleanup_free_ char *buf = NULL;
305
306 r = parse_os_release(NULL, "IMAGE_ID", &buf);
307 if (r < 0)
308 return log_error_errno(r, "Failed to load /etc/os-release: %m");
309
310 if (isempty(buf))
311 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "IMAGE_ID= field not set in /etc/os-release.");
17e2e807 312
f337f903
LP
313 free_and_replace(arg_entry_token, buf);
314 break;
315 }
316
317 case ARG_ENTRY_TOKEN_OS_ID: {
318 _cleanup_free_ char *buf = NULL;
319
320 r = parse_os_release(NULL, "ID", &buf);
321 if (r < 0)
322 return log_error_errno(r, "Failed to load /etc/os-release: %m");
323
324 if (isempty(buf))
325 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "ID= field not set in /etc/os-release.");
326
327 free_and_replace(arg_entry_token, buf);
328 break;
329 }
330
331 case ARG_ENTRY_TOKEN_LITERAL:
332 assert(!isempty(arg_entry_token)); /* already filled in by command line parser */
333 break;
334 }
335
d9bdb29b 336 if (isempty(arg_entry_token) || !(utf8_is_valid(arg_entry_token) && string_is_safe(arg_entry_token)))
f337f903
LP
337 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected entry token not valid: %s", arg_entry_token);
338
339 log_debug("Using entry token: %s", arg_entry_token);
340 return 0;
341}
342
343static 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");
347}
348
349static int settle_make_entry_directory(void) {
350 int r;
351
b72676e7
LP
352 r = load_etc_machine_id();
353 if (r < 0)
354 return r;
355
356 r = load_etc_machine_info();
357 if (r < 0)
358 return r;
359
360 r = load_etc_kernel_install_conf();
f337f903
LP
361 if (r < 0)
362 return r;
363
364 r = settle_entry_token();
365 if (r < 0)
366 return r;
367
368 bool layout_type1 = use_boot_loader_spec_type1();
369 if (arg_make_entry_directory < 0) { /* Automatic mode */
370 if (layout_type1) {
371 if (arg_entry_token == ARG_ENTRY_TOKEN_MACHINE_ID) {
372 r = path_is_temporary_fs("/etc/machine-id");
373 if (r < 0)
374 return log_debug_errno(r, "Couldn't determine whether /etc/machine-id is on a temporary file system: %m");
375
376 arg_make_entry_directory = r == 0;
377 } else
378 arg_make_entry_directory = true;
379 } else
380 arg_make_entry_directory = false;
381 }
382
383 if (arg_make_entry_directory > 0 && !layout_type1)
47fb161e 384 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
f337f903 385 "KERNEL_INSTALL_LAYOUT=%s is configured, but Boot Loader Specification Type #1 entry directory creation was requested.",
47fb161e
ZJS
386 arg_install_layout);
387
388 return 0;
6a3fff75 389}
390
e7dd673d 391/* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */
d3226d77 392static int get_file_version(int fd, char **v) {
0974a682
KS
393 struct stat st;
394 char *buf;
395 const char *s, *e;
396 char *x = NULL;
c53aafb7 397 int r;
7b4d7cc0 398
d3226d77 399 assert(fd >= 0);
0974a682 400 assert(v);
7b4d7cc0 401
d3226d77 402 if (fstat(fd, &st) < 0)
db6d9fae 403 return log_error_errno(errno, "Failed to stat EFI binary: %m");
7b4d7cc0 404
c4ba5b51
LP
405 r = stat_verify_regular(&st);
406 if (r < 0)
d90f2add 407 return log_error_errno(r, "EFI binary is not a regular file: %m");
c4ba5b51 408
1a823cde 409 if (st.st_size < 27 || file_offset_beyond_memory_size(st.st_size)) {
db6d9fae 410 *v = NULL;
0974a682 411 return 0;
db6d9fae 412 }
eb9da376 413
d3226d77 414 buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
0974a682 415 if (buf == MAP_FAILED)
db6d9fae 416 return log_error_errno(errno, "Failed to memory map EFI binary: %m");
7b4d7cc0 417
d8782cc5 418 s = mempmem_safe(buf, st.st_size - 8, "#### LoaderInfo: ", 17);
0974a682
KS
419 if (!s)
420 goto finish;
7b4d7cc0 421
e8b08edc 422 e = memmem_safe(s, st.st_size - (s - buf), " ####", 5);
0974a682 423 if (!e || e - s < 3) {
78d5d4ed 424 r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Malformed version string.");
0974a682
KS
425 goto finish;
426 }
7b4d7cc0 427
0974a682
KS
428 x = strndup(s, e - s);
429 if (!x) {
d3226d77 430 r = log_oom();
0974a682
KS
431 goto finish;
432 }
433 r = 1;
7b4d7cc0 434
0974a682 435finish:
db6d9fae 436 (void) munmap(buf, st.st_size);
0974a682
KS
437 *v = x;
438 return r;
439}
7b4d7cc0 440
6e916539
JJ
441static 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. */
444
445#ifdef __x86_64__
446 _cleanup_free_ char *platform_size = NULL;
447 int r;
448
449 r = read_one_line_file("/sys/firmware/efi/fw_platform_size", &platform_size);
450 if (r == -ENOENT)
451 return EFI_MACHINE_TYPE_NAME;
452 if (r < 0) {
c0f86d66 453 log_warning_errno(r, "Error reading EFI firmware word size, assuming '%i': %m", __WORDSIZE);
6e916539
JJ
454 return EFI_MACHINE_TYPE_NAME;
455 }
456
457 if (streq(platform_size, "64"))
458 return EFI_MACHINE_TYPE_NAME;
459 if (streq(platform_size, "32"))
460 return "ia32";
461
462 log_warning(
c0f86d66 463 "Unknown EFI firmware word size '%s', using default word size '%i' instead.",
6e916539
JJ
464 platform_size,
465 __WORDSIZE);
466#endif
467
468 return EFI_MACHINE_TYPE_NAME;
469}
470
84be9b63
LP
471static int enumerate_binaries(
472 const char *esp_path,
473 const char *path,
474 const char *prefix,
475 char **previous,
476 bool *is_first) {
477
d3226d77 478 _cleanup_closedir_ DIR *d = NULL;
3730dc5d 479 _cleanup_free_ char *p = NULL;
fbf45d22 480 int c = 0, r;
fbf45d22
LP
481
482 assert(esp_path);
483 assert(path);
84be9b63
LP
484 assert(previous);
485 assert(is_first);
0974a682 486
b353d5ee 487 r = chase_symlinks_and_opendir(path, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &p, &d);
3730dc5d
LP
488 if (r == -ENOENT)
489 return 0;
490 if (r < 0)
491 return log_error_errno(r, "Failed to read \"%s/%s\": %m", esp_path, path);
0974a682 492
e41256dc 493 FOREACH_DIRENT(de, d, break) {
d3226d77 494 _cleanup_free_ char *v = NULL;
fbf45d22 495 _cleanup_close_ int fd = -1;
0974a682 496
d3226d77 497 if (!endswith_no_case(de->d_name, ".efi"))
0974a682
KS
498 continue;
499
d3226d77 500 if (prefix && !startswith_no_case(de->d_name, prefix))
0974a682
KS
501 continue;
502
d3226d77
ZJS
503 fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
504 if (fd < 0)
505 return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
0974a682 506
d3226d77 507 r = get_file_version(fd, &v);
0974a682 508 if (r < 0)
d3226d77 509 return r;
84be9b63
LP
510
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 */
512 printf(" %s %s%s\n",
513 *is_first ? "File:" : " ",
514 special_glyph(SPECIAL_GLYPH_TREE_BRANCH), *previous);
515 *is_first = false;
516 *previous = mfree(*previous);
517 }
518
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 */
0974a682 522 if (r > 0)
84be9b63 523 r = asprintf(previous, "/%s/%s (%s%s%s)", path, de->d_name, ansi_highlight(), v, ansi_normal());
0974a682 524 else
84be9b63
LP
525 r = asprintf(previous, "/%s/%s", path, de->d_name);
526 if (r < 0)
527 return log_oom();
fbf45d22 528
0974a682 529 c++;
0974a682
KS
530 }
531
d3226d77 532 return c;
7b4d7cc0
KS
533}
534
0974a682 535static int status_binaries(const char *esp_path, sd_id128_t partition) {
84be9b63
LP
536 _cleanup_free_ char *last = NULL;
537 bool is_first = true;
538 int r, k;
0974a682 539
cb180369 540 printf("%sAvailable Boot Loaders on ESP:%s\n", ansi_underline(), ansi_normal());
0974a682 541
cd2d4c7f
YW
542 if (!esp_path) {
543 printf(" ESP: Cannot find or access mount point of ESP.\n\n");
544 return -ENOENT;
545 }
546
547 printf(" ESP: %s", esp_path);
548 if (!sd_id128_is_null(partition))
bd44566c 549 printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")", SD_ID128_FORMAT_VAL(partition));
cd2d4c7f 550 printf("\n");
0974a682 551
84be9b63
LP
552 r = enumerate_binaries(esp_path, "EFI/systemd", NULL, &last, &is_first);
553 if (r < 0) {
554 printf("\n");
555 return r;
556 }
557
558 k = enumerate_binaries(esp_path, "EFI/BOOT", "boot", &last, &is_first);
559 if (k < 0) {
560 printf("\n");
561 return k;
562 }
563
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 */
565 printf(" %s %s%s\n",
566 is_first ? "File:" : " ",
567 special_glyph(SPECIAL_GLYPH_TREE_RIGHT), last);
0974a682 568
14e6e444 569 if (r == 0 && !arg_quiet)
84be9b63
LP
570 log_info("systemd-boot not installed in ESP.");
571 if (k == 0 && !arg_quiet)
48184e43 572 log_info("No default/fallback boot loader installed in ESP.");
0974a682 573
882b3bd6 574 printf("\n");
84be9b63 575 return 0;
0974a682
KS
576}
577
51470e1e 578static int print_efi_option(uint16_t id, int *n_printed, bool in_order) {
7cb0f263
TA
579 _cleanup_free_ char *title = NULL;
580 _cleanup_free_ char *path = NULL;
0974a682
KS
581 sd_id128_t partition;
582 bool active;
c53aafb7 583 int r;
0974a682 584
51470e1e
LP
585 assert(n_printed);
586
0974a682 587 r = efi_get_boot_option(id, &title, &partition, &path, &active);
af1bed8e
LP
588 if (r == -ENOENT) {
589 log_debug_errno(r, "Boot option 0x%04X referenced but missing, ignoring: %m", id);
590 return 0;
591 }
0974a682 592 if (r < 0)
af1bed8e 593 return log_error_errno(r, "Failed to read boot option 0x%04X: %m", id);
7b4d7cc0 594
0974a682 595 /* print only configured entries with partition information */
51470e1e 596 if (!path || sd_id128_is_null(partition)) {
a7dcb75c 597 log_debug("Ignoring boot entry 0x%04X without partition information.", id);
0974a682 598 return 0;
51470e1e 599 }
0974a682
KS
600
601 efi_tilt_backslashes(path);
602
51470e1e
LP
603 if (*n_printed == 0) /* Print section title before first entry */
604 printf("%sBoot Loaders Listed in EFI Variables:%s\n", ansi_underline(), ansi_normal());
605
ba857253 606 printf(" Title: %s%s%s\n", ansi_highlight(), strna(title), ansi_normal());
0974a682
KS
607 printf(" ID: 0x%04X\n", id);
608 printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : "");
bd44566c
ZJS
609 printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
610 SD_ID128_FORMAT_VAL(partition));
9a6f746f 611 printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path);
0974a682
KS
612 printf("\n");
613
51470e1e
LP
614 (*n_printed)++;
615 return 1;
0974a682
KS
616}
617
618static int status_variables(void) {
d3226d77 619 _cleanup_free_ uint16_t *options = NULL, *order = NULL;
51470e1e 620 int n_options, n_order, n_printed = 0;
0974a682 621
0974a682 622 n_options = efi_get_boot_options(&options);
d3226d77 623 if (n_options == -ENOENT)
f939cff7
LP
624 return log_error_errno(n_options,
625 "Failed to access EFI variables, efivarfs"
d3226d77 626 " needs to be available at /sys/firmware/efi/efivars/.");
f939cff7 627 if (n_options < 0)
d3226d77 628 return log_error_errno(n_options, "Failed to read EFI boot entries: %m");
0974a682 629
0974a682 630 n_order = efi_get_boot_order(&order);
d3226d77 631 if (n_order == -ENOENT)
0974a682 632 n_order = 0;
d3226d77 633 else if (n_order < 0)
7709ef3a 634 return log_error_errno(n_order, "Failed to read EFI boot order: %m");
0974a682
KS
635
636 /* print entries in BootOrder first */
c67bd42b 637 for (int i = 0; i < n_order; i++)
51470e1e 638 (void) print_efi_option(order[i], &n_printed, /* in_order= */ true);
0974a682
KS
639
640 /* print remaining entries */
c67bd42b
ZJS
641 for (int i = 0; i < n_options; i++) {
642 for (int j = 0; j < n_order; j++)
d3226d77 643 if (options[i] == order[j])
a908cf0a 644 goto next_option;
0974a682 645
51470e1e 646 (void) print_efi_option(options[i], &n_printed, /* in_order= */ false);
a908cf0a
MM
647
648 next_option:
649 continue;
0974a682
KS
650 }
651
51470e1e
LP
652 if (n_printed == 0)
653 printf("No boot loaders listed in EFI Variables.\n\n");
654
d3226d77 655 return 0;
0974a682
KS
656}
657
8214758b
ZJS
658static int boot_config_load_and_select(
659 BootConfig *config,
660 const char *esp_path,
661 dev_t esp_devid,
662 const char *xbootldr_path,
663 dev_t xbootldr_devid) {
664
8214758b
ZJS
665 int r;
666
667 /* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would
668 * find the same entries twice. */
7176f06c 669 bool same = esp_path && xbootldr_path && devnum_set_and_equal(esp_devid, xbootldr_devid);
8214758b
ZJS
670
671 r = boot_config_load(config, esp_path, same ? NULL : xbootldr_path);
672 if (r < 0)
673 return r;
674
80a2381d
LB
675 if (!arg_root) {
676 _cleanup_strv_free_ char **efi_entries = NULL;
8214758b 677
80a2381d
LB
678 r = efi_loader_get_entries(&efi_entries);
679 if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
680 log_debug_errno(r, "Boot loader reported no entries.");
681 else if (r < 0)
682 log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
683 else
684 (void) boot_config_augment_from_loader(config, efi_entries, /* only_auto= */ false);
685 }
686
687 return boot_config_select_special_entries(config, /* skip_efivars= */ !!arg_root);
8214758b
ZJS
688}
689
81fed855 690static int status_entries(
8214758b 691 const BootConfig *config,
81fed855
LP
692 const char *esp_path,
693 sd_id128_t esp_partition_uuid,
694 const char *xbootldr_path,
695 sd_id128_t xbootldr_partition_uuid) {
696
81fed855
LP
697 sd_id128_t dollar_boot_partition_uuid;
698 const char *dollar_boot_path;
a099e035 699 int r;
7e87c7d9 700
8214758b 701 assert(config);
81fed855
LP
702 assert(esp_path || xbootldr_path);
703
704 if (xbootldr_path) {
705 dollar_boot_path = xbootldr_path;
706 dollar_boot_partition_uuid = xbootldr_partition_uuid;
707 } else {
708 dollar_boot_path = esp_path;
709 dollar_boot_partition_uuid = esp_partition_uuid;
710 }
711
cb180369
LP
712 printf("%sBoot Loader Entries:%s\n"
713 " $BOOT: %s", ansi_underline(), ansi_normal(), dollar_boot_path);
81fed855 714 if (!sd_id128_is_null(dollar_boot_partition_uuid))
bd44566c
ZJS
715 printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR ")",
716 SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid));
81fed855
LP
717 printf("\n\n");
718
8214758b
ZJS
719 if (config->default_entry < 0)
720 printf("%zu entries, no entry could be determined as default.\n", config->n_entries);
7e87c7d9 721 else {
cb180369 722 printf("%sDefault Boot Loader Entry:%s\n", ansi_underline(), ansi_normal());
a099e035 723
432ce537 724 r = show_boot_entry(
8214758b 725 boot_config_default_entry(config),
bb682057
LP
726 /* show_as_default= */ false,
727 /* show_as_selected= */ false,
728 /* show_discovered= */ false);
d3eb6072
ZJS
729 if (r > 0)
730 /* < 0 is already logged by the function itself, let's just emit an extra warning if
731 the default entry is broken */
732 printf("\nWARNING: default boot entry is broken\n");
7e87c7d9
ZJS
733 }
734
735 return 0;
736}
737
0974a682
KS
738static int compare_product(const char *a, const char *b) {
739 size_t x, y;
740
741 assert(a);
742 assert(b);
743
744 x = strcspn(a, " ");
745 y = strcspn(b, " ");
746 if (x != y)
747 return x < y ? -1 : x > y ? 1 : 0;
748
749 return strncmp(a, b, x);
750}
751
752static int compare_version(const char *a, const char *b) {
753 assert(a);
754 assert(b);
755
756 a += strcspn(a, " ");
757 a += strspn(a, " ");
758 b += strcspn(b, " ");
759 b += strspn(b, " ");
760
8087644a 761 return strverscmp_improved(a, b);
0974a682
KS
762}
763
175d308c 764static int version_check(int fd_from, const char *from, int fd_to, const char *to) {
d3226d77 765 _cleanup_free_ char *a = NULL, *b = NULL;
0974a682
KS
766 int r;
767
175d308c 768 assert(fd_from >= 0);
0974a682 769 assert(from);
175d308c 770 assert(fd_to >= 0);
0974a682
KS
771 assert(to);
772
175d308c 773 r = get_file_version(fd_from, &a);
0974a682 774 if (r < 0)
d3226d77 775 return r;
baaa35ad 776 if (r == 0)
e5a8b4b5 777 return log_notice_errno(SYNTHETIC_ERRNO(EREMOTE),
baaa35ad
ZJS
778 "Source file \"%s\" does not carry version information!",
779 from);
0974a682 780
175d308c 781 r = get_file_version(fd_to, &b);
0974a682 782 if (r < 0)
d3226d77 783 return r;
baaa35ad 784 if (r == 0 || compare_product(a, b) != 0)
e5a8b4b5 785 return log_notice_errno(SYNTHETIC_ERRNO(EREMOTE),
baaa35ad
ZJS
786 "Skipping \"%s\", since it's owned by another boot loader.",
787 to);
0974a682 788
e5a8b4b5 789 r = compare_version(a, b);
892fcb89 790 log_debug("Comparing versions: \"%s\" %s \"%s", a, comparison_operator(r), b);
e5a8b4b5 791 if (r < 0)
892fcb89
ZJS
792 return log_warning_errno(SYNTHETIC_ERRNO(ESTALE),
793 "Skipping \"%s\", since newer boot loader version in place already.", to);
794 if (r == 0)
795 return log_info_errno(SYNTHETIC_ERRNO(ESTALE),
796 "Skipping \"%s\", since same boot loader version in place already.", to);
0974a682 797
d3226d77 798 return 0;
0974a682
KS
799}
800
175d308c
LP
801static int copy_file_with_version_check(const char *from, const char *to, bool force) {
802 _cleanup_close_ int fd_from = -1, fd_to = -1;
803 _cleanup_free_ char *t = NULL;
0974a682 804 int r;
0974a682 805
175d308c
LP
806 fd_from = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
807 if (fd_from < 0)
d3226d77 808 return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", from);
0974a682
KS
809
810 if (!force) {
175d308c
LP
811 fd_to = open(to, O_RDONLY|O_CLOEXEC|O_NOCTTY);
812 if (fd_to < 0) {
6b8664cb 813 if (errno != ENOENT)
175d308c
LP
814 return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", to);
815 } else {
816 r = version_check(fd_from, from, fd_to, to);
817 if (r < 0)
818 return r;
819
820 if (lseek(fd_from, 0, SEEK_SET) == (off_t) -1)
6719ca72 821 return log_error_errno(errno, "Failed to seek in \"%s\": %m", from);
175d308c
LP
822
823 fd_to = safe_close(fd_to);
0974a682 824 }
175d308c 825 }
d3226d77 826
175d308c
LP
827 r = tempfn_random(to, NULL, &t);
828 if (r < 0)
829 return log_oom();
0974a682 830
175d308c
LP
831 RUN_WITH_UMASK(0000) {
832 fd_to = open(t, O_WRONLY|O_CREAT|O_CLOEXEC|O_EXCL|O_NOFOLLOW, 0644);
833 if (fd_to < 0)
834 return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", t);
0974a682
KS
835 }
836
f5fbe71d 837 r = copy_bytes(fd_from, fd_to, UINT64_MAX, COPY_REFLINK);
0974a682 838 if (r < 0) {
0675e94a
AJ
839 (void) unlink(t);
840 return log_error_errno(r, "Failed to copy data from \"%s\" to \"%s\": %m", from, t);
0974a682
KS
841 }
842
adc6f43b 843 (void) copy_times(fd_from, fd_to, 0);
0974a682 844
bf819d3a
LP
845 r = fsync_full(fd_to);
846 if (r < 0) {
0675e94a 847 (void) unlink_noerrno(t);
bf819d3a 848 return log_error_errno(r, "Failed to copy data from \"%s\" to \"%s\": %m", from, t);
0675e94a
AJ
849 }
850
b1c05b98 851 if (renameat(AT_FDCWD, t, AT_FDCWD, to) < 0) {
175d308c
LP
852 (void) unlink_noerrno(t);
853 return log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", t, to);
0974a682
KS
854 }
855
d3226d77 856 log_info("Copied \"%s\" to \"%s\".", from, to);
0974a682 857
175d308c 858 return 0;
0974a682
KS
859}
860
0974a682 861static int mkdir_one(const char *prefix, const char *suffix) {
e2600fd5 862 _cleanup_free_ char *p = NULL;
0974a682 863
e2600fd5 864 p = path_join(prefix, suffix);
0974a682 865 if (mkdir(p, 0700) < 0) {
d3226d77
ZJS
866 if (errno != EEXIST)
867 return log_error_errno(errno, "Failed to create \"%s\": %m", p);
0974a682 868 } else
d3226d77 869 log_info("Created \"%s\".", p);
7b4d7cc0 870
0974a682
KS
871 return 0;
872}
873
fbf45d22 874static const char *const esp_subdirs[] = {
e44c3229 875 /* The directories to place in the ESP */
d3226d77
ZJS
876 "EFI",
877 "EFI/systemd",
00f69504 878 "EFI/BOOT",
d3226d77 879 "loader",
9ee051b9 880 NULL
d3226d77
ZJS
881};
882
e44c3229
LP
883static const char *const dollar_boot_subdirs[] = {
884 /* The directories to place in the XBOOTLDR partition or the ESP, depending what exists */
885 "loader",
886 "loader/entries", /* Type #1 entries */
887 "EFI",
888 "EFI/Linux", /* Type #2 entries */
889 NULL
890};
891
892static int create_subdirs(const char *root, const char * const *subdirs) {
0974a682
KS
893 int r;
894
e44c3229
LP
895 STRV_FOREACH(i, subdirs) {
896 r = mkdir_one(root, *i);
d3226d77
ZJS
897 if (r < 0)
898 return r;
899 }
7b4d7cc0 900
7b4d7cc0 901 return 0;
7b4d7cc0
KS
902}
903
0974a682 904static int copy_one_file(const char *esp_path, const char *name, bool force) {
02d06ba1 905 char *root = IN_SET(arg_install_source, ARG_INSTALL_SOURCE_AUTO, ARG_INSTALL_SOURCE_IMAGE) ? arg_root : NULL;
80a2381d 906 _cleanup_free_ char *source_path = NULL, *dest_path = NULL, *p = NULL, *q = NULL;
5509f912 907 const char *e;
80a2381d
LB
908 char *dest_name, *s;
909 int r, ret;
0974a682 910
12caf727
ДГ
911 dest_name = strdupa_safe(name);
912 s = endswith_no_case(dest_name, ".signed");
913 if (s)
914 *s = 0;
915
80a2381d
LB
916 p = path_join(BOOTLIBDIR, name);
917 if (!p)
918 return log_oom();
919
b353d5ee 920 r = chase_symlinks(p, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &source_path, NULL);
02d06ba1
LB
921 /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
922 if (r == -ENOENT && root && arg_install_source == ARG_INSTALL_SOURCE_AUTO)
b353d5ee 923 r = chase_symlinks(p, NULL, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &source_path, NULL);
80a2381d
LB
924 if (r < 0)
925 return log_error_errno(r,
926 "Failed to resolve path %s%s%s: %m",
927 p,
02d06ba1 928 root ? " under directory " : "",
05abe850 929 strempty(root));
80a2381d
LB
930
931 q = path_join("/EFI/systemd/", dest_name);
932 if (!q)
933 return log_oom();
934
b353d5ee 935 r = chase_symlinks(q, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_NONEXISTENT, &dest_path, NULL);
80a2381d
LB
936 if (r < 0)
937 return log_error_errno(r, "Failed to resolve path %s under directory %s: %m", q, esp_path);
938
939 /* Note that if this fails we do the second copy anyway, but return this error code,
940 * so we stash it away in a separate variable. */
941 ret = copy_file_with_version_check(source_path, dest_path, force);
0974a682 942
12caf727 943 e = startswith(dest_name, "systemd-boot");
5509f912 944 if (e) {
80a2381d 945 _cleanup_free_ char *default_dest_path = NULL;
d3226d77 946 char *v;
0974a682
KS
947
948 /* Create the EFI default boot loader name (specified for removable devices) */
80a2381d 949 v = strjoina("/EFI/BOOT/BOOT", e);
846b8fc3 950 ascii_strupper(strrchr(v, '/') + 1);
0974a682 951
b353d5ee 952 r = chase_symlinks(v, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_NONEXISTENT, &default_dest_path, NULL);
80a2381d
LB
953 if (r < 0)
954 return log_error_errno(r, "Failed to resolve path %s under directory %s: %m", v, esp_path);
955
956 r = copy_file_with_version_check(source_path, default_dest_path, force);
957 if (r < 0 && ret == 0)
958 ret = r;
0974a682
KS
959 }
960
80a2381d 961 return ret;
7b4d7cc0
KS
962}
963
6e916539 964static int install_binaries(const char *esp_path, const char *arch, bool force) {
02d06ba1 965 char *root = IN_SET(arg_install_source, ARG_INSTALL_SOURCE_AUTO, ARG_INSTALL_SOURCE_IMAGE) ? arg_root : NULL;
d3226d77 966 _cleanup_closedir_ DIR *d = NULL;
80a2381d
LB
967 _cleanup_free_ char *path = NULL;
968 int r;
0974a682 969
b353d5ee 970 r = chase_symlinks_and_opendir(BOOTLIBDIR, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &path, &d);
02d06ba1
LB
971 /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
972 if (r == -ENOENT && root && arg_install_source == ARG_INSTALL_SOURCE_AUTO)
b353d5ee 973 r = chase_symlinks_and_opendir(BOOTLIBDIR, NULL, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &path, &d);
80a2381d 974 if (r < 0)
05abe850 975 return log_error_errno(r, "Failed to open boot loader directory %s%s: %m", strempty(root), BOOTLIBDIR);
0974a682 976
6e916539
JJ
977 const char *suffix = strjoina(arch, ".efi");
978 const char *suffix_signed = strjoina(arch, ".efi.signed");
979
80a2381d 980 FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read \"%s\": %m", path)) {
0974a682
KS
981 int k;
982
6e916539 983 if (!endswith_no_case(de->d_name, suffix) && !endswith_no_case(de->d_name, suffix_signed))
0974a682
KS
984 continue;
985
12caf727
ДГ
986 /* skip the .efi file, if there's a .signed version of it */
987 if (endswith_no_case(de->d_name, ".efi")) {
d16da79e 988 _cleanup_free_ const char *s = strjoin(de->d_name, ".signed");
12caf727
ДГ
989 if (!s)
990 return log_oom();
d16da79e 991 if (faccessat(dirfd(d), s, F_OK, 0) >= 0)
12caf727
ДГ
992 continue;
993 }
994
0974a682 995 k = copy_one_file(esp_path, de->d_name, force);
e5a8b4b5
LP
996 /* Don't propagate an error code if no update necessary, installed version already equal or
997 * newer version, or other boot loader in place. */
998 if (arg_graceful && IN_SET(k, -ESTALE, -EREMOTE))
999 continue;
0974a682
KS
1000 if (k < 0 && r == 0)
1001 r = k;
1002 }
1003
0974a682 1004 return r;
7b4d7cc0
KS
1005}
1006
b461576d 1007static bool same_entry(uint16_t id, sd_id128_t uuid, const char *path) {
d3226d77 1008 _cleanup_free_ char *opath = NULL;
0974a682 1009 sd_id128_t ouuid;
d3226d77 1010 int r;
7b4d7cc0 1011
d3226d77
ZJS
1012 r = efi_get_boot_option(id, NULL, &ouuid, &opath, NULL);
1013 if (r < 0)
0974a682
KS
1014 return false;
1015 if (!sd_id128_equal(uuid, ouuid))
d3226d77 1016 return false;
5c2e5957 1017
1018 /* Some motherboards convert the path to uppercase under certain circumstances
1019 * (e.g. after booting into the Boot Menu in the ASUS ROG STRIX B350-F GAMING),
1020 * so use case-insensitive checking */
1021 if (!strcaseeq_ptr(path, opath))
d3226d77 1022 return false;
0974a682 1023
d3226d77 1024 return true;
0974a682
KS
1025}
1026
1027static int find_slot(sd_id128_t uuid, const char *path, uint16_t *id) {
d3226d77 1028 _cleanup_free_ uint16_t *options = NULL;
0974a682 1029
da60e5b5 1030 int n = efi_get_boot_options(&options);
d3226d77
ZJS
1031 if (n < 0)
1032 return n;
0974a682 1033
e7dd673d 1034 /* find already existing systemd-boot entry */
da60e5b5 1035 for (int i = 0; i < n; i++)
0974a682 1036 if (same_entry(options[i], uuid, path)) {
d3226d77
ZJS
1037 *id = options[i];
1038 return 1;
0974a682
KS
1039 }
1040
1041 /* find free slot in the sorted BootXXXX variable list */
da60e5b5 1042 for (int i = 0; i < n; i++)
0974a682 1043 if (i != options[i]) {
d3226d77 1044 *id = i;
26d54e12 1045 return 0;
0974a682
KS
1046 }
1047
1048 /* use the next one */
da60e5b5 1049 if (n == 0xffff)
0974a682 1050 return -ENOSPC;
da60e5b5 1051 *id = n;
d3226d77 1052 return 0;
0974a682
KS
1053}
1054
1055static int insert_into_order(uint16_t slot, bool first) {
d3226d77
ZJS
1056 _cleanup_free_ uint16_t *order = NULL;
1057 uint16_t *t;
c67bd42b 1058 int n;
0974a682 1059
d3226d77
ZJS
1060 n = efi_get_boot_order(&order);
1061 if (n <= 0)
0974a682 1062 /* no entry, add us */
d3226d77 1063 return efi_set_boot_order(&slot, 1);
0974a682
KS
1064
1065 /* are we the first and only one? */
d3226d77
ZJS
1066 if (n == 1 && order[0] == slot)
1067 return 0;
0974a682
KS
1068
1069 /* are we already in the boot order? */
c67bd42b 1070 for (int i = 0; i < n; i++) {
0974a682
KS
1071 if (order[i] != slot)
1072 continue;
1073
1074 /* we do not require to be the first one, all is fine */
1075 if (!first)
d3226d77 1076 return 0;
0974a682
KS
1077
1078 /* move us to the first slot */
d3226d77 1079 memmove(order + 1, order, i * sizeof(uint16_t));
0974a682 1080 order[0] = slot;
d3226d77 1081 return efi_set_boot_order(order, n);
0974a682
KS
1082 }
1083
1084 /* extend array */
a7798cd8 1085 t = reallocarray(order, n + 1, sizeof(uint16_t));
d3226d77
ZJS
1086 if (!t)
1087 return -ENOMEM;
1088 order = t;
0974a682
KS
1089
1090 /* add us to the top or end of the list */
1091 if (first) {
d3226d77 1092 memmove(order + 1, order, n * sizeof(uint16_t));
0974a682
KS
1093 order[0] = slot;
1094 } else
d3226d77 1095 order[n] = slot;
0974a682 1096
d3226d77 1097 return efi_set_boot_order(order, n + 1);
0974a682
KS
1098}
1099
1100static int remove_from_order(uint16_t slot) {
7cb0f263 1101 _cleanup_free_ uint16_t *order = NULL;
c67bd42b 1102 int n;
0974a682 1103
d3226d77
ZJS
1104 n = efi_get_boot_order(&order);
1105 if (n <= 0)
1106 return n;
0974a682 1107
c67bd42b 1108 for (int i = 0; i < n; i++) {
0974a682
KS
1109 if (order[i] != slot)
1110 continue;
1111
d3226d77
ZJS
1112 if (i + 1 < n)
1113 memmove(order + i, order + i+1, (n - i) * sizeof(uint16_t));
1114 return efi_set_boot_order(order, n - 1);
0974a682
KS
1115 }
1116
d3226d77 1117 return 0;
0974a682
KS
1118}
1119
3730dc5d
LP
1120static int install_variables(
1121 const char *esp_path,
1122 uint32_t part,
1123 uint64_t pstart,
1124 uint64_t psize,
1125 sd_id128_t uuid,
1126 const char *path,
1127 bool first) {
1128
0974a682
KS
1129 uint16_t slot;
1130 int r;
1131
80a2381d
LB
1132 if (arg_root) {
1133 log_info("Acting on %s, skipping EFI variable setup.",
1134 arg_image ? "image" : "root directory");
1135 return 0;
1136 }
1137
0974a682 1138 if (!is_efi_boot()) {
d3226d77 1139 log_warning("Not booted with EFI, skipping EFI variable setup.");
0974a682
KS
1140 return 0;
1141 }
1142
b353d5ee 1143 r = chase_symlinks_and_access(path, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, NULL, NULL);
3730dc5d
LP
1144 if (r == -ENOENT)
1145 return 0;
1146 if (r < 0)
1147 return log_error_errno(r, "Cannot access \"%s/%s\": %m", esp_path, path);
7b4d7cc0 1148
0974a682 1149 r = find_slot(uuid, path, &slot);
d3226d77
ZJS
1150 if (r < 0)
1151 return log_error_errno(r,
1152 r == -ENOENT ?
1153 "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" :
1154 "Failed to determine current boot order: %m");
7b4d7cc0 1155
181ccb43 1156 if (first || r == 0) {
d9bdb29b 1157 r = efi_add_boot_option(slot, pick_efi_boot_option_description(),
0974a682
KS
1158 part, pstart, psize,
1159 uuid, path);
d3226d77
ZJS
1160 if (r < 0)
1161 return log_error_errno(r, "Failed to create EFI Boot variable entry: %m");
0974a682 1162
d9bdb29b 1163 log_info("Created EFI boot entry \"%s\".", pick_efi_boot_option_description());
d3226d77 1164 }
0974a682 1165
d3226d77 1166 return insert_into_order(slot, first);
0974a682
KS
1167}
1168
1169static int remove_boot_efi(const char *esp_path) {
d3226d77 1170 _cleanup_closedir_ DIR *d = NULL;
3730dc5d 1171 _cleanup_free_ char *p = NULL;
d3226d77 1172 int r, c = 0;
0974a682 1173
b353d5ee 1174 r = chase_symlinks_and_opendir("/EFI/BOOT", esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &p, &d);
3730dc5d
LP
1175 if (r == -ENOENT)
1176 return 0;
1177 if (r < 0)
1178 return log_error_errno(r, "Failed to open directory \"%s/EFI/BOOT\": %m", esp_path);
0974a682 1179
e41256dc 1180 FOREACH_DIRENT(de, d, break) {
d3226d77
ZJS
1181 _cleanup_close_ int fd = -1;
1182 _cleanup_free_ char *v = NULL;
0974a682 1183
d3226d77 1184 if (!endswith_no_case(de->d_name, ".efi"))
0974a682
KS
1185 continue;
1186
b7536c45 1187 if (!startswith_no_case(de->d_name, "boot"))
0974a682
KS
1188 continue;
1189
d3226d77 1190 fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
dd114e11 1191 if (fd < 0)
d3226d77 1192 return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
0974a682 1193
d3226d77 1194 r = get_file_version(fd, &v);
0974a682 1195 if (r < 0)
d3226d77
ZJS
1196 return r;
1197 if (r > 0 && startswith(v, "systemd-boot ")) {
1198 r = unlinkat(dirfd(d), de->d_name, 0);
1199 if (r < 0)
1200 return log_error_errno(errno, "Failed to remove \"%s/%s\": %m", p, de->d_name);
1201
a592ab6a 1202 log_info("Removed \"%s/%s\".", p, de->d_name);
0974a682
KS
1203 }
1204
1205 c++;
0974a682
KS
1206 }
1207
d3226d77 1208 return c;
0974a682
KS
1209}
1210
1211static int rmdir_one(const char *prefix, const char *suffix) {
270384b2 1212 const char *p;
7b4d7cc0 1213
270384b2 1214 p = prefix_roota(prefix, suffix);
0974a682 1215 if (rmdir(p) < 0) {
fbf45d22
LP
1216 bool ignore = IN_SET(errno, ENOENT, ENOTEMPTY);
1217
1218 log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, errno,
1219 "Failed to remove directory \"%s\": %m", p);
1220 if (!ignore)
1221 return -errno;
7b4d7cc0 1222 } else
d3226d77 1223 log_info("Removed \"%s\".", p);
7b4d7cc0 1224
0974a682 1225 return 0;
7b4d7cc0
KS
1226}
1227
e44c3229
LP
1228static int remove_subdirs(const char *root, const char *const *subdirs) {
1229 int r, q;
fbf45d22 1230
e44c3229
LP
1231 /* We use recursion here to destroy the directories in reverse order. Which should be safe given how
1232 * short the array is. */
fbf45d22 1233
e44c3229
LP
1234 if (!subdirs[0]) /* A the end of the list */
1235 return 0;
fbf45d22 1236
e44c3229
LP
1237 r = remove_subdirs(root, subdirs + 1);
1238 q = rmdir_one(root, subdirs[0]);
1239
1240 return r < 0 ? r : q;
1241}
1242
f337f903 1243static int remove_entry_directory(const char *root) {
6a3fff75 1244 assert(root);
f337f903 1245 assert(arg_make_entry_directory >= 0);
6a3fff75 1246
f337f903 1247 if (!arg_make_entry_directory || !arg_entry_token)
6a3fff75 1248 return 0;
1249
f337f903 1250 return rmdir_one(root, arg_entry_token);
6a3fff75 1251}
1252
0974a682 1253static int remove_binaries(const char *esp_path) {
270384b2 1254 const char *p;
0974a682
KS
1255 int r, q;
1256
270384b2 1257 p = prefix_roota(esp_path, "/EFI/systemd");
c6878637 1258 r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
0974a682
KS
1259
1260 q = remove_boot_efi(esp_path);
1261 if (q < 0 && r == 0)
1262 r = q;
1263
0974a682
KS
1264 return r;
1265}
1266
e44c3229 1267static int remove_file(const char *root, const char *file) {
fbf45d22
LP
1268 const char *p;
1269
e44c3229
LP
1270 assert(root);
1271 assert(file);
fbf45d22 1272
e44c3229 1273 p = prefix_roota(root, file);
fbf45d22 1274 if (unlink(p) < 0) {
e44c3229
LP
1275 log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
1276 "Failed to unlink file \"%s\": %m", p);
fbf45d22 1277
e44c3229
LP
1278 return errno == ENOENT ? 0 : -errno;
1279 }
fbf45d22 1280
e44c3229
LP
1281 log_info("Removed \"%s\".", p);
1282 return 1;
fbf45d22
LP
1283}
1284
0974a682
KS
1285static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) {
1286 uint16_t slot;
1287 int r;
1288
80a2381d 1289 if (arg_root || !is_efi_boot())
0974a682
KS
1290 return 0;
1291
1292 r = find_slot(uuid, path, &slot);
1293 if (r != 1)
1294 return 0;
1295
1296 r = efi_remove_boot_option(slot);
1297 if (r < 0)
1298 return r;
1299
1300 if (in_order)
d3226d77 1301 return remove_from_order(slot);
f939cff7
LP
1302
1303 return 0;
0974a682
KS
1304}
1305
e44c3229 1306static int remove_loader_variables(void) {
e44c3229
LP
1307 int r = 0;
1308
1309 /* Remove all persistent loader variables we define */
1310
5980d463 1311 FOREACH_STRING(var,
e6f055cb
ZJS
1312 EFI_LOADER_VARIABLE(LoaderConfigTimeout),
1313 EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot),
1314 EFI_LOADER_VARIABLE(LoaderEntryDefault),
1315 EFI_LOADER_VARIABLE(LoaderEntryOneShot),
1316 EFI_LOADER_VARIABLE(LoaderSystemToken)){
e44c3229
LP
1317
1318 int q;
1319
5980d463 1320 q = efi_set_variable(var, NULL, 0);
e44c3229
LP
1321 if (q == -ENOENT)
1322 continue;
1323 if (q < 0) {
5980d463 1324 log_warning_errno(q, "Failed to remove EFI variable %s: %m", var);
e44c3229
LP
1325 if (r >= 0)
1326 r = q;
1327 } else
5980d463 1328 log_info("Removed EFI variable %s.", var);
e44c3229
LP
1329 }
1330
1331 return r;
1332}
1333
31e57550 1334static int install_loader_config(const char *esp_path) {
f5b84de2
LP
1335 _cleanup_(unlink_and_freep) char *t = NULL;
1336 _cleanup_fclose_ FILE *f = NULL;
d5ff6d6d 1337 const char *p;
b46c3e49 1338 int r;
0974a682 1339
f337f903 1340 assert(arg_make_entry_directory >= 0);
6a3fff75 1341
270384b2 1342 p = prefix_roota(esp_path, "/loader/loader.conf");
f5b84de2
LP
1343 if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */
1344 return 0;
1345
a5b30e15
LP
1346 r = fopen_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t, &f);
1347 if (r < 0)
1348 return log_error_errno(r, "Failed to open \"%s\" for writing: %m", p);
0974a682 1349
a36b411e 1350 fprintf(f, "#timeout 3\n"
31e57550 1351 "#console-mode keep\n");
85b55869 1352
f337f903
LP
1353 if (arg_make_entry_directory) {
1354 assert(arg_entry_token);
1355 fprintf(f, "default %s-*\n", arg_entry_token);
6a3fff75 1356 }
0974a682 1357
a5b30e15 1358 r = flink_tmpfile(f, t, p);
f5b84de2
LP
1359 if (r == -EEXIST)
1360 return 0; /* Silently skip creation if the file exists now (recheck) */
1361 if (r < 0)
1362 return log_error_errno(r, "Failed to move \"%s\" into place: %m", p);
1363
1364 t = mfree(t);
f5b84de2 1365 return 1;
0974a682
KS
1366}
1367
80889bd9
LP
1368static int install_loader_specification(const char *root) {
1369 _cleanup_(unlink_and_freep) char *t = NULL;
1370 _cleanup_fclose_ FILE *f = NULL;
1371 _cleanup_free_ char *p = NULL;
1372 int r;
1373
1374 p = path_join(root, "/loader/entries.srel");
1375 if (!p)
1376 return log_oom();
1377
1378 if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */
1379 return 0;
1380
1381 r = fopen_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t, &f);
1382 if (r < 0)
1383 return log_error_errno(r, "Failed to open \"%s\" for writing: %m", p);
1384
1385 fprintf(f, "type1\n");
1386
1387 r = flink_tmpfile(f, t, p);
1388 if (r == -EEXIST)
1389 return 0; /* Silently skip creation if the file exists now (recheck) */
1390 if (r < 0)
1391 return log_error_errno(r, "Failed to move \"%s\" into place: %m", p);
1392
1393 t = mfree(t);
1394 return 1;
1395}
1396
f337f903 1397static int install_entry_directory(const char *root) {
6a3fff75 1398 assert(root);
f337f903 1399 assert(arg_make_entry_directory >= 0);
6a3fff75 1400
f337f903 1401 if (!arg_make_entry_directory)
6a3fff75 1402 return 0;
1403
f337f903
LP
1404 assert(arg_entry_token);
1405 return mkdir_one(root, arg_entry_token);
47fb161e
ZJS
1406}
1407
f337f903 1408static int install_entry_token(void) {
47fb161e
ZJS
1409 int r;
1410
f337f903
LP
1411 assert(arg_make_entry_directory >= 0);
1412 assert(arg_entry_token);
47fb161e 1413
f337f903
LP
1414 /* Let's save the used entry token in /etc/kernel/entry-token if we used it to create the entry
1415 * directory, or if anything else but the machine ID */
47fb161e 1416
f337f903
LP
1417 if (!arg_make_entry_directory && arg_entry_token_type == ARG_ENTRY_TOKEN_MACHINE_ID)
1418 return 0;
47fb161e 1419
f337f903 1420 r = write_string_file("/etc/kernel/entry-token", arg_entry_token, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_MKDIR_0755);
6a3fff75 1421 if (r < 0)
f337f903 1422 return log_error_errno(r, "Failed to write entry token '%s' to /etc/kernel/entry-token", arg_entry_token);
47fb161e 1423
47fb161e 1424 return 0;
6a3fff75 1425}
1426
2f2c539c 1427static int help(int argc, char *argv[], void *userdata) {
37ec0fdd
LP
1428 _cleanup_free_ char *link = NULL;
1429 int r;
1430
1431 r = terminal_urlify_man("bootctl", "1", &link);
1432 if (r < 0)
1433 return log_oom();
2f2c539c 1434
64a7fcc5
LP
1435 printf("%1$s [OPTIONS...] COMMAND ...\n"
1436 "\n%5$sControl EFI firmware boot settings and manage boot loader.%6$s\n"
1437 "\n%3$sGeneric EFI Firmware/Boot Loader Commands:%4$s\n"
1438 " status Show status of installed boot loader and EFI variables\n"
002914e6
LP
1439 " reboot-to-firmware [BOOL]\n"
1440 " Query or set reboot-to-firmware EFI flag\n"
64a7fcc5
LP
1441 " systemd-efi-options [STRING]\n"
1442 " Query or set system options string in EFI variable\n"
1443 "\n%3$sBoot Loader Specification Commands:%4$s\n"
2536752d
ZJS
1444 " list List boot loader entries\n"
1445 " set-default ID Set default boot loader entry\n"
1446 " set-oneshot ID Set default boot loader entry, for next boot only\n"
39ddc32a
JJ
1447 " set-timeout SECONDS Set the menu timeout\n"
1448 " set-timeout-oneshot SECONDS\n"
1449 " Set the menu timeout for the next boot only\n"
64a7fcc5
LP
1450 "\n%3$ssystemd-boot Commands:%4$s\n"
1451 " install Install systemd-boot to the ESP and EFI variables\n"
1452 " update Update systemd-boot in the ESP and EFI variables\n"
1453 " remove Remove systemd-boot from the ESP and EFI variables\n"
1454 " is-installed Test whether systemd-boot is installed in the ESP\n"
1455 " random-seed Initialize random seed in ESP and EFI variables\n"
1456 "\n%3$sOptions:%4$s\n"
e1fac8a6
ZJS
1457 " -h --help Show this help\n"
1458 " --version Print version\n"
1459 " --esp-path=PATH Path to the EFI System Partition (ESP)\n"
1460 " --boot-path=PATH Path to the $BOOT partition\n"
80a2381d
LB
1461 " --root=PATH Operate on an alternate filesystem root\n"
1462 " --image=PATH Operate on disk image as filesystem root\n"
02d06ba1
LB
1463 " --install-source=auto|image|host\n"
1464 " Where to pick files when using --root=/--image=\n"
e1fac8a6
ZJS
1465 " -p --print-esp-path Print path to the EFI System Partition\n"
1466 " -x --print-boot-path Print path to the $BOOT partition\n"
1467 " --no-variables Don't touch EFI variables\n"
1468 " --no-pager Do not pipe output into a pager\n"
351de38e
LP
1469 " --graceful Don't fail when the ESP cannot be found or EFI\n"
1470 " variables cannot be written\n"
14e6e444 1471 " -q --quiet Suppress output\n"
f337f903
LP
1472 " --make-entry-directory=yes|no|auto\n"
1473 " Create $BOOT/ENTRY-TOKEN/ directory\n"
1474 " --entry-token=machine-id|os-id|os-image-id|auto|literal:…\n"
1475 " Entry token to use for this installation\n"
0d1506d4
LP
1476 " --json=pretty|short|off\n"
1477 " Generate JSON output\n"
6e916539
JJ
1478 " --all-architectures\n"
1479 " Install all supported EFI architectures\n"
d9bdb29b
RH
1480 " --efi-boot-option-description=DESCRIPTION\n"
1481 " Description of the entry in the boot option list\n"
bc556335
DDM
1482 "\nSee the %2$s for details.\n",
1483 program_invocation_short_name,
1484 link,
1485 ansi_underline(),
1486 ansi_normal(),
1487 ansi_highlight(),
1488 ansi_normal());
0974a682
KS
1489
1490 return 0;
1491}
1492
0974a682
KS
1493static int parse_argv(int argc, char *argv[]) {
1494 enum {
fbf45d22
LP
1495 ARG_ESP_PATH = 0x100,
1496 ARG_BOOT_PATH,
80a2381d
LB
1497 ARG_ROOT,
1498 ARG_IMAGE,
02d06ba1 1499 ARG_INSTALL_SOURCE,
0974a682
KS
1500 ARG_VERSION,
1501 ARG_NO_VARIABLES,
57db6f18 1502 ARG_NO_PAGER,
351de38e 1503 ARG_GRACEFUL,
f337f903
LP
1504 ARG_MAKE_ENTRY_DIRECTORY,
1505 ARG_ENTRY_TOKEN,
0d1506d4 1506 ARG_JSON,
6e916539 1507 ARG_ARCH_ALL,
d9bdb29b 1508 ARG_EFI_BOOT_OPTION_DESCRIPTION,
7b4d7cc0
KS
1509 };
1510
0974a682 1511 static const struct option options[] = {
d9bdb29b
RH
1512 { "help", no_argument, NULL, 'h' },
1513 { "version", no_argument, NULL, ARG_VERSION },
1514 { "esp-path", required_argument, NULL, ARG_ESP_PATH },
1515 { "path", required_argument, NULL, ARG_ESP_PATH }, /* Compatibility alias */
1516 { "boot-path", required_argument, NULL, ARG_BOOT_PATH },
1517 { "root", required_argument, NULL, ARG_ROOT },
1518 { "image", required_argument, NULL, ARG_IMAGE },
1519 { "install-source", required_argument, NULL, ARG_INSTALL_SOURCE },
1520 { "print-esp-path", no_argument, NULL, 'p' },
1521 { "print-path", no_argument, NULL, 'p' }, /* Compatibility alias */
1522 { "print-boot-path", no_argument, NULL, 'x' },
1523 { "no-variables", no_argument, NULL, ARG_NO_VARIABLES },
1524 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1525 { "graceful", no_argument, NULL, ARG_GRACEFUL },
1526 { "quiet", no_argument, NULL, 'q' },
1527 { "make-entry-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY },
1528 { "make-machine-id-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, /* Compatibility alias */
1529 { "entry-token", required_argument, NULL, ARG_ENTRY_TOKEN },
1530 { "json", required_argument, NULL, ARG_JSON },
1531 { "all-architectures", no_argument, NULL, ARG_ARCH_ALL },
1532 { "efi-boot-option-description", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION },
a36b411e 1533 {}
0974a682
KS
1534 };
1535
2f2c539c 1536 int c, r;
6a3fff75 1537 bool b;
7b4d7cc0
KS
1538
1539 assert(argc >= 0);
1540 assert(argv);
1541
fba4e945 1542 while ((c = getopt_long(argc, argv, "hpx", options, NULL)) >= 0)
0974a682 1543 switch (c) {
7b4d7cc0 1544
0974a682 1545 case 'h':
2f2c539c 1546 help(0, NULL, NULL);
7b4d7cc0 1547 return 0;
7b4d7cc0 1548
0974a682 1549 case ARG_VERSION:
3f6fd1ba 1550 return version();
7b4d7cc0 1551
fbf45d22
LP
1552 case ARG_ESP_PATH:
1553 r = free_and_strdup(&arg_esp_path, optarg);
1554 if (r < 0)
1555 return log_oom();
1556 break;
1557
1558 case ARG_BOOT_PATH:
1559 r = free_and_strdup(&arg_xbootldr_path, optarg);
2f2c539c
LP
1560 if (r < 0)
1561 return log_oom();
0974a682
KS
1562 break;
1563
80a2381d
LB
1564 case ARG_ROOT:
1565 r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
1566 if (r < 0)
1567 return r;
1568 break;
1569
1570 case ARG_IMAGE:
1571 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
1572 if (r < 0)
1573 return r;
1574 break;
1575
02d06ba1
LB
1576 case ARG_INSTALL_SOURCE:
1577 if (streq(optarg, "auto"))
1578 arg_install_source = ARG_INSTALL_SOURCE_AUTO;
1579 else if (streq(optarg, "image"))
1580 arg_install_source = ARG_INSTALL_SOURCE_IMAGE;
1581 else if (streq(optarg, "host"))
1582 arg_install_source = ARG_INSTALL_SOURCE_HOST;
1583 else
1584 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1585 "Unexpected parameter for --install-source=: %s", optarg);
1586
1587 break;
1588
30b50477 1589 case 'p':
aa467bca
ZJS
1590 if (arg_print_dollar_boot_path)
1591 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1592 "--print-boot-path/-x cannot be combined with --print-esp-path/-p");
fbf45d22
LP
1593 arg_print_esp_path = true;
1594 break;
1595
fba4e945 1596 case 'x':
aa467bca
ZJS
1597 if (arg_print_esp_path)
1598 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1599 "--print-boot-path/-x cannot be combined with --print-esp-path/-p");
fbf45d22 1600 arg_print_dollar_boot_path = true;
30b50477
ZJS
1601 break;
1602
0974a682
KS
1603 case ARG_NO_VARIABLES:
1604 arg_touch_variables = false;
1605 break;
1606
57db6f18 1607 case ARG_NO_PAGER:
0221d68a 1608 arg_pager_flags |= PAGER_DISABLE;
57db6f18
LP
1609 break;
1610
351de38e
LP
1611 case ARG_GRACEFUL:
1612 arg_graceful = true;
1613 break;
1614
14e6e444
ZJS
1615 case 'q':
1616 arg_quiet = true;
1617 break;
1618
f337f903
LP
1619 case ARG_ENTRY_TOKEN: {
1620 const char *e;
1621
1622 if (streq(optarg, "machine-id")) {
1623 arg_entry_token_type = ARG_ENTRY_TOKEN_MACHINE_ID;
1624 arg_entry_token = mfree(arg_entry_token);
1625 } else if (streq(optarg, "os-image-id")) {
1626 arg_entry_token_type = ARG_ENTRY_TOKEN_OS_IMAGE_ID;
1627 arg_entry_token = mfree(arg_entry_token);
1628 } else if (streq(optarg, "os-id")) {
1629 arg_entry_token_type = ARG_ENTRY_TOKEN_OS_ID;
1630 arg_entry_token = mfree(arg_entry_token);
1631 } else if ((e = startswith(optarg, "literal:"))) {
1632 arg_entry_token_type = ARG_ENTRY_TOKEN_LITERAL;
1633
1634 r = free_and_strdup_warn(&arg_entry_token, e);
1635 if (r < 0)
1636 return r;
1637 } else
1638 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1639 "Unexpected parameter for --entry-token=: %s", optarg);
1640
1641 break;
1642 }
1643
1644 case ARG_MAKE_ENTRY_DIRECTORY:
1ff493d5 1645 if (streq(optarg, "auto")) /* retained for backwards compatibility */
f337f903 1646 arg_make_entry_directory = -1; /* yes if machine-id is permanent */
6a3fff75 1647 else {
f337f903 1648 r = parse_boolean_argument("--make-entry-directory=", optarg, &b);
6a3fff75 1649 if (r < 0)
1650 return r;
f337f903
LP
1651
1652 arg_make_entry_directory = b;
6a3fff75 1653 }
1654 break;
1655
0d1506d4
LP
1656 case ARG_JSON:
1657 r = parse_json_argument(optarg, &arg_json_format_flags);
1658 if (r <= 0)
1659 return r;
6e916539 1660 break;
0d1506d4 1661
6e916539
JJ
1662 case ARG_ARCH_ALL:
1663 arg_arch_all = true;
0d1506d4
LP
1664 break;
1665
d9bdb29b
RH
1666 case ARG_EFI_BOOT_OPTION_DESCRIPTION:
1667 if (isempty(optarg) || !(string_is_safe(optarg) && utf8_is_valid(optarg))) {
1668 _cleanup_free_ char *escaped = NULL;
1669
1670 escaped = cescape(optarg);
1671 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1672 "Invalid --efi-boot-option-description=: %s", strna(escaped));
1673 }
1674 if (strlen(optarg) > EFI_BOOT_OPTION_DESCRIPTION_MAX)
1675 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1676 "--efi-boot-option-description= too long: %zu > %zu", strlen(optarg), EFI_BOOT_OPTION_DESCRIPTION_MAX);
1677 r = free_and_strdup_warn(&arg_efi_boot_option_description, optarg);
1678 if (r < 0)
1679 return r;
1680 break;
1681
0974a682
KS
1682 case '?':
1683 return -EINVAL;
1684
1685 default:
04499a70 1686 assert_not_reached();
7b4d7cc0 1687 }
7b4d7cc0 1688
80a2381d
LB
1689 if ((arg_root || arg_image) && argv[optind] && !STR_IN_SET(argv[optind], "status", "list",
1690 "install", "update", "remove", "is-installed", "random-seed"))
1691 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1692 "Options --root= and --image= are not supported with verb %s.",
1693 argv[optind]);
1694
1695 if (arg_root && arg_image)
1696 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
1697
02d06ba1
LB
1698 if (arg_install_source != ARG_INSTALL_SOURCE_AUTO && !arg_root && !arg_image)
1699 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--install-from-host is only supported with --root= or --image=.");
1700
0974a682
KS
1701 return 1;
1702}
7b4d7cc0 1703
e6f055cb 1704static void read_efi_var(const char *variable, char **ret) {
551710cf
ZJS
1705 int r;
1706
e6f055cb 1707 r = efi_get_variable_string(variable, ret);
551710cf 1708 if (r < 0 && r != -ENOENT)
e6f055cb 1709 log_warning_errno(r, "Failed to read EFI variable %s: %m", variable);
551710cf
ZJS
1710}
1711
607ebf2b 1712static void print_yes_no_line(bool first, bool good, const char *name) {
ae54abe7 1713 printf("%s%s %s\n",
607ebf2b 1714 first ? " Features: " : " ",
ae54abe7 1715 COLOR_MARK_BOOL(good),
607ebf2b
ZJS
1716 name);
1717}
1718
957b2423 1719static int are_we_installed(const char *esp_path) {
d9f048b5
ZJS
1720 int r;
1721
d9f048b5
ZJS
1722 /* Tests whether systemd-boot is installed. It's not obvious what to use as check here: we could
1723 * check EFI variables, we could check what binary /EFI/BOOT/BOOT*.EFI points to, or whether the
1724 * loader entries directory exists. Here we opted to check whether /EFI/systemd/ is non-empty, which
1725 * should be a suitable and very minimal check for a number of reasons:
1726 *
1727 * → The check is architecture independent (i.e. we check if any systemd-boot loader is installed,
1728 * not a specific one.)
1729 *
1730 * → It doesn't assume we are the only boot loader (i.e doesn't check if we own the main
1731 * /EFI/BOOT/BOOT*.EFI fallback binary.
1732 *
1733 * → It specifically checks for systemd-boot, not for other boot loaders (which a check for
1734 * /boot/loader/entries would do). */
1735
957b2423 1736 _cleanup_free_ char *p = path_join(esp_path, "/EFI/systemd/");
d9f048b5
ZJS
1737 if (!p)
1738 return log_oom();
1739
28e5e1e9 1740 log_debug("Checking whether %s contains any files%s", p, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
db55bbf2 1741 r = dir_is_empty(p, /* ignore_hidden_or_backup= */ false);
d9f048b5
ZJS
1742 if (r < 0 && r != -ENOENT)
1743 return log_error_errno(r, "Failed to check whether %s contains any files: %m", p);
1744
1745 return r == 0;
1746}
1747
2f2c539c 1748static int verb_status(int argc, char *argv[], void *userdata) {
fbf45d22 1749 sd_id128_t esp_uuid = SD_ID128_NULL, xbootldr_uuid = SD_ID128_NULL;
f63b5ad9 1750 dev_t esp_devid = 0, xbootldr_devid = 0;
46fb255b 1751 int r, k;
0974a682 1752
f63b5ad9 1753 r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, &esp_uuid, &esp_devid);
fbf45d22 1754 if (arg_print_esp_path) {
5caa3167
LP
1755 if (r == -EACCES) /* If we couldn't acquire the ESP path, log about access errors (which is the only
1756 * error the find_esp_and_warn() won't log on its own) */
fbf45d22 1757 return log_error_errno(r, "Failed to determine ESP location: %m");
30b50477
ZJS
1758 if (r < 0)
1759 return r;
1760
fbf45d22
LP
1761 puts(arg_esp_path);
1762 }
1763
f63b5ad9 1764 r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, &xbootldr_uuid, &xbootldr_devid);
fbf45d22
LP
1765 if (arg_print_dollar_boot_path) {
1766 if (r == -EACCES)
8214758b 1767 return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
fbf45d22
LP
1768 if (r < 0)
1769 return r;
1770
f8a2b09a
YW
1771 const char *path = arg_dollar_boot_path();
1772 if (!path)
1773 return log_error_errno(SYNTHETIC_ERRNO(EACCES), "Failed to determine XBOOTLDR location: %m");
1774
1775 puts(path);
30b50477 1776 }
551710cf 1777
fbf45d22
LP
1778 if (arg_print_esp_path || arg_print_dollar_boot_path)
1779 return 0;
1780
957b2423
ZJS
1781 r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just
1782 * show what we can show */
5caa3167 1783
384c2c32 1784 pager_open(arg_pager_flags);
57db6f18 1785
80a2381d 1786 if (!arg_root && is_efi_boot()) {
80641a81
LP
1787 static const struct {
1788 uint64_t flag;
1789 const char *name;
6977ea99 1790 } loader_flags[] = {
22c5ff51
LP
1791 { EFI_LOADER_FEATURE_BOOT_COUNTING, "Boot counting" },
1792 { EFI_LOADER_FEATURE_CONFIG_TIMEOUT, "Menu timeout control" },
1793 { EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT, "One-shot menu timeout control" },
1794 { EFI_LOADER_FEATURE_ENTRY_DEFAULT, "Default entry control" },
1795 { EFI_LOADER_FEATURE_ENTRY_ONESHOT, "One-shot entry control" },
1796 { EFI_LOADER_FEATURE_XBOOTLDR, "Support for XBOOTLDR partition" },
1797 { EFI_LOADER_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" },
2553a548 1798 { EFI_LOADER_FEATURE_LOAD_DRIVER, "Load drop-in drivers" },
a67f22c4
LP
1799 { EFI_LOADER_FEATURE_SORT_KEY, "Support Type #1 sort-key field" },
1800 { EFI_LOADER_FEATURE_SAVED_ENTRY, "Support @saved pseudo-entry" },
1801 { EFI_LOADER_FEATURE_DEVICETREE, "Support Type #1 devicetree field" },
80641a81 1802 };
6977ea99
LP
1803 static const struct {
1804 uint64_t flag;
1805 const char *name;
1806 } stub_flags[] = {
1807 { EFI_STUB_FEATURE_REPORT_BOOT_PARTITION, "Stub sets ESP information" },
1808 { EFI_STUB_FEATURE_PICK_UP_CREDENTIALS, "Picks up credentials from boot partition" },
1809 { EFI_STUB_FEATURE_PICK_UP_SYSEXTS, "Picks up system extension images from boot partition" },
1810 { EFI_STUB_FEATURE_THREE_PCRS, "Measures kernel+command line+sysexts" },
1811 };
81375b9b 1812 _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
25579a43 1813 sd_id128_t loader_part_uuid = SD_ID128_NULL;
6977ea99 1814 uint64_t loader_features = 0, stub_features = 0;
0ea911d1 1815 Tpm2Support s;
e6f055cb 1816 int have;
0974a682 1817
e6f055cb
ZJS
1818 read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareType), &fw_type);
1819 read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareInfo), &fw_info);
1820 read_efi_var(EFI_LOADER_VARIABLE(LoaderInfo), &loader);
1821 read_efi_var(EFI_LOADER_VARIABLE(StubInfo), &stub);
1822 read_efi_var(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &loader_path);
80641a81 1823 (void) efi_loader_get_features(&loader_features);
6977ea99 1824 (void) efi_stub_get_features(&stub_features);
551710cf 1825
2f2c539c
LP
1826 if (loader_path)
1827 efi_tilt_backslashes(loader_path);
1828
46fb255b
ZJS
1829 k = efi_loader_get_device_part_uuid(&loader_part_uuid);
1830 if (k < 0 && k != -ENOENT)
1831 r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m");
2f2c539c 1832
c4964512 1833 SecureBootMode secure = efi_get_secure_boot_mode();
cb180369 1834 printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
6e916539
JJ
1835 printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal());
1836 printf(" Firmware Arch: %s\n", get_efi_arch());
1837 printf(" Secure Boot: %sd (%s)\n",
c4964512
JJ
1838 enable_disable(IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED)),
1839 secure_boot_mode_to_string(secure));
0ea911d1
LP
1840
1841 s = tpm2_support();
6e916539 1842 printf(" TPM2 Support: %s%s%s\n",
0ea911d1
LP
1843 FLAGS_SET(s, TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER) ? ansi_highlight_green() :
1844 (s & (TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER)) != 0 ? ansi_highlight_red() : ansi_highlight_yellow(),
1845 FLAGS_SET(s, TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER) ? "yes" :
1846 (s & TPM2_SUPPORT_FIRMWARE) ? "firmware only, driver unavailable" :
1847 (s & TPM2_SUPPORT_DRIVER) ? "driver only, firmware unavailable" : "no",
1848 ansi_normal());
8a96369e 1849
c1b9708c
LP
1850 k = efi_get_reboot_to_firmware();
1851 if (k > 0)
6e916539 1852 printf(" Boot into FW: %sactive%s\n", ansi_highlight_yellow(), ansi_normal());
c1b9708c 1853 else if (k == 0)
6e916539 1854 printf(" Boot into FW: supported\n");
c1b9708c 1855 else if (k == -EOPNOTSUPP)
6e916539 1856 printf(" Boot into FW: not supported\n");
8a96369e 1857 else {
c1b9708c 1858 errno = -k;
6e916539 1859 printf(" Boot into FW: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
8a96369e 1860 }
2f2c539c
LP
1861 printf("\n");
1862
cb180369 1863 printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
ba857253 1864 printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal());
80641a81 1865
6977ea99
LP
1866 for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++)
1867 print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name);
80641a81 1868
607ebf2b
ZJS
1869 sd_id128_t bootloader_esp_uuid;
1870 bool have_bootloader_esp_uuid = efi_loader_get_device_part_uuid(&bootloader_esp_uuid) >= 0;
80641a81 1871
250db1bf 1872 print_yes_no_line(false, have_bootloader_esp_uuid, "Boot loader sets ESP information");
22e54dd6
ZJS
1873 if (have_bootloader_esp_uuid && !sd_id128_is_null(esp_uuid) &&
1874 !sd_id128_equal(esp_uuid, bootloader_esp_uuid))
1875 printf("WARNING: The boot loader reports a different ESP UUID than detected ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR")!\n",
1876 SD_ID128_FORMAT_VAL(bootloader_esp_uuid),
1877 SD_ID128_FORMAT_VAL(esp_uuid));
80641a81 1878
6977ea99 1879 if (stub) {
81375b9b 1880 printf(" Stub: %s\n", stub);
6977ea99
LP
1881 for (size_t i = 0; i < ELEMENTSOF(stub_flags); i++)
1882 print_yes_no_line(i == 0, FLAGS_SET(stub_features, stub_flags[i].flag), stub_flags[i].name);
1883 }
e28973ee 1884 if (!sd_id128_is_null(loader_part_uuid))
bd44566c 1885 printf(" ESP: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
2f2c539c
LP
1886 SD_ID128_FORMAT_VAL(loader_part_uuid));
1887 else
cd2d4c7f 1888 printf(" ESP: n/a\n");
9a6f746f 1889 printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
2f2c539c 1890 printf("\n");
d6e9a347 1891
cb180369 1892 printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
e6f055cb
ZJS
1893 have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), F_OK) >= 0;
1894 printf(" System Token: %s\n", have ? "set" : "not set");
d6e9a347
LP
1895
1896 if (arg_esp_path) {
1897 _cleanup_free_ char *p = NULL;
1898
1899 p = path_join(arg_esp_path, "/loader/random-seed");
1900 if (!p)
1901 return log_oom();
1902
e6f055cb
ZJS
1903 have = access(p, F_OK) >= 0;
1904 printf(" Exists: %s\n", yes_no(have));
d6e9a347
LP
1905 }
1906
1907 printf("\n");
2f2c539c 1908 } else
cb180369
LP
1909 printf("%sSystem:%s\n"
1910 "Not booted with EFI\n\n",
1911 ansi_underline(), ansi_normal());
7b4d7cc0 1912
fbf45d22
LP
1913 if (arg_esp_path) {
1914 k = status_binaries(arg_esp_path, esp_uuid);
ecec2a5d
LP
1915 if (k < 0)
1916 r = k;
1917 }
2f2c539c 1918
80a2381d 1919 if (!arg_root && is_efi_boot()) {
46fb255b
ZJS
1920 k = status_variables();
1921 if (k < 0)
1922 r = k;
cd2d4c7f 1923 }
0974a682 1924
fbf45d22 1925 if (arg_esp_path || arg_xbootldr_path) {
8214758b
ZJS
1926 _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
1927
1928 k = boot_config_load_and_select(&config,
1929 arg_esp_path, esp_devid,
1930 arg_xbootldr_path, xbootldr_devid);
ecec2a5d
LP
1931 if (k < 0)
1932 r = k;
8214758b
ZJS
1933 else {
1934 k = status_entries(&config,
1935 arg_esp_path, esp_uuid,
1936 arg_xbootldr_path, xbootldr_uuid);
1937 if (k < 0)
1938 r = k;
1939 }
ecec2a5d 1940 }
7e87c7d9 1941
46fb255b 1942 return r;
2f2c539c 1943}
7b4d7cc0 1944
7e87c7d9 1945static int verb_list(int argc, char *argv[], void *userdata) {
f7a7a5e2 1946 _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL;
f63b5ad9 1947 dev_t esp_devid = 0, xbootldr_devid = 0;
5caa3167 1948 int r;
7e87c7d9 1949
8214758b
ZJS
1950 /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two
1951 * things: turn off logging about access errors and turn off potentially privileged device probing.
1952 * Here we're interested in the latter but not the former, hence request the mode, and log about
1953 * EACCES. */
7e87c7d9 1954
f63b5ad9 1955 r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, NULL, &esp_devid);
5caa3167 1956 if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */
8214758b 1957 return log_error_errno(r, "Failed to determine ESP location: %m");
7e87c7d9
ZJS
1958 if (r < 0)
1959 return r;
1960
f63b5ad9 1961 r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, NULL, &xbootldr_devid);
fbf45d22
LP
1962 if (r == -EACCES)
1963 return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m");
1964 if (r < 0)
1965 return r;
1966
8214758b 1967 r = boot_config_load_and_select(&config, arg_esp_path, esp_devid, arg_xbootldr_path, xbootldr_devid);
f7a7a5e2
LP
1968 if (r < 0)
1969 return r;
1970
432ce537 1971 if (config.n_entries == 0 && FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
20a28174 1972 log_info("No boot loader entries found.");
432ce537 1973 return 0;
cd2d4c7f 1974 }
0974a682 1975
432ce537
ZJS
1976 pager_open(arg_pager_flags);
1977 return show_boot_entries(&config, arg_json_format_flags);
2f2c539c 1978}
7b4d7cc0 1979
e44c3229
LP
1980static int install_random_seed(const char *esp) {
1981 _cleanup_(unlink_and_freep) char *tmp = NULL;
3daeef08 1982 uint8_t buffer[RANDOM_EFI_SEED_SIZE];
e44c3229
LP
1983 _cleanup_free_ char *path = NULL;
1984 _cleanup_close_ int fd = -1;
0be72218 1985 size_t token_size;
e44c3229
LP
1986 ssize_t n;
1987 int r;
1988
1989 assert(esp);
1990
1991 path = path_join(esp, "/loader/random-seed");
1992 if (!path)
1993 return log_oom();
1994
0be72218 1995 r = crypto_random_bytes(buffer, sizeof(buffer));
e44c3229 1996 if (r < 0)
a4d20801 1997 return log_error_errno(r, "Failed to acquire random seed: %m");
e44c3229 1998
a4a55e9a
LP
1999 /* Normally create_subdirs() should already have created everything we need, but in case "bootctl
2000 * random-seed" is called we want to just create the minimum we need for it, and not the full
2001 * list. */
2002 r = mkdir_parents(path, 0755);
2003 if (r < 0)
2004 return log_error_errno(r, "Failed to create parent directory for %s: %m", path);
2005
e44c3229
LP
2006 r = tempfn_random(path, "bootctl", &tmp);
2007 if (r < 0)
2008 return log_oom();
2009
2010 fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|O_WRONLY|O_CLOEXEC, 0600);
2011 if (fd < 0) {
2012 tmp = mfree(tmp);
2013 return log_error_errno(fd, "Failed to open random seed file for writing: %m");
2014 }
2015
0be72218 2016 n = write(fd, buffer, sizeof(buffer));
e44c3229
LP
2017 if (n < 0)
2018 return log_error_errno(errno, "Failed to write random seed file: %m");
0be72218 2019 if ((size_t) n != sizeof(buffer))
e44c3229
LP
2020 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while writing random seed file.");
2021
2022 if (rename(tmp, path) < 0)
2023 return log_error_errno(r, "Failed to move random seed file into place: %m");
2024
2025 tmp = mfree(tmp);
2026
0be72218 2027 log_info("Random seed file %s successfully written (%zu bytes).", path, sizeof(buffer));
e44c3229
LP
2028
2029 if (!arg_touch_variables)
2030 return 0;
2031
2032 if (!is_efi_boot()) {
2033 log_notice("Not booted with EFI, skipping EFI variable setup.");
2034 return 0;
2035 }
2036
80a2381d
LB
2037 if (arg_root) {
2038 log_warning("Acting on %s, skipping EFI variable setup.",
2039 arg_image ? "image" : "root directory");
2040 return 0;
2041 }
2042
e44c3229
LP
2043 r = getenv_bool("SYSTEMD_WRITE_SYSTEM_TOKEN");
2044 if (r < 0) {
2045 if (r != -ENXIO)
2046 log_warning_errno(r, "Failed to parse $SYSTEMD_WRITE_SYSTEM_TOKEN, ignoring.");
2047
2048 if (detect_vm() > 0) {
2049 /* Let's not write a system token if we detect we are running in a VM
2050 * environment. Why? Our default security model for the random seed uses the system
2051 * token as a mechanism to ensure we are not vulnerable to golden master sloppiness
2052 * issues, i.e. that people initialize the random seed file, then copy the image to
2053 * many systems and end up with the same random seed in each that is assumed to be
2054 * valid but in reality is the same for all machines. By storing a system token in
2055 * the EFI variable space we can make sure that even though the random seeds on disk
2056 * are all the same they will be different on each system under the assumption that
2057 * the EFI variable space is maintained separate from the random seed storage. That
162392b7 2058 * is generally the case on physical systems, as the ESP is stored on persistent
e44c3229
LP
2059 * storage, and the EFI variables in NVRAM. However in virtualized environments this
2060 * is generally not true: the EFI variable set is typically stored along with the
2061 * disk image itself. For example, using the OVMF EFI firmware the EFI variables are
2062 * stored in a file in the ESP itself. */
2063
2064 log_notice("Not installing system token, since we are running in a virtualized environment.");
2065 return 0;
2066 }
2067 } else if (r == 0) {
2068 log_notice("Not writing system token, because $SYSTEMD_WRITE_SYSTEM_TOKEN is set to false.");
2069 return 0;
2070 }
2071
e6f055cb 2072 r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), NULL, NULL, &token_size);
ad0b610b
LP
2073 if (r == -ENODATA)
2074 log_debug_errno(r, "LoaderSystemToken EFI variable is invalid (too short?), replacing.");
2075 else if (r < 0) {
e44c3229
LP
2076 if (r != -ENOENT)
2077 return log_error_errno(r, "Failed to test system token validity: %m");
2078 } else {
0be72218 2079 if (token_size >= sizeof(buffer)) {
e44c3229
LP
2080 /* Let's avoid writes if we can, and initialize this only once. */
2081 log_debug("System token already written, not updating.");
2082 return 0;
2083 }
2084
0be72218 2085 log_debug("Existing system token size (%zu) does not match our expectations (%zu), replacing.", token_size, sizeof(buffer));
e44c3229
LP
2086 }
2087
0be72218 2088 r = crypto_random_bytes(buffer, sizeof(buffer));
e44c3229
LP
2089 if (r < 0)
2090 return log_error_errno(r, "Failed to acquire random seed: %m");
2091
2092 /* Let's write this variable with an umask in effect, so that unprivileged users can't see the token
2093 * and possibly get identification information or too much insight into the kernel's entropy pool
2094 * state. */
2095 RUN_WITH_UMASK(0077) {
0be72218 2096 r = efi_set_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), buffer, sizeof(buffer));
351de38e
LP
2097 if (r < 0) {
2098 if (!arg_graceful)
2099 return log_error_errno(r, "Failed to write 'LoaderSystemToken' EFI variable: %m");
2100
2101 if (r == -EINVAL)
2102 log_warning_errno(r, "Unable to write 'LoaderSystemToken' EFI variable (firmware problem?), ignoring: %m");
2103 else
2104 log_warning_errno(r, "Unable to write 'LoaderSystemToken' EFI variable, ignoring: %m");
2105 } else
0be72218 2106 log_info("Successfully initialized system token in EFI variable with %zu bytes.", sizeof(buffer));
e44c3229
LP
2107 }
2108
e44c3229
LP
2109 return 0;
2110}
2111
fbf45d22
LP
2112static int sync_everything(void) {
2113 int ret = 0, k;
e0e8d177 2114
fbf45d22
LP
2115 if (arg_esp_path) {
2116 k = syncfs_path(AT_FDCWD, arg_esp_path);
2117 if (k < 0)
2118 ret = log_error_errno(k, "Failed to synchronize the ESP '%s': %m", arg_esp_path);
2119 }
e0e8d177 2120
fbf45d22
LP
2121 if (arg_xbootldr_path) {
2122 k = syncfs_path(AT_FDCWD, arg_xbootldr_path);
2123 if (k < 0)
2124 ret = log_error_errno(k, "Failed to synchronize $BOOT '%s': %m", arg_xbootldr_path);
2125 }
e0e8d177 2126
fbf45d22 2127 return ret;
e0e8d177
LP
2128}
2129
2f2c539c 2130static int verb_install(int argc, char *argv[], void *userdata) {
2f2c539c
LP
2131 sd_id128_t uuid = SD_ID128_NULL;
2132 uint64_t pstart = 0, psize = 0;
2133 uint32_t part = 0;
e5a8b4b5 2134 bool install, graceful;
2f2c539c
LP
2135 int r;
2136
8d3e0d60
LP
2137 /* Invoked for both "update" and "install" */
2138
e5a8b4b5
LP
2139 install = streq(argv[0], "install");
2140 graceful = !install && arg_graceful; /* support graceful mode for updates */
2141
f63b5ad9 2142 r = acquire_esp(/* unprivileged_mode= */ false, graceful, &part, &pstart, &psize, &uuid, NULL);
e5a8b4b5
LP
2143 if (graceful && r == -ENOKEY)
2144 return 0; /* If --graceful is specified and we can't find an ESP, handle this cleanly */
2f2c539c
LP
2145 if (r < 0)
2146 return r;
2147
49927ad8
ZJS
2148 if (!install) {
2149 /* If we are updating, don't do anything if sd-boot wasn't actually installed. */
957b2423 2150 r = are_we_installed(arg_esp_path);
49927ad8
ZJS
2151 if (r < 0)
2152 return r;
2153 if (r == 0) {
2154 log_debug("Skipping update because sd-boot is not installed in the ESP.");
2155 return 0;
2156 }
2157 }
2158
f63b5ad9 2159 r = acquire_xbootldr(/* unprivileged_mode= */ false, NULL, NULL);
fbf45d22
LP
2160 if (r < 0)
2161 return r;
2162
f337f903 2163 r = settle_make_entry_directory();
47fb161e
ZJS
2164 if (r < 0)
2165 return r;
6a3fff75 2166
6e916539
JJ
2167 const char *arch = arg_arch_all ? "" : get_efi_arch();
2168
2f2c539c 2169 RUN_WITH_UMASK(0002) {
fbf45d22
LP
2170 if (install) {
2171 /* Don't create any of these directories when we are just updating. When we update
2172 * we'll drop-in our files (unless there are newer ones already), but we won't create
2173 * the directories for them in the first place. */
e44c3229
LP
2174 r = create_subdirs(arg_esp_path, esp_subdirs);
2175 if (r < 0)
2176 return r;
2177
2178 r = create_subdirs(arg_dollar_boot_path(), dollar_boot_subdirs);
fbf45d22
LP
2179 if (r < 0)
2180 return r;
2181 }
2182
6e916539 2183 r = install_binaries(arg_esp_path, arch, install);
0974a682 2184 if (r < 0)
d3226d77 2185 return r;
0974a682 2186
2f2c539c 2187 if (install) {
31e57550 2188 r = install_loader_config(arg_esp_path);
e44c3229
LP
2189 if (r < 0)
2190 return r;
2191
f337f903 2192 r = install_entry_directory(arg_dollar_boot_path());
6a3fff75 2193 if (r < 0)
2194 return r;
2195
f337f903 2196 r = install_entry_token();
47fb161e
ZJS
2197 if (r < 0)
2198 return r;
2199
e44c3229 2200 r = install_random_seed(arg_esp_path);
d3226d77
ZJS
2201 if (r < 0)
2202 return r;
2203 }
80889bd9
LP
2204
2205 r = install_loader_specification(arg_dollar_boot_path());
2206 if (r < 0)
2207 return r;
2f2c539c 2208 }
0974a682 2209
fbf45d22 2210 (void) sync_everything();
e0e8d177 2211
6e916539
JJ
2212 if (!arg_touch_variables)
2213 return 0;
7b4d7cc0 2214
6e916539
JJ
2215 if (arg_arch_all) {
2216 log_info("Not changing EFI variables with --all-architectures.");
2217 return 0;
2218 }
2219
2220 char *path = strjoina("/EFI/systemd/systemd-boot", arch, ".efi");
2221 return install_variables(arg_esp_path, part, pstart, psize, uuid, path, install);
2f2c539c 2222}
0974a682 2223
2f2c539c 2224static int verb_remove(int argc, char *argv[], void *userdata) {
31e57550 2225 sd_id128_t uuid = SD_ID128_NULL;
fbf45d22 2226 int r, q;
0974a682 2227
f63b5ad9 2228 r = acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, &uuid, NULL);
2f2c539c
LP
2229 if (r < 0)
2230 return r;
2231
f63b5ad9 2232 r = acquire_xbootldr(/* unprivileged_mode= */ false, NULL, NULL);
fbf45d22
LP
2233 if (r < 0)
2234 return r;
2f2c539c 2235
f337f903 2236 r = settle_make_entry_directory();
47fb161e
ZJS
2237 if (r < 0)
2238 return r;
6a3fff75 2239
fbf45d22 2240 r = remove_binaries(arg_esp_path);
e0e8d177 2241
e44c3229 2242 q = remove_file(arg_esp_path, "/loader/loader.conf");
fbf45d22
LP
2243 if (q < 0 && r >= 0)
2244 r = q;
2245
e44c3229 2246 q = remove_file(arg_esp_path, "/loader/random-seed");
fbf45d22
LP
2247 if (q < 0 && r >= 0)
2248 r = q;
2f2c539c 2249
80889bd9
LP
2250 q = remove_file(arg_esp_path, "/loader/entries.srel");
2251 if (q < 0 && r >= 0)
2252 r = q;
2253
e44c3229 2254 q = remove_subdirs(arg_esp_path, esp_subdirs);
fbf45d22
LP
2255 if (q < 0 && r >= 0)
2256 r = q;
2257
e44c3229
LP
2258 q = remove_subdirs(arg_esp_path, dollar_boot_subdirs);
2259 if (q < 0 && r >= 0)
2260 r = q;
fbf45d22 2261
f337f903 2262 q = remove_entry_directory(arg_esp_path);
6a3fff75 2263 if (q < 0 && r >= 0)
1c2b6177 2264 r = q;
6a3fff75 2265
e44c3229 2266 if (arg_xbootldr_path) {
80889bd9
LP
2267 /* Remove a subset of these also from the XBOOTLDR partition if it exists */
2268
2269 q = remove_file(arg_xbootldr_path, "/loader/entries.srel");
2270 if (q < 0 && r >= 0)
2271 r = q;
2272
e44c3229
LP
2273 q = remove_subdirs(arg_xbootldr_path, dollar_boot_subdirs);
2274 if (q < 0 && r >= 0)
2275 r = q;
6a3fff75 2276
f337f903 2277 q = remove_entry_directory(arg_xbootldr_path);
6a3fff75 2278 if (q < 0 && r >= 0)
2279 r = q;
7b4d7cc0
KS
2280 }
2281
e44c3229
LP
2282 (void) sync_everything();
2283
2284 if (!arg_touch_variables)
2285 return r;
2286
6e916539
JJ
2287 if (arg_arch_all) {
2288 log_info("Not changing EFI variables with --all-architectures.");
2289 return r;
2290 }
2291
2292 char *path = strjoina("/EFI/systemd/systemd-boot", get_efi_arch(), ".efi");
2293 q = remove_variables(uuid, path, true);
e44c3229
LP
2294 if (q < 0 && r >= 0)
2295 r = q;
2296
2297 q = remove_loader_variables();
2298 if (q < 0 && r >= 0)
2299 r = q;
2300
d3226d77 2301 return r;
7b4d7cc0
KS
2302}
2303
a2aa605d 2304static int verb_is_installed(int argc, char *argv[], void *userdata) {
a2aa605d
LP
2305 int r;
2306
18eb56c3
ZJS
2307 r = acquire_esp(/* privileged_mode= */ false,
2308 /* graceful= */ arg_graceful,
2309 NULL, NULL, NULL, NULL, NULL);
957b2423
ZJS
2310 if (r < 0)
2311 return r;
2312
2313 r = are_we_installed(arg_esp_path);
a2aa605d
LP
2314 if (r < 0)
2315 return r;
2316
d9f048b5 2317 if (r > 0) {
14e6e444
ZJS
2318 if (!arg_quiet)
2319 puts("yes");
d9f048b5
ZJS
2320 return EXIT_SUCCESS;
2321 } else {
14e6e444
ZJS
2322 if (!arg_quiet)
2323 puts("no");
a2aa605d
LP
2324 return EXIT_FAILURE;
2325 }
a2aa605d
LP
2326}
2327
39ddc32a
JJ
2328static int parse_timeout(const char *arg1, char16_t **ret_timeout, size_t *ret_timeout_size) {
2329 char utf8[DECIMAL_STR_MAX(usec_t)];
2330 char16_t *encoded;
2331 usec_t timeout;
2332 int r;
2333
2334 assert(arg1);
2335 assert(ret_timeout);
2336 assert(ret_timeout_size);
2337
2338 if (streq(arg1, "menu-force"))
2339 timeout = USEC_INFINITY;
2340 else if (streq(arg1, "menu-hidden"))
2341 timeout = 0;
2342 else {
2343 r = parse_time(arg1, &timeout, USEC_PER_SEC);
2344 if (r < 0)
2345 return log_error_errno(r, "Failed to parse timeout '%s': %m", arg1);
2346 if (timeout != USEC_INFINITY && timeout > UINT32_MAX * USEC_PER_SEC)
2347 log_warning("Timeout is too long and will be treated as 'menu-force' instead.");
2348 }
2349
2350 xsprintf(utf8, USEC_FMT, MIN(timeout / USEC_PER_SEC, UINT32_MAX));
6663b509 2351
39ddc32a
JJ
2352 encoded = utf8_to_utf16(utf8, strlen(utf8));
2353 if (!encoded)
2354 return log_oom();
6663b509 2355
39ddc32a
JJ
2356 *ret_timeout = encoded;
2357 *ret_timeout_size = char16_strlen(encoded) * 2 + 2;
2358 return 0;
2359}
2360
c4b84347 2361static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target, size_t *ret_target_size) {
6663b509 2362 char16_t *encoded = NULL;
c4b84347 2363 int r;
6663b509
LP
2364
2365 assert(arg1);
2366 assert(ret_target);
2367 assert(ret_target_size);
2368
c4b84347 2369 if (streq(arg1, "@current")) {
e6f055cb 2370 r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntrySelected), NULL, (void *) ret_target, ret_target_size);
c4b84347
ДГ
2371 if (r < 0)
2372 return log_error_errno(r, "Failed to get EFI variable 'LoaderEntrySelected': %m");
6663b509 2373
c4b84347 2374 } else if (streq(arg1, "@oneshot")) {
e6f055cb 2375 r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryOneShot), NULL, (void *) ret_target, ret_target_size);
c4b84347
ДГ
2376 if (r < 0)
2377 return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryOneShot': %m");
6663b509 2378
c4b84347 2379 } else if (streq(arg1, "@default")) {
e6f055cb 2380 r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryDefault), NULL, (void *) ret_target, ret_target_size);
c4b84347
ДГ
2381 if (r < 0)
2382 return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryDefault': %m");
6663b509 2383
ee4fd9cb 2384 } else if (arg1[0] == '@' && !streq(arg1, "@saved"))
6663b509
LP
2385 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported special entry identifier: %s", arg1);
2386 else {
c4b84347
ДГ
2387 encoded = utf8_to_utf16(arg1, strlen(arg1));
2388 if (!encoded)
2389 return log_oom();
6663b509 2390
c4b84347
ДГ
2391 *ret_target = encoded;
2392 *ret_target_size = char16_strlen(encoded) * 2 + 2;
2393 }
6663b509 2394
c4b84347
ДГ
2395 return 0;
2396}
2397
39ddc32a 2398static int verb_set_efivar(int argc, char *argv[], void *userdata) {
d88c96ff
LP
2399 int r;
2400
80a2381d
LB
2401 if (arg_root)
2402 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
2403 "Acting on %s, skipping EFI variable setup.",
2404 arg_image ? "image" : "root directory");
2405
baaa35ad
ZJS
2406 if (!is_efi_boot())
2407 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
2408 "Not booted with UEFI.");
d88c96ff 2409
e6f055cb 2410 if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderInfo)), F_OK) < 0) {
d88c96ff
LP
2411 if (errno == ENOENT) {
2412 log_error_errno(errno, "Not booted with a supported boot loader.");
2413 return -EOPNOTSUPP;
2414 }
2415
2416 return log_error_errno(errno, "Failed to detect whether boot loader supports '%s' operation: %m", argv[0]);
2417 }
2418
baaa35ad
ZJS
2419 if (detect_container() > 0)
2420 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
2421 "'%s' operation not supported in a container.",
2422 argv[0]);
d88c96ff 2423
baaa35ad
ZJS
2424 if (!arg_touch_variables)
2425 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
39ddc32a 2426 "'%s' operation cannot be combined with --no-variables.",
baaa35ad 2427 argv[0]);
d88c96ff 2428
39ddc32a
JJ
2429 const char *variable;
2430 int (* arg_parser)(const char *, char16_t **, size_t *);
2431
2432 if (streq(argv[0], "set-default")) {
2433 variable = EFI_LOADER_VARIABLE(LoaderEntryDefault);
2434 arg_parser = parse_loader_entry_target_arg;
2435 } else if (streq(argv[0], "set-oneshot")) {
2436 variable = EFI_LOADER_VARIABLE(LoaderEntryOneShot);
2437 arg_parser = parse_loader_entry_target_arg;
2438 } else if (streq(argv[0], "set-timeout")) {
2439 variable = EFI_LOADER_VARIABLE(LoaderConfigTimeout);
2440 arg_parser = parse_timeout;
2441 } else if (streq(argv[0], "set-timeout-oneshot")) {
2442 variable = EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot);
2443 arg_parser = parse_timeout;
2444 } else
2445 assert_not_reached();
d88c96ff
LP
2446
2447 if (isempty(argv[1])) {
e6f055cb 2448 r = efi_set_variable(variable, NULL, 0);
d88c96ff 2449 if (r < 0 && r != -ENOENT)
e6f055cb 2450 return log_error_errno(r, "Failed to remove EFI variable '%s': %m", variable);
d88c96ff 2451 } else {
39ddc32a
JJ
2452 _cleanup_free_ char16_t *value = NULL;
2453 size_t value_size = 0;
d88c96ff 2454
39ddc32a 2455 r = arg_parser(argv[1], &value, &value_size);
c4b84347
ДГ
2456 if (r < 0)
2457 return r;
39ddc32a 2458 r = efi_set_variable(variable, value, value_size);
d88c96ff 2459 if (r < 0)
e6f055cb 2460 return log_error_errno(r, "Failed to update EFI variable '%s': %m", variable);
d88c96ff
LP
2461 }
2462
2463 return 0;
2464}
2465
e44c3229
LP
2466static int verb_random_seed(int argc, char *argv[], void *userdata) {
2467 int r;
2468
80a2381d 2469 r = find_esp_and_warn(arg_root, arg_esp_path, false, &arg_esp_path, NULL, NULL, NULL, NULL, NULL);
351de38e
LP
2470 if (r == -ENOKEY) {
2471 /* find_esp_and_warn() doesn't warn about ENOKEY, so let's do that on our own */
2472 if (!arg_graceful)
2473 return log_error_errno(r, "Unable to find ESP.");
2474
2475 log_notice("No ESP found, not initializing random seed.");
2476 return 0;
2477 }
e44c3229
LP
2478 if (r < 0)
2479 return r;
2480
2481 r = install_random_seed(arg_esp_path);
2482 if (r < 0)
2483 return r;
2484
2485 (void) sync_everything();
2486 return 0;
2487}
2488
2536752d 2489static int verb_systemd_efi_options(int argc, char *argv[], void *userdata) {
4e5aa791
ZJS
2490 int r;
2491
2492 if (argc == 1) {
ad2d6880 2493 _cleanup_free_ char *line = NULL, *new = NULL;
4e5aa791 2494
2536752d 2495 r = systemd_efi_options_variable(&line);
ad2d6880
ZJS
2496 if (r == -ENODATA)
2497 log_debug("No SystemdOptions EFI variable present in cache.");
2498 else if (r < 0)
2499 return log_error_errno(r, "Failed to read SystemdOptions EFI variable from cache: %m");
2500 else
2501 puts(line);
2502
2503 r = systemd_efi_options_efivarfs_if_newer(&new);
2504 if (r == -ENODATA) {
2505 if (line)
2506 log_notice("Note: SystemdOptions EFI variable has been removed since boot.");
2507 } else if (r < 0)
2508 log_warning_errno(r, "Failed to check SystemdOptions EFI variable in efivarfs, ignoring: %m");
2509 else if (new && !streq_ptr(line, new))
2510 log_notice("Note: SystemdOptions EFI variable has been modified since boot. New value: %s",
2511 new);
4e5aa791 2512 } else {
e6f055cb 2513 r = efi_set_variable_string(EFI_SYSTEMD_VARIABLE(SystemdOptions), argv[1]);
4e5aa791
ZJS
2514 if (r < 0)
2515 return log_error_errno(r, "Failed to set SystemdOptions EFI variable: %m");
2516 }
2517
2518 return 0;
2519}
2520
002914e6
LP
2521static int verb_reboot_to_firmware(int argc, char *argv[], void *userdata) {
2522 int r;
2523
2524 if (argc < 2) {
2525 r = efi_get_reboot_to_firmware();
2526 if (r > 0) {
2527 puts("active");
2528 return EXIT_SUCCESS; /* success */
2529 }
2530 if (r == 0) {
2531 puts("supported");
2532 return 1; /* recognizable error #1 */
2533 }
2534 if (r == -EOPNOTSUPP) {
2535 puts("not supported");
2536 return 2; /* recognizable error #2 */
2537 }
2538
2539 log_error_errno(r, "Failed to query reboot-to-firmware state: %m");
2540 return 3; /* other kind of error */
2541 } else {
2542 r = parse_boolean(argv[1]);
2543 if (r < 0)
2544 return log_error_errno(r, "Failed to parse argument: %s", argv[1]);
2545
2546 r = efi_set_reboot_to_firmware(r);
2547 if (r < 0)
2548 return log_error_errno(r, "Failed to set reboot-to-firmware option: %m");
2549
2550 return 0;
2551 }
2552}
2553
2f2c539c 2554static int bootctl_main(int argc, char *argv[]) {
2f2c539c 2555 static const Verb verbs[] = {
2536752d
ZJS
2556 { "help", VERB_ANY, VERB_ANY, 0, help },
2557 { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
2558 { "install", VERB_ANY, 1, 0, verb_install },
2559 { "update", VERB_ANY, 1, 0, verb_install },
2560 { "remove", VERB_ANY, 1, 0, verb_remove },
2561 { "is-installed", VERB_ANY, 1, 0, verb_is_installed },
2562 { "list", VERB_ANY, 1, 0, verb_list },
39ddc32a
JJ
2563 { "set-default", 2, 2, 0, verb_set_efivar },
2564 { "set-oneshot", 2, 2, 0, verb_set_efivar },
2565 { "set-timeout", 2, 2, 0, verb_set_efivar },
2566 { "set-timeout-oneshot", 2, 2, 0, verb_set_efivar },
2536752d
ZJS
2567 { "random-seed", VERB_ANY, 1, 0, verb_random_seed },
2568 { "systemd-efi-options", VERB_ANY, 2, 0, verb_systemd_efi_options },
002914e6 2569 { "reboot-to-firmware", VERB_ANY, 2, 0, verb_reboot_to_firmware },
2f2c539c
LP
2570 {}
2571 };
2572
2573 return dispatch_verb(argc, argv, verbs, NULL);
2574}
2575
608f8ec9 2576static int run(int argc, char *argv[]) {
80a2381d 2577 _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
80a2381d 2578 _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
601185b4 2579 int r;
7b4d7cc0
KS
2580
2581 log_parse_environment();
2582 log_open();
2583
341890de 2584 /* If we run in a container, automatically turn off EFI file system access */
2f2c539c
LP
2585 if (detect_container() > 0)
2586 arg_touch_variables = false;
2587
7b4d7cc0 2588 r = parse_argv(argc, argv);
601185b4 2589 if (r <= 0)
608f8ec9 2590 return r;
57db6f18 2591
80a2381d
LB
2592 /* Open up and mount the image */
2593 if (arg_image) {
2594 assert(!arg_root);
2595
2596 r = mount_image_privately_interactively(
2597 arg_image,
2598 DISSECT_IMAGE_GENERIC_ROOT |
2599 DISSECT_IMAGE_RELAX_VAR_CHECK,
2600 &unlink_dir,
e330f97a 2601 &loop_device);
80a2381d
LB
2602 if (r < 0)
2603 return r;
2604
2605 arg_root = strdup(unlink_dir);
2606 if (!arg_root)
2607 return log_oom();
2608 }
2609
608f8ec9 2610 return bootctl_main(argc, argv);
7b4d7cc0 2611}
608f8ec9 2612
002914e6 2613DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);