]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/boot/bootctl.c
catalog: update Russian translation (#7678)
[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) */
fbd0b64f
LP
500 v = strjoina(esp_path, "/EFI/BOOT/BOOT",
501 name + STRLEN("systemd-boot"));
846b8fc3 502 ascii_strupper(strrchr(v, '/') + 1);
0974a682 503
175d308c 504 k = copy_file_with_version_check(p, v, force);
0974a682 505 if (k < 0 && r == 0)
d3226d77 506 r = k;
0974a682
KS
507 }
508
509 return r;
7b4d7cc0
KS
510}
511
0974a682
KS
512static int install_binaries(const char *esp_path, bool force) {
513 struct dirent *de;
d3226d77 514 _cleanup_closedir_ DIR *d = NULL;
0974a682
KS
515 int r = 0;
516
517 if (force) {
518 /* Don't create any of these directories when we are
519 * just updating. When we update we'll drop-in our
520 * files (unless there are newer ones already), but we
521 * won't create the directories for them in the first
522 * place. */
523 r = create_dirs(esp_path);
524 if (r < 0)
525 return r;
526 }
527
e7dd673d 528 d = opendir(BOOTLIBDIR);
d3226d77
ZJS
529 if (!d)
530 return log_error_errno(errno, "Failed to open \""BOOTLIBDIR"\": %m");
0974a682 531
e41256dc 532 FOREACH_DIRENT(de, d, break) {
0974a682
KS
533 int k;
534
d3226d77 535 if (!endswith_no_case(de->d_name, ".efi"))
0974a682
KS
536 continue;
537
538 k = copy_one_file(esp_path, de->d_name, force);
539 if (k < 0 && r == 0)
540 r = k;
541 }
542
0974a682 543 return r;
7b4d7cc0
KS
544}
545
0974a682 546static bool same_entry(uint16_t id, const sd_id128_t uuid, const char *path) {
d3226d77 547 _cleanup_free_ char *opath = NULL;
0974a682 548 sd_id128_t ouuid;
d3226d77 549 int r;
7b4d7cc0 550
d3226d77
ZJS
551 r = efi_get_boot_option(id, NULL, &ouuid, &opath, NULL);
552 if (r < 0)
0974a682
KS
553 return false;
554 if (!sd_id128_equal(uuid, ouuid))
d3226d77 555 return false;
0974a682 556 if (!streq_ptr(path, opath))
d3226d77 557 return false;
0974a682 558
d3226d77 559 return true;
0974a682
KS
560}
561
562static int find_slot(sd_id128_t uuid, const char *path, uint16_t *id) {
d3226d77
ZJS
563 _cleanup_free_ uint16_t *options = NULL;
564 int n, i;
0974a682 565
d3226d77
ZJS
566 n = efi_get_boot_options(&options);
567 if (n < 0)
568 return n;
0974a682 569
e7dd673d 570 /* find already existing systemd-boot entry */
d3226d77 571 for (i = 0; i < n; i++)
0974a682 572 if (same_entry(options[i], uuid, path)) {
d3226d77
ZJS
573 *id = options[i];
574 return 1;
0974a682
KS
575 }
576
577 /* find free slot in the sorted BootXXXX variable list */
d3226d77 578 for (i = 0; i < n; i++)
0974a682 579 if (i != options[i]) {
d3226d77
ZJS
580 *id = i;
581 return 1;
0974a682
KS
582 }
583
584 /* use the next one */
585 if (i == 0xffff)
586 return -ENOSPC;
d3226d77
ZJS
587 *id = i;
588 return 0;
0974a682
KS
589}
590
591static int insert_into_order(uint16_t slot, bool first) {
d3226d77
ZJS
592 _cleanup_free_ uint16_t *order = NULL;
593 uint16_t *t;
594 int n, i;
0974a682 595
d3226d77
ZJS
596 n = efi_get_boot_order(&order);
597 if (n <= 0)
0974a682 598 /* no entry, add us */
d3226d77 599 return efi_set_boot_order(&slot, 1);
0974a682
KS
600
601 /* are we the first and only one? */
d3226d77
ZJS
602 if (n == 1 && order[0] == slot)
603 return 0;
0974a682
KS
604
605 /* are we already in the boot order? */
d3226d77 606 for (i = 0; i < n; i++) {
0974a682
KS
607 if (order[i] != slot)
608 continue;
609
610 /* we do not require to be the first one, all is fine */
611 if (!first)
d3226d77 612 return 0;
0974a682
KS
613
614 /* move us to the first slot */
d3226d77 615 memmove(order + 1, order, i * sizeof(uint16_t));
0974a682 616 order[0] = slot;
d3226d77 617 return efi_set_boot_order(order, n);
0974a682
KS
618 }
619
620 /* extend array */
d3226d77
ZJS
621 t = realloc(order, (n + 1) * sizeof(uint16_t));
622 if (!t)
623 return -ENOMEM;
624 order = t;
0974a682
KS
625
626 /* add us to the top or end of the list */
627 if (first) {
d3226d77 628 memmove(order + 1, order, n * sizeof(uint16_t));
0974a682
KS
629 order[0] = slot;
630 } else
d3226d77 631 order[n] = slot;
0974a682 632
d3226d77 633 return efi_set_boot_order(order, n + 1);
0974a682
KS
634}
635
636static int remove_from_order(uint16_t slot) {
7cb0f263 637 _cleanup_free_ uint16_t *order = NULL;
d3226d77 638 int n, i;
0974a682 639
d3226d77
ZJS
640 n = efi_get_boot_order(&order);
641 if (n <= 0)
642 return n;
0974a682 643
d3226d77 644 for (i = 0; i < n; i++) {
0974a682
KS
645 if (order[i] != slot)
646 continue;
647
d3226d77
ZJS
648 if (i + 1 < n)
649 memmove(order + i, order + i+1, (n - i) * sizeof(uint16_t));
650 return efi_set_boot_order(order, n - 1);
0974a682
KS
651 }
652
d3226d77 653 return 0;
0974a682
KS
654}
655
656static int install_variables(const char *esp_path,
657 uint32_t part, uint64_t pstart, uint64_t psize,
658 sd_id128_t uuid, const char *path,
659 bool first) {
d3226d77 660 char *p;
0974a682
KS
661 uint16_t slot;
662 int r;
663
664 if (!is_efi_boot()) {
d3226d77 665 log_warning("Not booted with EFI, skipping EFI variable setup.");
0974a682
KS
666 return 0;
667 }
668
d3226d77 669 p = strjoina(esp_path, path);
0974a682
KS
670 if (access(p, F_OK) < 0) {
671 if (errno == ENOENT)
d3226d77 672 return 0;
f939cff7
LP
673
674 return log_error_errno(errno, "Cannot access \"%s\": %m", p);
0974a682 675 }
7b4d7cc0 676
0974a682 677 r = find_slot(uuid, path, &slot);
d3226d77
ZJS
678 if (r < 0)
679 return log_error_errno(r,
680 r == -ENOENT ?
681 "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" :
682 "Failed to determine current boot order: %m");
7b4d7cc0 683
181ccb43 684 if (first || r == 0) {
0974a682
KS
685 r = efi_add_boot_option(slot, "Linux Boot Manager",
686 part, pstart, psize,
687 uuid, path);
d3226d77
ZJS
688 if (r < 0)
689 return log_error_errno(r, "Failed to create EFI Boot variable entry: %m");
0974a682 690
d3226d77
ZJS
691 log_info("Created EFI boot entry \"Linux Boot Manager\".");
692 }
0974a682 693
d3226d77 694 return insert_into_order(slot, first);
0974a682
KS
695}
696
697static int remove_boot_efi(const char *esp_path) {
d3226d77
ZJS
698 char *p;
699 _cleanup_closedir_ DIR *d = NULL;
0974a682 700 struct dirent *de;
d3226d77 701 int r, c = 0;
0974a682 702
00f69504 703 p = strjoina(esp_path, "/EFI/BOOT");
0974a682
KS
704 d = opendir(p);
705 if (!d) {
d3226d77
ZJS
706 if (errno == ENOENT)
707 return 0;
0974a682 708
d3226d77 709 return log_error_errno(errno, "Failed to open directory \"%s\": %m", p);
0974a682
KS
710 }
711
e41256dc 712 FOREACH_DIRENT(de, d, break) {
d3226d77
ZJS
713 _cleanup_close_ int fd = -1;
714 _cleanup_free_ char *v = NULL;
0974a682 715
d3226d77 716 if (!endswith_no_case(de->d_name, ".efi"))
0974a682
KS
717 continue;
718
b7536c45 719 if (!startswith_no_case(de->d_name, "boot"))
0974a682
KS
720 continue;
721
d3226d77 722 fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
dd114e11 723 if (fd < 0)
d3226d77 724 return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
0974a682 725
d3226d77 726 r = get_file_version(fd, &v);
0974a682 727 if (r < 0)
d3226d77
ZJS
728 return r;
729 if (r > 0 && startswith(v, "systemd-boot ")) {
730 r = unlinkat(dirfd(d), de->d_name, 0);
731 if (r < 0)
732 return log_error_errno(errno, "Failed to remove \"%s/%s\": %m", p, de->d_name);
733
a592ab6a 734 log_info("Removed \"%s/%s\".", p, de->d_name);
0974a682
KS
735 }
736
737 c++;
0974a682
KS
738 }
739
d3226d77 740 return c;
0974a682
KS
741}
742
743static int rmdir_one(const char *prefix, const char *suffix) {
744 char *p;
7b4d7cc0 745
d3226d77 746 p = strjoina(prefix, "/", suffix);
0974a682 747 if (rmdir(p) < 0) {
d3226d77
ZJS
748 if (!IN_SET(errno, ENOENT, ENOTEMPTY))
749 return log_error_errno(errno, "Failed to remove \"%s\": %m", p);
7b4d7cc0 750 } else
d3226d77 751 log_info("Removed \"%s\".", p);
7b4d7cc0 752
0974a682 753 return 0;
7b4d7cc0
KS
754}
755
0974a682
KS
756static int remove_binaries(const char *esp_path) {
757 char *p;
758 int r, q;
d3226d77 759 unsigned i;
0974a682 760
d3226d77 761 p = strjoina(esp_path, "/EFI/systemd");
c6878637 762 r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
0974a682
KS
763
764 q = remove_boot_efi(esp_path);
765 if (q < 0 && r == 0)
766 r = q;
767
88a00ac5 768 for (i = ELEMENTSOF(efi_subdirs)-1; i > 0; i--) {
d3226d77
ZJS
769 q = rmdir_one(esp_path, efi_subdirs[i-1]);
770 if (q < 0 && r == 0)
771 r = q;
772 }
0974a682
KS
773
774 return r;
775}
776
777static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) {
778 uint16_t slot;
779 int r;
780
781 if (!is_efi_boot())
782 return 0;
783
784 r = find_slot(uuid, path, &slot);
785 if (r != 1)
786 return 0;
787
788 r = efi_remove_boot_option(slot);
789 if (r < 0)
790 return r;
791
792 if (in_order)
d3226d77 793 return remove_from_order(slot);
f939cff7
LP
794
795 return 0;
0974a682
KS
796}
797
798static int install_loader_config(const char *esp_path) {
0974a682 799
d5ff6d6d 800 char machine_string[SD_ID128_STRING_MAX];
f5b84de2
LP
801 _cleanup_(unlink_and_freep) char *t = NULL;
802 _cleanup_fclose_ FILE *f = NULL;
d5ff6d6d
LP
803 sd_id128_t machine_id;
804 const char *p;
f5b84de2 805 int r, fd;
0974a682 806
d5ff6d6d
LP
807 r = sd_id128_get_machine(&machine_id);
808 if (r < 0)
0675e94a 809 return log_error_errno(r, "Failed to get machine id: %m");
0974a682 810
d5ff6d6d 811 p = strjoina(esp_path, "/loader/loader.conf");
f5b84de2
LP
812
813 if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */
814 return 0;
815
816 fd = open_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t);
817 if (fd < 0)
818 return log_error_errno(fd, "Failed to open \"%s\" for writing: %m", p);
819
820 f = fdopen(fd, "we");
821 if (!f) {
822 safe_close(fd);
823 return log_oom();
824 }
0974a682 825
d5ff6d6d
LP
826 fprintf(f, "#timeout 3\n");
827 fprintf(f, "default %s-*\n", sd_id128_to_string(machine_id, machine_string));
0974a682 828
0675e94a 829 r = fflush_sync_and_check(f);
d5ff6d6d
LP
830 if (r < 0)
831 return log_error_errno(r, "Failed to write \"%s\": %m", p);
0974a682 832
f5b84de2
LP
833 r = link_tmpfile(fd, t, p);
834 if (r == -EEXIST)
835 return 0; /* Silently skip creation if the file exists now (recheck) */
836 if (r < 0)
837 return log_error_errno(r, "Failed to move \"%s\" into place: %m", p);
838
839 t = mfree(t);
840
841 return 1;
0974a682
KS
842}
843
2f2c539c
LP
844static int help(int argc, char *argv[], void *userdata) {
845
0974a682
KS
846 printf("%s [COMMAND] [OPTIONS...]\n"
847 "\n"
68cc17f1 848 "Install, update or remove the systemd-boot EFI boot manager.\n\n"
0974a682
KS
849 " -h --help Show this help\n"
850 " --version Print version\n"
851 " --path=PATH Path to the EFI System Partition (ESP)\n"
30b50477 852 " -p --print-path Print path to the EFI partition\n"
0974a682
KS
853 " --no-variables Don't touch EFI variables\n"
854 "\n"
71744250 855 "Commands:\n"
e7dd673d 856 " status Show status of installed systemd-boot and EFI variables\n"
7e87c7d9 857 " list List boot entries\n"
e7dd673d
TG
858 " install Install systemd-boot to the ESP and EFI variables\n"
859 " update Update systemd-boot in the ESP and EFI variables\n"
860 " remove Remove systemd-boot from the ESP and EFI variables\n",
0974a682
KS
861 program_invocation_short_name);
862
863 return 0;
864}
865
0974a682
KS
866static int parse_argv(int argc, char *argv[]) {
867 enum {
868 ARG_PATH = 0x100,
869 ARG_VERSION,
870 ARG_NO_VARIABLES,
7b4d7cc0
KS
871 };
872
0974a682
KS
873 static const struct option options[] = {
874 { "help", no_argument, NULL, 'h' },
875 { "version", no_argument, NULL, ARG_VERSION },
876 { "path", required_argument, NULL, ARG_PATH },
30b50477 877 { "print-path", no_argument, NULL, 'p' },
0974a682
KS
878 { "no-variables", no_argument, NULL, ARG_NO_VARIABLES },
879 { NULL, 0, NULL, 0 }
880 };
881
2f2c539c 882 int c, r;
7b4d7cc0
KS
883
884 assert(argc >= 0);
885 assert(argv);
886
30b50477 887 while ((c = getopt_long(argc, argv, "hp", options, NULL)) >= 0)
0974a682 888 switch (c) {
7b4d7cc0 889
0974a682 890 case 'h':
2f2c539c 891 help(0, NULL, NULL);
7b4d7cc0 892 return 0;
7b4d7cc0 893
0974a682 894 case ARG_VERSION:
3f6fd1ba 895 return version();
7b4d7cc0 896
0974a682 897 case ARG_PATH:
2f2c539c
LP
898 r = free_and_strdup(&arg_path, optarg);
899 if (r < 0)
900 return log_oom();
0974a682
KS
901 break;
902
30b50477
ZJS
903 case 'p':
904 arg_print_path = true;
905 break;
906
0974a682
KS
907 case ARG_NO_VARIABLES:
908 arg_touch_variables = false;
909 break;
910
911 case '?':
912 return -EINVAL;
913
914 default:
d3226d77 915 assert_not_reached("Unknown option");
7b4d7cc0 916 }
7b4d7cc0 917
0974a682
KS
918 return 1;
919}
7b4d7cc0 920
551710cf
ZJS
921static void read_loader_efi_var(const char *name, char **var) {
922 int r;
923
924 r = efi_get_variable_string(EFI_VENDOR_LOADER, name, var);
925 if (r < 0 && r != -ENOENT)
926 log_warning_errno(r, "Failed to read EFI variable %s: %m", name);
927}
928
2f2c539c 929static int verb_status(int argc, char *argv[], void *userdata) {
0974a682 930
2f2c539c 931 sd_id128_t uuid = SD_ID128_NULL;
46fb255b 932 int r, k;
0974a682 933
5caa3167 934 r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &uuid);
0974a682 935
30b50477 936 if (arg_print_path) {
5caa3167
LP
937 if (r == -EACCES) /* If we couldn't acquire the ESP path, log about access errors (which is the only
938 * error the find_esp_and_warn() won't log on its own) */
939 return log_error_errno(r, "Failed to determine ESP: %m");
30b50477
ZJS
940 if (r < 0)
941 return r;
942
943 puts(arg_path);
944 return 0;
945 }
551710cf 946
5caa3167
LP
947 r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just show what we
948 * can show */
949
2f2c539c 950 if (is_efi_boot()) {
25579a43
LP
951 _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL;
952 sd_id128_t loader_part_uuid = SD_ID128_NULL;
0974a682 953
2f2c539c
LP
954 read_loader_efi_var("LoaderFirmwareType", &fw_type);
955 read_loader_efi_var("LoaderFirmwareInfo", &fw_info);
956 read_loader_efi_var("LoaderInfo", &loader);
957 read_loader_efi_var("LoaderImageIdentifier", &loader_path);
551710cf 958
2f2c539c
LP
959 if (loader_path)
960 efi_tilt_backslashes(loader_path);
961
46fb255b
ZJS
962 k = efi_loader_get_device_part_uuid(&loader_part_uuid);
963 if (k < 0 && k != -ENOENT)
964 r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m");
2f2c539c
LP
965
966 printf("System:\n");
967 printf(" Firmware: %s (%s)\n", strna(fw_type), strna(fw_info));
968
46fb255b
ZJS
969 k = is_efi_secure_boot();
970 if (k < 0)
971 r = log_warning_errno(k, "Failed to query secure boot status: %m");
2f2c539c 972 else
46fb255b 973 printf(" Secure Boot: %sd\n", enable_disable(k));
0974a682 974
46fb255b
ZJS
975 k = is_efi_secure_boot_setup_mode();
976 if (k < 0)
977 r = log_warning_errno(k, "Failed to query secure boot mode: %m");
2f2c539c 978 else
46fb255b 979 printf(" Setup Mode: %s\n", k ? "setup" : "user");
2f2c539c
LP
980 printf("\n");
981
cd2d4c7f 982 printf("Current Loader:\n");
2f2c539c 983 printf(" Product: %s\n", strna(loader));
e28973ee 984 if (!sd_id128_is_null(loader_part_uuid))
cd2d4c7f 985 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
986 SD_ID128_FORMAT_VAL(loader_part_uuid));
987 else
cd2d4c7f 988 printf(" ESP: n/a\n");
2f2c539c
LP
989 printf(" File: %s%s\n", special_glyph(TREE_RIGHT), strna(loader_path));
990 printf("\n");
991 } else
cd2d4c7f 992 printf("System:\n Not booted with EFI\n\n");
7b4d7cc0 993
ecec2a5d
LP
994 if (arg_path) {
995 k = status_binaries(arg_path, uuid);
996 if (k < 0)
997 r = k;
998 }
2f2c539c 999
cd2d4c7f 1000 if (is_efi_boot()) {
46fb255b
ZJS
1001 k = status_variables();
1002 if (k < 0)
1003 r = k;
cd2d4c7f 1004 }
0974a682 1005
ecec2a5d
LP
1006 if (arg_path) {
1007 k = status_entries(arg_path, uuid);
1008 if (k < 0)
1009 r = k;
1010 }
7e87c7d9 1011
46fb255b 1012 return r;
2f2c539c 1013}
7b4d7cc0 1014
7e87c7d9 1015static int verb_list(int argc, char *argv[], void *userdata) {
5caa3167 1016 _cleanup_(boot_config_free) BootConfig config = {};
7e87c7d9 1017 sd_id128_t uuid = SD_ID128_NULL;
7e87c7d9 1018 unsigned n;
5caa3167 1019 int r;
7e87c7d9 1020
5caa3167
LP
1021 /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn
1022 * off logging about access errors and turn off potentially privileged device probing. Here we're interested in
1023 * the latter but not the former, hence request the mode, and log about EACCES. */
7e87c7d9 1024
5caa3167
LP
1025 r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &uuid);
1026 if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */
1027 return log_error_errno(r, "Failed to determine ESP: %m");
7e87c7d9
ZJS
1028 if (r < 0)
1029 return r;
1030
1031 r = boot_entries_load_config(arg_path, &config);
1032 if (r < 0)
1033 return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m",
1034 arg_path);
1035
1036 printf("Available boot entries:\n");
1037
1038 for (n = 0; n < config.n_entries; n++) {
1039 const BootEntry *e = &config.entries[n];
1040
1041 printf(" title: %s%s%s%s%s%s\n",
1042 ansi_highlight(),
64f05708 1043 boot_entry_title(e),
7e87c7d9
ZJS
1044 ansi_normal(),
1045 ansi_highlight_green(),
1046 n == config.default_entry ? " (default)" : "",
1047 ansi_normal());
1048 if (e->version)
1049 printf(" version: %s\n", e->version);
1050 if (e->machine_id)
1051 printf(" machine-id: %s\n", e->machine_id);
1052 if (e->architecture)
1053 printf(" architecture: %s\n", e->architecture);
1054 if (e->kernel)
1055 printf(" linux: %s\n", e->kernel);
1056 if (!strv_isempty(e->initrd)) {
1057 _cleanup_free_ char *t;
1058
1059 t = strv_join(e->initrd, " ");
1060 if (!t)
1061 return log_oom();
1062
1063 printf(" initrd: %s\n", t);
1064 }
1065 if (!strv_isempty(e->options)) {
1066 _cleanup_free_ char *t;
1067
1068 t = strv_join(e->options, " ");
1069 if (!t)
1070 return log_oom();
1071
1072 printf(" options: %s\n", t);
1073 }
1074 if (e->device_tree)
1075 printf(" devicetree: %s\n", e->device_tree);
1076
1077 puts("");
cd2d4c7f 1078 }
0974a682 1079
7e87c7d9 1080 return 0;
2f2c539c 1081}
7b4d7cc0 1082
2f2c539c 1083static int verb_install(int argc, char *argv[], void *userdata) {
0974a682 1084
2f2c539c
LP
1085 sd_id128_t uuid = SD_ID128_NULL;
1086 uint64_t pstart = 0, psize = 0;
1087 uint32_t part = 0;
1088 bool install;
1089 int r;
1090
5caa3167 1091 r = acquire_esp(false, &part, &pstart, &psize, &uuid);
2f2c539c
LP
1092 if (r < 0)
1093 return r;
1094
1095 install = streq(argv[0], "install");
1096
1097 RUN_WITH_UMASK(0002) {
1098 r = install_binaries(arg_path, install);
0974a682 1099 if (r < 0)
d3226d77 1100 return r;
0974a682 1101
2f2c539c 1102 if (install) {
d3226d77
ZJS
1103 r = install_loader_config(arg_path);
1104 if (r < 0)
1105 return r;
1106 }
2f2c539c 1107 }
0974a682 1108
2f2c539c
LP
1109 if (arg_touch_variables)
1110 r = install_variables(arg_path,
1111 part, pstart, psize, uuid,
1112 "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi",
1113 install);
7b4d7cc0 1114
2f2c539c
LP
1115 return r;
1116}
0974a682 1117
2f2c539c
LP
1118static int verb_remove(int argc, char *argv[], void *userdata) {
1119 sd_id128_t uuid = SD_ID128_NULL;
1120 int r;
0974a682 1121
5caa3167 1122 r = acquire_esp(false, NULL, NULL, NULL, &uuid);
2f2c539c
LP
1123 if (r < 0)
1124 return r;
1125
1126 r = remove_binaries(arg_path);
1127
1128 if (arg_touch_variables) {
1129 int q;
1130
1131 q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true);
1132 if (q < 0 && r == 0)
1133 r = q;
7b4d7cc0
KS
1134 }
1135
d3226d77 1136 return r;
7b4d7cc0
KS
1137}
1138
2f2c539c
LP
1139static int bootctl_main(int argc, char *argv[]) {
1140
1141 static const Verb verbs[] = {
c56d1e2c
CW
1142 { "help", VERB_ANY, VERB_ANY, 0, help },
1143 { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
1144 { "list", VERB_ANY, 1, 0, verb_list },
1145 { "install", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
1146 { "update", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
1147 { "remove", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_remove },
2f2c539c
LP
1148 {}
1149 };
1150
1151 return dispatch_verb(argc, argv, verbs, NULL);
1152}
1153
7b4d7cc0 1154int main(int argc, char *argv[]) {
601185b4 1155 int r;
7b4d7cc0
KS
1156
1157 log_parse_environment();
1158 log_open();
1159
2f2c539c
LP
1160 /* If we run in a container, automatically turn of EFI file system access */
1161 if (detect_container() > 0)
1162 arg_touch_variables = false;
1163
7b4d7cc0 1164 r = parse_argv(argc, argv);
601185b4 1165 if (r <= 0)
7b4d7cc0 1166 goto finish;
7b4d7cc0
KS
1167
1168 r = bootctl_main(argc, argv);
601185b4
ZJS
1169
1170 finish:
2f2c539c 1171 free(arg_path);
601185b4 1172 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
7b4d7cc0 1173}