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