]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/boot/bootctl.c
bootspec: load entries from both the ESP and XBOOTLDR partitions
[thirdparty/systemd.git] / src / boot / bootctl.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
7b4d7cc0 2
6b5cf3ea 3#include <blkid.h>
0974a682 4#include <ctype.h>
3f6fd1ba
LP
5#include <dirent.h>
6#include <errno.h>
0974a682 7#include <ftw.h>
3f6fd1ba
LP
8#include <getopt.h>
9#include <limits.h>
5fa6c13c 10#include <linux/magic.h>
0974a682 11#include <stdbool.h>
3f6fd1ba
LP
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <sys/mman.h>
16#include <sys/stat.h>
17#include <sys/statfs.h>
18#include <unistd.h>
7b4d7cc0 19
dccca82b
LP
20#include "sd-id128.h"
21
b5efdb8a 22#include "alloc-util.h"
3f6fd1ba 23#include "blkid-util.h"
7e87c7d9 24#include "bootspec.h"
175d308c 25#include "copy.h"
e41256dc 26#include "dirent-util.h"
0974a682 27#include "efivars.h"
3ffd4af2 28#include "fd-util.h"
0d39fa9c 29#include "fileio.h"
175d308c 30#include "fs-util.h"
8752c575 31#include "locale-util.h"
608f8ec9 32#include "main-func.h"
57db6f18 33#include "pager.h"
2f2c539c 34#include "parse-util.h"
294bf0c3 35#include "pretty-print.h"
c6878637 36#include "rm-rf.h"
175d308c 37#include "stat-util.h"
07630cea 38#include "string-util.h"
2f2c539c 39#include "strv.h"
7e87c7d9 40#include "terminal-util.h"
e4de7287 41#include "tmpfile-util.h"
2f2c539c 42#include "umask-util.h"
d88c96ff 43#include "utf8.h"
3f6fd1ba 44#include "util.h"
2f2c539c
LP
45#include "verbs.h"
46#include "virt.h"
7b4d7cc0 47
2f2c539c 48static char *arg_path = NULL;
30b50477 49static bool arg_print_path = false;
25579a43 50static bool arg_touch_variables = true;
0221d68a 51static PagerFlags arg_pager_flags = 0;
25579a43 52
608f8ec9
YW
53STATIC_DESTRUCTOR_REGISTER(arg_path, freep);
54
5caa3167
LP
55static int acquire_esp(
56 bool unprivileged_mode,
57 uint32_t *ret_part,
58 uint64_t *ret_pstart,
59 uint64_t *ret_psize,
60 sd_id128_t *ret_uuid) {
61
62 char *np;
2f2c539c
LP
63 int r;
64
5caa3167
LP
65 /* Find the ESP, and log about errors. Note that find_esp_and_warn() will log in all error cases on its own,
66 * except for ENOKEY (which is good, we want to show our own message in that case, suggesting use of --path=)
67 * and EACCESS (only when we request unprivileged mode; in this case we simply eat up the error here, so that
68 * --list and --status work too, without noise about this). */
69
70 r = find_esp_and_warn(arg_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid);
71 if (r == -ENOKEY)
af918182 72 return log_error_errno(r,
5caa3167 73 "Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n"
af918182 74 "Alternatively, use --path= to specify path to mount point.");
5caa3167
LP
75 if (r < 0)
76 return r;
77
78 free_and_replace(arg_path, np);
0974a682 79
30b50477 80 log_debug("Using EFI System Partition at %s.", arg_path);
5caa3167 81
0974a682 82 return 0;
7b4d7cc0
KS
83}
84
e7dd673d 85/* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */
d3226d77 86static int get_file_version(int fd, char **v) {
0974a682
KS
87 struct stat st;
88 char *buf;
89 const char *s, *e;
90 char *x = NULL;
91 int r = 0;
7b4d7cc0 92
d3226d77 93 assert(fd >= 0);
0974a682 94 assert(v);
7b4d7cc0 95
d3226d77 96 if (fstat(fd, &st) < 0)
db6d9fae 97 return log_error_errno(errno, "Failed to stat EFI binary: %m");
7b4d7cc0 98
c4ba5b51
LP
99 r = stat_verify_regular(&st);
100 if (r < 0)
101 return log_error_errno(errno, "EFI binary is not a regular file: %m");
102
db6d9fae
LP
103 if (st.st_size < 27) {
104 *v = NULL;
0974a682 105 return 0;
db6d9fae 106 }
eb9da376 107
d3226d77 108 buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
0974a682 109 if (buf == MAP_FAILED)
db6d9fae 110 return log_error_errno(errno, "Failed to memory map EFI binary: %m");
7b4d7cc0 111
0974a682
KS
112 s = memmem(buf, st.st_size - 8, "#### LoaderInfo: ", 17);
113 if (!s)
114 goto finish;
115 s += 17;
7b4d7cc0 116
0974a682
KS
117 e = memmem(s, st.st_size - (s - buf), " ####", 5);
118 if (!e || e - s < 3) {
78d5d4ed 119 r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Malformed version string.");
0974a682
KS
120 goto finish;
121 }
7b4d7cc0 122
0974a682
KS
123 x = strndup(s, e - s);
124 if (!x) {
d3226d77 125 r = log_oom();
0974a682
KS
126 goto finish;
127 }
128 r = 1;
7b4d7cc0 129
0974a682 130finish:
db6d9fae 131 (void) munmap(buf, st.st_size);
0974a682
KS
132 *v = x;
133 return r;
134}
7b4d7cc0 135
0974a682 136static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) {
d3226d77
ZJS
137 char *p;
138 _cleanup_closedir_ DIR *d = NULL;
0974a682 139 struct dirent *de;
0974a682
KS
140 int r = 0, c = 0;
141
d3226d77 142 p = strjoina(esp_path, "/", path);
0974a682
KS
143 d = opendir(p);
144 if (!d) {
d3226d77
ZJS
145 if (errno == ENOENT)
146 return 0;
7b4d7cc0 147
d3226d77 148 return log_error_errno(errno, "Failed to read \"%s\": %m", p);
0974a682
KS
149 }
150
e41256dc 151 FOREACH_DIRENT(de, d, break) {
d3226d77
ZJS
152 _cleanup_close_ int fd = -1;
153 _cleanup_free_ char *v = NULL;
0974a682 154
d3226d77 155 if (!endswith_no_case(de->d_name, ".efi"))
0974a682
KS
156 continue;
157
d3226d77 158 if (prefix && !startswith_no_case(de->d_name, prefix))
0974a682
KS
159 continue;
160
d3226d77
ZJS
161 fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
162 if (fd < 0)
163 return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
0974a682 164
d3226d77 165 r = get_file_version(fd, &v);
0974a682 166 if (r < 0)
d3226d77 167 return r;
0974a682 168 if (r > 0)
9a6f746f 169 printf(" File: %s/%s/%s (%s%s%s)\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name, ansi_highlight(), v, ansi_normal());
0974a682 170 else
9a6f746f 171 printf(" File: %s/%s/%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name);
0974a682 172 c++;
0974a682
KS
173 }
174
d3226d77 175 return c;
7b4d7cc0
KS
176}
177
0974a682
KS
178static int status_binaries(const char *esp_path, sd_id128_t partition) {
179 int r;
180
7fd66f3c 181 printf("Available Boot Loaders on ESP:\n");
0974a682 182
cd2d4c7f
YW
183 if (!esp_path) {
184 printf(" ESP: Cannot find or access mount point of ESP.\n\n");
185 return -ENOENT;
186 }
187
188 printf(" ESP: %s", esp_path);
189 if (!sd_id128_is_null(partition))
190 printf(" (/dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x)", SD_ID128_FORMAT_VAL(partition));
191 printf("\n");
0974a682
KS
192
193 r = enumerate_binaries(esp_path, "EFI/systemd", NULL);
194 if (r == 0)
48184e43 195 log_info("systemd-boot not installed in ESP.");
0974a682
KS
196 else if (r < 0)
197 return r;
198
00f69504 199 r = enumerate_binaries(esp_path, "EFI/BOOT", "boot");
0974a682 200 if (r == 0)
48184e43 201 log_info("No default/fallback boot loader installed in ESP.");
0974a682
KS
202 else if (r < 0)
203 return r;
204
c11ae0ba
KS
205 printf("\n");
206
0974a682
KS
207 return 0;
208}
209
210static int print_efi_option(uint16_t id, bool in_order) {
7cb0f263
TA
211 _cleanup_free_ char *title = NULL;
212 _cleanup_free_ char *path = NULL;
0974a682
KS
213 sd_id128_t partition;
214 bool active;
215 int r = 0;
216
217 r = efi_get_boot_option(id, &title, &partition, &path, &active);
218 if (r < 0)
7cb0f263 219 return r;
7b4d7cc0 220
0974a682 221 /* print only configured entries with partition information */
3bbaff3e 222 if (!path || sd_id128_is_null(partition))
0974a682
KS
223 return 0;
224
225 efi_tilt_backslashes(path);
226
ba857253 227 printf(" Title: %s%s%s\n", ansi_highlight(), strna(title), ansi_normal());
0974a682
KS
228 printf(" ID: 0x%04X\n", id);
229 printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : "");
230 printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(partition));
9a6f746f 231 printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), path);
0974a682
KS
232 printf("\n");
233
7cb0f263 234 return 0;
0974a682
KS
235}
236
237static int status_variables(void) {
d3226d77 238 _cleanup_free_ uint16_t *options = NULL, *order = NULL;
a36b411e 239 int n_options, n_order, i;
0974a682 240
0974a682 241 n_options = efi_get_boot_options(&options);
d3226d77 242 if (n_options == -ENOENT)
f939cff7
LP
243 return log_error_errno(n_options,
244 "Failed to access EFI variables, efivarfs"
d3226d77 245 " needs to be available at /sys/firmware/efi/efivars/.");
f939cff7 246 if (n_options < 0)
d3226d77 247 return log_error_errno(n_options, "Failed to read EFI boot entries: %m");
0974a682 248
0974a682 249 n_order = efi_get_boot_order(&order);
d3226d77 250 if (n_order == -ENOENT)
0974a682 251 n_order = 0;
d3226d77 252 else if (n_order < 0)
7709ef3a 253 return log_error_errno(n_order, "Failed to read EFI boot order: %m");
0974a682
KS
254
255 /* print entries in BootOrder first */
7fd66f3c 256 printf("Boot Loaders Listed in EFI Variables:\n");
0974a682
KS
257 for (i = 0; i < n_order; i++)
258 print_efi_option(order[i], true);
259
260 /* print remaining entries */
261 for (i = 0; i < n_options; i++) {
262 int j;
0974a682
KS
263
264 for (j = 0; j < n_order; j++)
d3226d77 265 if (options[i] == order[j])
a908cf0a 266 goto next_option;
0974a682
KS
267
268 print_efi_option(options[i], false);
a908cf0a
MM
269
270 next_option:
271 continue;
0974a682
KS
272 }
273
d3226d77 274 return 0;
0974a682
KS
275}
276
20a28174
LP
277static int boot_entry_show(const BootEntry *e, bool show_as_default) {
278 assert(e);
279
280 printf(" title: %s%s%s%s%s%s\n",
281 ansi_highlight(),
282 boot_entry_title(e),
283 ansi_normal(),
284 ansi_highlight_green(),
285 show_as_default ? " (default)" : "",
286 ansi_normal());
287
288 if (e->id)
289 printf(" id: %s\n", e->id);
290 if (e->version)
291 printf(" version: %s\n", e->version);
292 if (e->machine_id)
293 printf(" machine-id: %s\n", e->machine_id);
294 if (e->architecture)
295 printf(" architecture: %s\n", e->architecture);
296 if (e->kernel)
297 printf(" linux: %s\n", e->kernel);
298 if (!strv_isempty(e->initrd)) {
299 _cleanup_free_ char *t;
300
301 t = strv_join(e->initrd, " ");
302 if (!t)
303 return log_oom();
304
305 printf(" initrd: %s\n", t);
306 }
307 if (!strv_isempty(e->options)) {
308 _cleanup_free_ char *t;
309
310 t = strv_join(e->options, " ");
311 if (!t)
312 return log_oom();
313
314 printf(" options: %s\n", t);
315 }
316 if (e->device_tree)
317 printf(" devicetree: %s\n", e->device_tree);
318
319 return 0;
320}
321
7e87c7d9 322static int status_entries(const char *esp_path, sd_id128_t partition) {
7e87c7d9 323 _cleanup_(boot_config_free) BootConfig config = {};
a099e035 324 int r;
7e87c7d9 325
a2f8664e 326 r = boot_entries_load_config(esp_path, NULL, &config);
7e87c7d9 327 if (r < 0)
21f7a622 328 return r;
7e87c7d9
ZJS
329
330 if (config.default_entry < 0)
a099e035 331 printf("%zu entries, no entry could be determined as default.\n", config.n_entries);
7e87c7d9 332 else {
7fd66f3c 333 printf("Default Boot Loader Entry:\n");
a099e035 334
20a28174 335 boot_entry_show(config.entries + config.default_entry, false);
7e87c7d9
ZJS
336 }
337
338 return 0;
339}
340
0974a682
KS
341static int compare_product(const char *a, const char *b) {
342 size_t x, y;
343
344 assert(a);
345 assert(b);
346
347 x = strcspn(a, " ");
348 y = strcspn(b, " ");
349 if (x != y)
350 return x < y ? -1 : x > y ? 1 : 0;
351
352 return strncmp(a, b, x);
353}
354
355static int compare_version(const char *a, const char *b) {
356 assert(a);
357 assert(b);
358
359 a += strcspn(a, " ");
360 a += strspn(a, " ");
361 b += strcspn(b, " ");
362 b += strspn(b, " ");
363
364 return strverscmp(a, b);
365}
366
175d308c 367static int version_check(int fd_from, const char *from, int fd_to, const char *to) {
d3226d77 368 _cleanup_free_ char *a = NULL, *b = NULL;
0974a682
KS
369 int r;
370
175d308c 371 assert(fd_from >= 0);
0974a682 372 assert(from);
175d308c 373 assert(fd_to >= 0);
0974a682
KS
374 assert(to);
375
175d308c 376 r = get_file_version(fd_from, &a);
0974a682 377 if (r < 0)
d3226d77 378 return r;
baaa35ad
ZJS
379 if (r == 0)
380 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
381 "Source file \"%s\" does not carry version information!",
382 from);
0974a682 383
175d308c 384 r = get_file_version(fd_to, &b);
0974a682 385 if (r < 0)
d3226d77 386 return r;
baaa35ad
ZJS
387 if (r == 0 || compare_product(a, b) != 0)
388 return log_notice_errno(SYNTHETIC_ERRNO(EEXIST),
389 "Skipping \"%s\", since it's owned by another boot loader.",
390 to);
0974a682 391
78d5d4ed
LP
392 if (compare_version(a, b) < 0)
393 return log_warning_errno(SYNTHETIC_ERRNO(ESTALE), "Skipping \"%s\", since a newer boot loader version exists already.", to);
0974a682 394
d3226d77 395 return 0;
0974a682
KS
396}
397
175d308c
LP
398static int copy_file_with_version_check(const char *from, const char *to, bool force) {
399 _cleanup_close_ int fd_from = -1, fd_to = -1;
400 _cleanup_free_ char *t = NULL;
0974a682 401 int r;
0974a682 402
175d308c
LP
403 fd_from = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
404 if (fd_from < 0)
d3226d77 405 return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", from);
0974a682
KS
406
407 if (!force) {
175d308c
LP
408 fd_to = open(to, O_RDONLY|O_CLOEXEC|O_NOCTTY);
409 if (fd_to < 0) {
410 if (errno != -ENOENT)
411 return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", to);
412 } else {
413 r = version_check(fd_from, from, fd_to, to);
414 if (r < 0)
415 return r;
416
417 if (lseek(fd_from, 0, SEEK_SET) == (off_t) -1)
6719ca72 418 return log_error_errno(errno, "Failed to seek in \"%s\": %m", from);
175d308c
LP
419
420 fd_to = safe_close(fd_to);
0974a682 421 }
175d308c 422 }
d3226d77 423
175d308c
LP
424 r = tempfn_random(to, NULL, &t);
425 if (r < 0)
426 return log_oom();
0974a682 427
175d308c
LP
428 RUN_WITH_UMASK(0000) {
429 fd_to = open(t, O_WRONLY|O_CREAT|O_CLOEXEC|O_EXCL|O_NOFOLLOW, 0644);
430 if (fd_to < 0)
431 return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", t);
0974a682
KS
432 }
433
175d308c 434 r = copy_bytes(fd_from, fd_to, (uint64_t) -1, COPY_REFLINK);
0974a682 435 if (r < 0) {
0675e94a
AJ
436 (void) unlink(t);
437 return log_error_errno(r, "Failed to copy data from \"%s\" to \"%s\": %m", from, t);
0974a682
KS
438 }
439
175d308c 440 (void) copy_times(fd_from, fd_to);
0974a682 441
8ac2f74f 442 if (fsync(fd_to) < 0) {
0675e94a
AJ
443 (void) unlink_noerrno(t);
444 return log_error_errno(errno, "Failed to copy data from \"%s\" to \"%s\": %m", from, t);
445 }
446
8ac2f74f
LP
447 (void) fsync_directory_of_file(fd_to);
448
b1c05b98 449 if (renameat(AT_FDCWD, t, AT_FDCWD, to) < 0) {
175d308c
LP
450 (void) unlink_noerrno(t);
451 return log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", t, to);
0974a682
KS
452 }
453
d3226d77 454 log_info("Copied \"%s\" to \"%s\".", from, to);
0974a682 455
175d308c 456 return 0;
0974a682
KS
457}
458
0974a682
KS
459static int mkdir_one(const char *prefix, const char *suffix) {
460 char *p;
461
d3226d77 462 p = strjoina(prefix, "/", suffix);
0974a682 463 if (mkdir(p, 0700) < 0) {
d3226d77
ZJS
464 if (errno != EEXIST)
465 return log_error_errno(errno, "Failed to create \"%s\": %m", p);
0974a682 466 } else
d3226d77 467 log_info("Created \"%s\".", p);
7b4d7cc0 468
0974a682
KS
469 return 0;
470}
471
d3226d77
ZJS
472static const char *efi_subdirs[] = {
473 "EFI",
474 "EFI/systemd",
00f69504 475 "EFI/BOOT",
d3226d77 476 "loader",
9ee051b9
YW
477 "loader/entries",
478 NULL
d3226d77
ZJS
479};
480
0974a682 481static int create_dirs(const char *esp_path) {
181ccb43 482 const char **i;
0974a682
KS
483 int r;
484
181ccb43
LP
485 STRV_FOREACH(i, efi_subdirs) {
486 r = mkdir_one(esp_path, *i);
d3226d77
ZJS
487 if (r < 0)
488 return r;
489 }
7b4d7cc0 490
7b4d7cc0 491 return 0;
7b4d7cc0
KS
492}
493
0974a682 494static int copy_one_file(const char *esp_path, const char *name, bool force) {
d3226d77 495 char *p, *q;
0974a682
KS
496 int r;
497
d3226d77
ZJS
498 p = strjoina(BOOTLIBDIR "/", name);
499 q = strjoina(esp_path, "/EFI/systemd/", name);
175d308c 500 r = copy_file_with_version_check(p, q, force);
0974a682 501
e7dd673d 502 if (startswith(name, "systemd-boot")) {
0974a682 503 int k;
d3226d77 504 char *v;
0974a682
KS
505
506 /* Create the EFI default boot loader name (specified for removable devices) */
fbd0b64f
LP
507 v = strjoina(esp_path, "/EFI/BOOT/BOOT",
508 name + STRLEN("systemd-boot"));
846b8fc3 509 ascii_strupper(strrchr(v, '/') + 1);
0974a682 510
175d308c 511 k = copy_file_with_version_check(p, v, force);
0974a682 512 if (k < 0 && r == 0)
d3226d77 513 r = k;
0974a682
KS
514 }
515
516 return r;
7b4d7cc0
KS
517}
518
0974a682
KS
519static int install_binaries(const char *esp_path, bool force) {
520 struct dirent *de;
d3226d77 521 _cleanup_closedir_ DIR *d = NULL;
0974a682
KS
522 int r = 0;
523
524 if (force) {
525 /* Don't create any of these directories when we are
526 * just updating. When we update we'll drop-in our
527 * files (unless there are newer ones already), but we
528 * won't create the directories for them in the first
529 * place. */
530 r = create_dirs(esp_path);
531 if (r < 0)
532 return r;
533 }
534
e7dd673d 535 d = opendir(BOOTLIBDIR);
d3226d77
ZJS
536 if (!d)
537 return log_error_errno(errno, "Failed to open \""BOOTLIBDIR"\": %m");
0974a682 538
e41256dc 539 FOREACH_DIRENT(de, d, break) {
0974a682
KS
540 int k;
541
d3226d77 542 if (!endswith_no_case(de->d_name, ".efi"))
0974a682
KS
543 continue;
544
545 k = copy_one_file(esp_path, de->d_name, force);
546 if (k < 0 && r == 0)
547 r = k;
548 }
549
0974a682 550 return r;
7b4d7cc0
KS
551}
552
0974a682 553static bool same_entry(uint16_t id, const sd_id128_t uuid, const char *path) {
d3226d77 554 _cleanup_free_ char *opath = NULL;
0974a682 555 sd_id128_t ouuid;
d3226d77 556 int r;
7b4d7cc0 557
d3226d77
ZJS
558 r = efi_get_boot_option(id, NULL, &ouuid, &opath, NULL);
559 if (r < 0)
0974a682
KS
560 return false;
561 if (!sd_id128_equal(uuid, ouuid))
d3226d77 562 return false;
0974a682 563 if (!streq_ptr(path, opath))
d3226d77 564 return false;
0974a682 565
d3226d77 566 return true;
0974a682
KS
567}
568
569static int find_slot(sd_id128_t uuid, const char *path, uint16_t *id) {
d3226d77
ZJS
570 _cleanup_free_ uint16_t *options = NULL;
571 int n, i;
0974a682 572
d3226d77
ZJS
573 n = efi_get_boot_options(&options);
574 if (n < 0)
575 return n;
0974a682 576
e7dd673d 577 /* find already existing systemd-boot entry */
d3226d77 578 for (i = 0; i < n; i++)
0974a682 579 if (same_entry(options[i], uuid, path)) {
d3226d77
ZJS
580 *id = options[i];
581 return 1;
0974a682
KS
582 }
583
584 /* find free slot in the sorted BootXXXX variable list */
d3226d77 585 for (i = 0; i < n; i++)
0974a682 586 if (i != options[i]) {
d3226d77
ZJS
587 *id = i;
588 return 1;
0974a682
KS
589 }
590
591 /* use the next one */
592 if (i == 0xffff)
593 return -ENOSPC;
d3226d77
ZJS
594 *id = i;
595 return 0;
0974a682
KS
596}
597
598static int insert_into_order(uint16_t slot, bool first) {
d3226d77
ZJS
599 _cleanup_free_ uint16_t *order = NULL;
600 uint16_t *t;
601 int n, i;
0974a682 602
d3226d77
ZJS
603 n = efi_get_boot_order(&order);
604 if (n <= 0)
0974a682 605 /* no entry, add us */
d3226d77 606 return efi_set_boot_order(&slot, 1);
0974a682
KS
607
608 /* are we the first and only one? */
d3226d77
ZJS
609 if (n == 1 && order[0] == slot)
610 return 0;
0974a682
KS
611
612 /* are we already in the boot order? */
d3226d77 613 for (i = 0; i < n; i++) {
0974a682
KS
614 if (order[i] != slot)
615 continue;
616
617 /* we do not require to be the first one, all is fine */
618 if (!first)
d3226d77 619 return 0;
0974a682
KS
620
621 /* move us to the first slot */
d3226d77 622 memmove(order + 1, order, i * sizeof(uint16_t));
0974a682 623 order[0] = slot;
d3226d77 624 return efi_set_boot_order(order, n);
0974a682
KS
625 }
626
627 /* extend array */
d3226d77
ZJS
628 t = realloc(order, (n + 1) * sizeof(uint16_t));
629 if (!t)
630 return -ENOMEM;
631 order = t;
0974a682
KS
632
633 /* add us to the top or end of the list */
634 if (first) {
d3226d77 635 memmove(order + 1, order, n * sizeof(uint16_t));
0974a682
KS
636 order[0] = slot;
637 } else
d3226d77 638 order[n] = slot;
0974a682 639
d3226d77 640 return efi_set_boot_order(order, n + 1);
0974a682
KS
641}
642
643static int remove_from_order(uint16_t slot) {
7cb0f263 644 _cleanup_free_ uint16_t *order = NULL;
d3226d77 645 int n, i;
0974a682 646
d3226d77
ZJS
647 n = efi_get_boot_order(&order);
648 if (n <= 0)
649 return n;
0974a682 650
d3226d77 651 for (i = 0; i < n; i++) {
0974a682
KS
652 if (order[i] != slot)
653 continue;
654
d3226d77
ZJS
655 if (i + 1 < n)
656 memmove(order + i, order + i+1, (n - i) * sizeof(uint16_t));
657 return efi_set_boot_order(order, n - 1);
0974a682
KS
658 }
659
d3226d77 660 return 0;
0974a682
KS
661}
662
663static int install_variables(const char *esp_path,
664 uint32_t part, uint64_t pstart, uint64_t psize,
665 sd_id128_t uuid, const char *path,
666 bool first) {
d3226d77 667 char *p;
0974a682
KS
668 uint16_t slot;
669 int r;
670
671 if (!is_efi_boot()) {
d3226d77 672 log_warning("Not booted with EFI, skipping EFI variable setup.");
0974a682
KS
673 return 0;
674 }
675
d3226d77 676 p = strjoina(esp_path, path);
0974a682
KS
677 if (access(p, F_OK) < 0) {
678 if (errno == ENOENT)
d3226d77 679 return 0;
f939cff7
LP
680
681 return log_error_errno(errno, "Cannot access \"%s\": %m", p);
0974a682 682 }
7b4d7cc0 683
0974a682 684 r = find_slot(uuid, path, &slot);
d3226d77
ZJS
685 if (r < 0)
686 return log_error_errno(r,
687 r == -ENOENT ?
688 "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" :
689 "Failed to determine current boot order: %m");
7b4d7cc0 690
181ccb43 691 if (first || r == 0) {
0974a682
KS
692 r = efi_add_boot_option(slot, "Linux Boot Manager",
693 part, pstart, psize,
694 uuid, path);
d3226d77
ZJS
695 if (r < 0)
696 return log_error_errno(r, "Failed to create EFI Boot variable entry: %m");
0974a682 697
d3226d77
ZJS
698 log_info("Created EFI boot entry \"Linux Boot Manager\".");
699 }
0974a682 700
d3226d77 701 return insert_into_order(slot, first);
0974a682
KS
702}
703
704static int remove_boot_efi(const char *esp_path) {
d3226d77
ZJS
705 char *p;
706 _cleanup_closedir_ DIR *d = NULL;
0974a682 707 struct dirent *de;
d3226d77 708 int r, c = 0;
0974a682 709
00f69504 710 p = strjoina(esp_path, "/EFI/BOOT");
0974a682
KS
711 d = opendir(p);
712 if (!d) {
d3226d77
ZJS
713 if (errno == ENOENT)
714 return 0;
0974a682 715
d3226d77 716 return log_error_errno(errno, "Failed to open directory \"%s\": %m", p);
0974a682
KS
717 }
718
e41256dc 719 FOREACH_DIRENT(de, d, break) {
d3226d77
ZJS
720 _cleanup_close_ int fd = -1;
721 _cleanup_free_ char *v = NULL;
0974a682 722
d3226d77 723 if (!endswith_no_case(de->d_name, ".efi"))
0974a682
KS
724 continue;
725
b7536c45 726 if (!startswith_no_case(de->d_name, "boot"))
0974a682
KS
727 continue;
728
d3226d77 729 fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
dd114e11 730 if (fd < 0)
d3226d77 731 return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
0974a682 732
d3226d77 733 r = get_file_version(fd, &v);
0974a682 734 if (r < 0)
d3226d77
ZJS
735 return r;
736 if (r > 0 && startswith(v, "systemd-boot ")) {
737 r = unlinkat(dirfd(d), de->d_name, 0);
738 if (r < 0)
739 return log_error_errno(errno, "Failed to remove \"%s/%s\": %m", p, de->d_name);
740
a592ab6a 741 log_info("Removed \"%s/%s\".", p, de->d_name);
0974a682
KS
742 }
743
744 c++;
0974a682
KS
745 }
746
d3226d77 747 return c;
0974a682
KS
748}
749
750static int rmdir_one(const char *prefix, const char *suffix) {
751 char *p;
7b4d7cc0 752
d3226d77 753 p = strjoina(prefix, "/", suffix);
0974a682 754 if (rmdir(p) < 0) {
d3226d77
ZJS
755 if (!IN_SET(errno, ENOENT, ENOTEMPTY))
756 return log_error_errno(errno, "Failed to remove \"%s\": %m", p);
7b4d7cc0 757 } else
d3226d77 758 log_info("Removed \"%s\".", p);
7b4d7cc0 759
0974a682 760 return 0;
7b4d7cc0
KS
761}
762
0974a682
KS
763static int remove_binaries(const char *esp_path) {
764 char *p;
765 int r, q;
d3226d77 766 unsigned i;
0974a682 767
d3226d77 768 p = strjoina(esp_path, "/EFI/systemd");
c6878637 769 r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
0974a682
KS
770
771 q = remove_boot_efi(esp_path);
772 if (q < 0 && r == 0)
773 r = q;
774
88a00ac5 775 for (i = ELEMENTSOF(efi_subdirs)-1; i > 0; i--) {
d3226d77
ZJS
776 q = rmdir_one(esp_path, efi_subdirs[i-1]);
777 if (q < 0 && r == 0)
778 r = q;
779 }
0974a682
KS
780
781 return r;
782}
783
784static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) {
785 uint16_t slot;
786 int r;
787
788 if (!is_efi_boot())
789 return 0;
790
791 r = find_slot(uuid, path, &slot);
792 if (r != 1)
793 return 0;
794
795 r = efi_remove_boot_option(slot);
796 if (r < 0)
797 return r;
798
799 if (in_order)
d3226d77 800 return remove_from_order(slot);
f939cff7
LP
801
802 return 0;
0974a682
KS
803}
804
805static int install_loader_config(const char *esp_path) {
0974a682 806
d5ff6d6d 807 char machine_string[SD_ID128_STRING_MAX];
f5b84de2
LP
808 _cleanup_(unlink_and_freep) char *t = NULL;
809 _cleanup_fclose_ FILE *f = NULL;
d5ff6d6d
LP
810 sd_id128_t machine_id;
811 const char *p;
f5b84de2 812 int r, fd;
0974a682 813
d5ff6d6d
LP
814 r = sd_id128_get_machine(&machine_id);
815 if (r < 0)
0675e94a 816 return log_error_errno(r, "Failed to get machine id: %m");
0974a682 817
d5ff6d6d 818 p = strjoina(esp_path, "/loader/loader.conf");
f5b84de2
LP
819
820 if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */
821 return 0;
822
823 fd = open_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t);
824 if (fd < 0)
825 return log_error_errno(fd, "Failed to open \"%s\" for writing: %m", p);
826
e92aaed3 827 f = fdopen(fd, "w");
f5b84de2
LP
828 if (!f) {
829 safe_close(fd);
830 return log_oom();
831 }
0974a682 832
a36b411e
LP
833 fprintf(f, "#timeout 3\n"
834 "#console-mode keep\n"
835 "default %s-*\n", sd_id128_to_string(machine_id, machine_string));
0974a682 836
0675e94a 837 r = fflush_sync_and_check(f);
d5ff6d6d
LP
838 if (r < 0)
839 return log_error_errno(r, "Failed to write \"%s\": %m", p);
0974a682 840
f5b84de2
LP
841 r = link_tmpfile(fd, t, p);
842 if (r == -EEXIST)
843 return 0; /* Silently skip creation if the file exists now (recheck) */
844 if (r < 0)
845 return log_error_errno(r, "Failed to move \"%s\" into place: %m", p);
846
847 t = mfree(t);
848
849 return 1;
0974a682
KS
850}
851
2f2c539c 852static int help(int argc, char *argv[], void *userdata) {
37ec0fdd
LP
853 _cleanup_free_ char *link = NULL;
854 int r;
855
856 r = terminal_urlify_man("bootctl", "1", &link);
857 if (r < 0)
858 return log_oom();
2f2c539c 859
37ec0fdd 860 printf("%s [COMMAND] [OPTIONS...]\n\n"
68cc17f1 861 "Install, update or remove the systemd-boot EFI boot manager.\n\n"
0974a682
KS
862 " -h --help Show this help\n"
863 " --version Print version\n"
864 " --path=PATH Path to the EFI System Partition (ESP)\n"
30b50477 865 " -p --print-path Print path to the EFI partition\n"
0974a682 866 " --no-variables Don't touch EFI variables\n"
57db6f18 867 " --no-pager Do not pipe output into a pager\n"
d88c96ff 868 "\nBoot Loader Commands:\n"
e7dd673d
TG
869 " status Show status of installed systemd-boot and EFI variables\n"
870 " install Install systemd-boot to the ESP and EFI variables\n"
871 " update Update systemd-boot in the ESP and EFI variables\n"
37ec0fdd 872 " remove Remove systemd-boot from the ESP and EFI variables\n"
d88c96ff
LP
873 "\nBoot Loader Entries Commands:\n"
874 " list List boot loader entries\n"
875 " set-default ID Set default boot loader entry\n"
876 " set-oneshot ID Set default boot loader entry, for next boot only\n"
37ec0fdd
LP
877 "\nSee the %s for details.\n"
878 , program_invocation_short_name
d88c96ff 879 , link);
0974a682
KS
880
881 return 0;
882}
883
0974a682
KS
884static int parse_argv(int argc, char *argv[]) {
885 enum {
886 ARG_PATH = 0x100,
887 ARG_VERSION,
888 ARG_NO_VARIABLES,
57db6f18 889 ARG_NO_PAGER,
7b4d7cc0
KS
890 };
891
0974a682
KS
892 static const struct option options[] = {
893 { "help", no_argument, NULL, 'h' },
894 { "version", no_argument, NULL, ARG_VERSION },
895 { "path", required_argument, NULL, ARG_PATH },
30b50477 896 { "print-path", no_argument, NULL, 'p' },
0974a682 897 { "no-variables", no_argument, NULL, ARG_NO_VARIABLES },
57db6f18 898 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
a36b411e 899 {}
0974a682
KS
900 };
901
2f2c539c 902 int c, r;
7b4d7cc0
KS
903
904 assert(argc >= 0);
905 assert(argv);
906
30b50477 907 while ((c = getopt_long(argc, argv, "hp", options, NULL)) >= 0)
0974a682 908 switch (c) {
7b4d7cc0 909
0974a682 910 case 'h':
2f2c539c 911 help(0, NULL, NULL);
7b4d7cc0 912 return 0;
7b4d7cc0 913
0974a682 914 case ARG_VERSION:
3f6fd1ba 915 return version();
7b4d7cc0 916
0974a682 917 case ARG_PATH:
2f2c539c
LP
918 r = free_and_strdup(&arg_path, optarg);
919 if (r < 0)
920 return log_oom();
0974a682
KS
921 break;
922
30b50477
ZJS
923 case 'p':
924 arg_print_path = true;
925 break;
926
0974a682
KS
927 case ARG_NO_VARIABLES:
928 arg_touch_variables = false;
929 break;
930
57db6f18 931 case ARG_NO_PAGER:
0221d68a 932 arg_pager_flags |= PAGER_DISABLE;
57db6f18
LP
933 break;
934
0974a682
KS
935 case '?':
936 return -EINVAL;
937
938 default:
d3226d77 939 assert_not_reached("Unknown option");
7b4d7cc0 940 }
7b4d7cc0 941
0974a682
KS
942 return 1;
943}
7b4d7cc0 944
551710cf
ZJS
945static void read_loader_efi_var(const char *name, char **var) {
946 int r;
947
948 r = efi_get_variable_string(EFI_VENDOR_LOADER, name, var);
949 if (r < 0 && r != -ENOENT)
950 log_warning_errno(r, "Failed to read EFI variable %s: %m", name);
951}
952
2f2c539c 953static int verb_status(int argc, char *argv[], void *userdata) {
0974a682 954
2f2c539c 955 sd_id128_t uuid = SD_ID128_NULL;
46fb255b 956 int r, k;
0974a682 957
5caa3167 958 r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &uuid);
0974a682 959
30b50477 960 if (arg_print_path) {
5caa3167
LP
961 if (r == -EACCES) /* If we couldn't acquire the ESP path, log about access errors (which is the only
962 * error the find_esp_and_warn() won't log on its own) */
963 return log_error_errno(r, "Failed to determine ESP: %m");
30b50477
ZJS
964 if (r < 0)
965 return r;
966
967 puts(arg_path);
968 return 0;
969 }
551710cf 970
5caa3167
LP
971 r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just show what we
972 * can show */
973
0221d68a 974 (void) pager_open(arg_pager_flags);
57db6f18 975
2f2c539c 976 if (is_efi_boot()) {
80641a81
LP
977 static const struct {
978 uint64_t flag;
979 const char *name;
980 } flags[] = {
981 { EFI_LOADER_FEATURE_BOOT_COUNTING, "Boot counting" },
982 { EFI_LOADER_FEATURE_CONFIG_TIMEOUT, "Menu timeout control" },
983 { EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT, "One-shot menu timeout control" },
984 { EFI_LOADER_FEATURE_ENTRY_DEFAULT, "Default entry control" },
985 { EFI_LOADER_FEATURE_ENTRY_ONESHOT, "One-shot entry control" },
986 };
987
81375b9b 988 _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
25579a43 989 sd_id128_t loader_part_uuid = SD_ID128_NULL;
80641a81
LP
990 uint64_t loader_features = 0;
991 size_t i;
0974a682 992
2f2c539c
LP
993 read_loader_efi_var("LoaderFirmwareType", &fw_type);
994 read_loader_efi_var("LoaderFirmwareInfo", &fw_info);
995 read_loader_efi_var("LoaderInfo", &loader);
81375b9b 996 read_loader_efi_var("StubInfo", &stub);
2f2c539c 997 read_loader_efi_var("LoaderImageIdentifier", &loader_path);
80641a81 998 (void) efi_loader_get_features(&loader_features);
551710cf 999
2f2c539c
LP
1000 if (loader_path)
1001 efi_tilt_backslashes(loader_path);
1002
46fb255b
ZJS
1003 k = efi_loader_get_device_part_uuid(&loader_part_uuid);
1004 if (k < 0 && k != -ENOENT)
1005 r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m");
2f2c539c
LP
1006
1007 printf("System:\n");
ba857253 1008 printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal());
33987ba0
YW
1009 printf(" Secure Boot: %sd\n", enable_disable(is_efi_secure_boot()));
1010 printf(" Setup Mode: %s\n", is_efi_secure_boot_setup_mode() ? "setup" : "user");
2f2c539c
LP
1011 printf("\n");
1012
7fd66f3c 1013 printf("Current Boot Loader:\n");
ba857253 1014 printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal());
80641a81
LP
1015
1016 for (i = 0; i < ELEMENTSOF(flags); i++) {
1017
1018 if (i == 0)
1019 printf(" Features: ");
1020 else
1021 printf(" ");
1022
1023 if (FLAGS_SET(loader_features, flags[i].flag))
9a6f746f 1024 printf("%s%s%s %s\n", ansi_highlight_green(), special_glyph(SPECIAL_GLYPH_CHECK_MARK), ansi_normal(), flags[i].name);
80641a81 1025 else
9a6f746f 1026 printf("%s%s%s %s\n", ansi_highlight_red(), special_glyph(SPECIAL_GLYPH_CROSS_MARK), ansi_normal(), flags[i].name);
80641a81
LP
1027 }
1028
81375b9b
ДГ
1029 if (stub)
1030 printf(" Stub: %s\n", stub);
e28973ee 1031 if (!sd_id128_is_null(loader_part_uuid))
cd2d4c7f 1032 printf(" ESP: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
2f2c539c
LP
1033 SD_ID128_FORMAT_VAL(loader_part_uuid));
1034 else
cd2d4c7f 1035 printf(" ESP: n/a\n");
9a6f746f 1036 printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
2f2c539c
LP
1037 printf("\n");
1038 } else
cd2d4c7f 1039 printf("System:\n Not booted with EFI\n\n");
7b4d7cc0 1040
ecec2a5d
LP
1041 if (arg_path) {
1042 k = status_binaries(arg_path, uuid);
1043 if (k < 0)
1044 r = k;
1045 }
2f2c539c 1046
cd2d4c7f 1047 if (is_efi_boot()) {
46fb255b
ZJS
1048 k = status_variables();
1049 if (k < 0)
1050 r = k;
cd2d4c7f 1051 }
0974a682 1052
ecec2a5d
LP
1053 if (arg_path) {
1054 k = status_entries(arg_path, uuid);
1055 if (k < 0)
1056 r = k;
1057 }
7e87c7d9 1058
46fb255b 1059 return r;
2f2c539c 1060}
7b4d7cc0 1061
7e87c7d9 1062static int verb_list(int argc, char *argv[], void *userdata) {
5caa3167 1063 _cleanup_(boot_config_free) BootConfig config = {};
bd2865ca 1064 _cleanup_free_ char **found_by_loader = NULL;
7e87c7d9 1065 sd_id128_t uuid = SD_ID128_NULL;
5caa3167 1066 int r;
7e87c7d9 1067
5caa3167
LP
1068 /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn
1069 * off logging about access errors and turn off potentially privileged device probing. Here we're interested in
1070 * the latter but not the former, hence request the mode, and log about EACCES. */
7e87c7d9 1071
5caa3167
LP
1072 r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &uuid);
1073 if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */
1074 return log_error_errno(r, "Failed to determine ESP: %m");
7e87c7d9
ZJS
1075 if (r < 0)
1076 return r;
1077
a2f8664e 1078 r = boot_entries_load_config(arg_path, NULL, &config);
7e87c7d9 1079 if (r < 0)
21f7a622 1080 return r;
7e87c7d9 1081
bd2865ca
LP
1082 r = efi_loader_get_entries(&found_by_loader);
1083 if (r < 0 && !IN_SET(r, -ENOENT, -EOPNOTSUPP))
1084 log_debug_errno(r, "Failed to acquire boot loader discovered entries: %m");
1085
20a28174
LP
1086 if (config.n_entries == 0)
1087 log_info("No boot loader entries found.");
1088 else {
1089 size_t n;
7e87c7d9 1090
0221d68a 1091 (void) pager_open(arg_pager_flags);
57db6f18 1092
20a28174 1093 printf("Boot Loader Entries:\n");
7e87c7d9 1094
20a28174
LP
1095 for (n = 0; n < config.n_entries; n++) {
1096 r = boot_entry_show(config.entries + n, n == (size_t) config.default_entry);
1097 if (r < 0)
1098 return r;
7e87c7d9 1099
20a28174 1100 puts("");
bd2865ca
LP
1101
1102 strv_remove(found_by_loader, config.entries[n].id);
7e87c7d9 1103 }
cd2d4c7f 1104 }
0974a682 1105
bd2865ca
LP
1106 if (!strv_isempty(found_by_loader)) {
1107 char **i;
1108
1109 printf("Automatic/Other Entries Found by Boot Loader:\n\n");
1110
1111 STRV_FOREACH(i, found_by_loader)
1112 puts(*i);
1113 }
1114
7e87c7d9 1115 return 0;
2f2c539c 1116}
7b4d7cc0 1117
e0e8d177
LP
1118static int sync_esp(void) {
1119 _cleanup_close_ int fd = -1;
1120
1121 if (!arg_path)
1122 return 0;
1123
1124 fd = open(arg_path, O_CLOEXEC|O_DIRECTORY|O_RDONLY);
1125 if (fd < 0)
1126 return log_error_errno(errno, "Couldn't open ESP '%s' for synchronization: %m", arg_path);
1127
1128 if (syncfs(fd) < 0)
1129 return log_error_errno(errno, "Failed to synchronize the ESP '%s': %m", arg_path);
1130
1131 return 1;
1132}
1133
2f2c539c 1134static int verb_install(int argc, char *argv[], void *userdata) {
0974a682 1135
2f2c539c
LP
1136 sd_id128_t uuid = SD_ID128_NULL;
1137 uint64_t pstart = 0, psize = 0;
1138 uint32_t part = 0;
1139 bool install;
1140 int r;
1141
5caa3167 1142 r = acquire_esp(false, &part, &pstart, &psize, &uuid);
2f2c539c
LP
1143 if (r < 0)
1144 return r;
1145
1146 install = streq(argv[0], "install");
1147
1148 RUN_WITH_UMASK(0002) {
1149 r = install_binaries(arg_path, install);
0974a682 1150 if (r < 0)
d3226d77 1151 return r;
0974a682 1152
2f2c539c 1153 if (install) {
d3226d77
ZJS
1154 r = install_loader_config(arg_path);
1155 if (r < 0)
1156 return r;
1157 }
2f2c539c 1158 }
0974a682 1159
e0e8d177
LP
1160 (void) sync_esp();
1161
2f2c539c
LP
1162 if (arg_touch_variables)
1163 r = install_variables(arg_path,
1164 part, pstart, psize, uuid,
1165 "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi",
1166 install);
7b4d7cc0 1167
2f2c539c
LP
1168 return r;
1169}
0974a682 1170
2f2c539c
LP
1171static int verb_remove(int argc, char *argv[], void *userdata) {
1172 sd_id128_t uuid = SD_ID128_NULL;
1173 int r;
0974a682 1174
5caa3167 1175 r = acquire_esp(false, NULL, NULL, NULL, &uuid);
2f2c539c
LP
1176 if (r < 0)
1177 return r;
1178
1179 r = remove_binaries(arg_path);
1180
e0e8d177
LP
1181 (void) sync_esp();
1182
2f2c539c
LP
1183 if (arg_touch_variables) {
1184 int q;
1185
1186 q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true);
1187 if (q < 0 && r == 0)
1188 r = q;
7b4d7cc0
KS
1189 }
1190
d3226d77 1191 return r;
7b4d7cc0
KS
1192}
1193
d88c96ff
LP
1194static int verb_set_default(int argc, char *argv[], void *userdata) {
1195 const char *name;
1196 int r;
1197
baaa35ad
ZJS
1198 if (!is_efi_boot())
1199 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1200 "Not booted with UEFI.");
d88c96ff
LP
1201
1202 if (access("/sys/firmware/efi/efivars/LoaderInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f", F_OK) < 0) {
1203 if (errno == ENOENT) {
1204 log_error_errno(errno, "Not booted with a supported boot loader.");
1205 return -EOPNOTSUPP;
1206 }
1207
1208 return log_error_errno(errno, "Failed to detect whether boot loader supports '%s' operation: %m", argv[0]);
1209 }
1210
baaa35ad
ZJS
1211 if (detect_container() > 0)
1212 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1213 "'%s' operation not supported in a container.",
1214 argv[0]);
d88c96ff 1215
baaa35ad
ZJS
1216 if (!arg_touch_variables)
1217 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1218 "'%s' operation cannot be combined with --touch-variables=no.",
1219 argv[0]);
d88c96ff
LP
1220
1221 name = streq(argv[0], "set-default") ? "LoaderEntryDefault" : "LoaderEntryOneShot";
1222
1223 if (isempty(argv[1])) {
1224 r = efi_set_variable(EFI_VENDOR_LOADER, name, NULL, 0);
1225 if (r < 0 && r != -ENOENT)
1226 return log_error_errno(r, "Failed to remove EFI variale: %m");
1227 } else {
1228 _cleanup_free_ char16_t *encoded = NULL;
1229
1230 encoded = utf8_to_utf16(argv[1], strlen(argv[1]));
1231 if (!encoded)
1232 return log_oom();
1233
1234 r = efi_set_variable(EFI_VENDOR_LOADER, name, encoded, char16_strlen(encoded) * 2 + 2);
1235 if (r < 0)
1236 return log_error_errno(r, "Failed to update EFI variable: %m");
1237 }
1238
1239 return 0;
1240}
1241
2f2c539c
LP
1242static int bootctl_main(int argc, char *argv[]) {
1243
1244 static const Verb verbs[] = {
d88c96ff
LP
1245 { "help", VERB_ANY, VERB_ANY, 0, help },
1246 { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
1247 { "install", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
1248 { "update", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
1249 { "remove", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_remove },
1250 { "list", VERB_ANY, 1, 0, verb_list },
1251 { "set-default", 2, 2, VERB_MUST_BE_ROOT, verb_set_default },
1252 { "set-oneshot", 2, 2, VERB_MUST_BE_ROOT, verb_set_default },
2f2c539c
LP
1253 {}
1254 };
1255
1256 return dispatch_verb(argc, argv, verbs, NULL);
1257}
1258
608f8ec9 1259static int run(int argc, char *argv[]) {
601185b4 1260 int r;
7b4d7cc0
KS
1261
1262 log_parse_environment();
1263 log_open();
1264
2f2c539c
LP
1265 /* If we run in a container, automatically turn of EFI file system access */
1266 if (detect_container() > 0)
1267 arg_touch_variables = false;
1268
7b4d7cc0 1269 r = parse_argv(argc, argv);
601185b4 1270 if (r <= 0)
608f8ec9 1271 return r;
57db6f18 1272
608f8ec9 1273 return bootctl_main(argc, argv);
7b4d7cc0 1274}
608f8ec9
YW
1275
1276DEFINE_MAIN_FUNCTION(run);