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