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