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