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