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