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