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