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