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