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