]> git.proxmox.com Git - systemd.git/blame - src/boot/bootctl.c
Merge tag 'upstream/229'
[systemd.git] / src / boot / bootctl.c
CommitLineData
663996b3
MS
1/***
2 This file is part of systemd.
3
e3bff60a
MP
4 Copyright 2013-2015 Kay Sievers
5 Copyright 2013 Lennart Poettering
663996b3
MS
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
e3bff60a 21#include <assert.h>
6300502b 22#include <blkid/blkid.h>
e3bff60a 23#include <ctype.h>
6300502b
MP
24#include <dirent.h>
25#include <errno.h>
e3bff60a 26#include <ftw.h>
6300502b
MP
27#include <getopt.h>
28#include <limits.h>
e3bff60a 29#include <stdbool.h>
6300502b
MP
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>
663996b3 37
db2df898 38#include "alloc-util.h"
6300502b 39#include "blkid-util.h"
4c89c718 40#include "dirent-util.h"
e3bff60a 41#include "efivars.h"
db2df898
MP
42#include "fd-util.h"
43#include "fileio.h"
44#include "locale-util.h"
e3bff60a 45#include "rm-rf.h"
db2df898 46#include "string-util.h"
6300502b 47#include "util.h"
e3bff60a
MP
48
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;
52 _cleanup_free_ char *t = NULL;
53 _cleanup_blkid_free_probe_ blkid_probe b = NULL;
54 int r;
55 const char *v, *t2;
56
57 if (statfs(p, &sfs) < 0)
58 return log_error_errno(errno, "Failed to check file system type of \"%s\": %m", p);
59
60 if (sfs.f_type != 0x4d44) {
61 log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
62 return -ENODEV;
63 }
64
65 if (stat(p, &st) < 0)
66 return log_error_errno(errno, "Failed to determine block device node of \"%s\": %m", p);
67
68 if (major(st.st_dev) == 0) {
69 log_error("Block device node of %p is invalid.", p);
70 return -ENODEV;
71 }
72
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);
77
78 if (st.st_dev == st2.st_dev) {
79 log_error("Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p);
80 return -ENODEV;
81 }
82
83 r = asprintf(&t, "/dev/block/%u:%u", major(st.st_dev), minor(st.st_dev));
84 if (r < 0)
85 return log_oom();
86
87 errno = 0;
88 b = blkid_new_probe_from_filename(t);
89 if (!b) {
90 if (errno == 0)
91 return log_oom();
92
93 return log_error_errno(errno, "Failed to open file system \"%s\": %m", p);
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) {
104 log_error("File system \"%s\" is ambigious.", p);
105 return -ENODEV;
106 } else if (r == 1) {
107 log_error("File system \"%s\" does not contain a label.", p);
108 return -ENODEV;
109 } else if (r != 0) {
110 r = errno ? -errno : -EIO;
111 return log_error_errno(r, "Failed to probe file system \"%s\": %m", p);
112 }
113
114 errno = 0;
115 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
116 if (r != 0) {
117 r = errno ? -errno : -EIO;
118 return log_error_errno(r, "Failed to probe file system type \"%s\": %m", p);
119 }
120
121 if (!streq(v, "vfat")) {
122 log_error("File system \"%s\" is not FAT.", p);
123 return -ENODEV;
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;
130 return log_error_errno(r, "Failed to probe partition scheme \"%s\": %m", p);
131 }
132
133 if (!streq(v, "gpt")) {
134 log_error("File system \"%s\" is not on a GPT partition table.", p);
135 return -ENODEV;
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;
142 return log_error_errno(r, "Failed to probe partition type UUID \"%s\": %m", p);
143 }
144
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;
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;
154 return log_error_errno(r, "Failed to probe partition entry UUID \"%s\": %m", p);
155 }
156
157 r = sd_id128_from_string(v, uuid);
158 if (r < 0) {
159 log_error("Partition \"%s\" has invalid UUID \"%s\".", p, v);
160 return -EIO;
161 }
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;
167 return log_error_errno(r, "Failed to probe partition number \"%s\": m", p);
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;
175 return log_error_errno(r, "Failed to probe partition offset \"%s\": %m", p);
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;
183 return log_error_errno(r, "Failed to probe partition size \"%s\": %m", p);
184 }
185 *psize = strtoul(v, NULL, 10);
186
187 return 0;
663996b3
MS
188}
189
e3bff60a
MP
190/* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */
191static int get_file_version(int fd, char **v) {
192 struct stat st;
193 char *buf;
194 const char *s, *e;
195 char *x = NULL;
196 int r = 0;
663996b3 197
e3bff60a
MP
198 assert(fd >= 0);
199 assert(v);
663996b3 200
e3bff60a
MP
201 if (fstat(fd, &st) < 0)
202 return -errno;
663996b3 203
e3bff60a
MP
204 if (st.st_size < 27)
205 return 0;
60f067b4 206
e3bff60a
MP
207 buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
208 if (buf == MAP_FAILED)
209 return -errno;
663996b3 210
e3bff60a
MP
211 s = memmem(buf, st.st_size - 8, "#### LoaderInfo: ", 17);
212 if (!s)
213 goto finish;
214 s += 17;
663996b3 215
e3bff60a
MP
216 e = memmem(s, st.st_size - (s - buf), " ####", 5);
217 if (!e || e - s < 3) {
218 log_error("Malformed version string.");
219 r = -EINVAL;
220 goto finish;
221 }
222
223 x = strndup(s, e - s);
224 if (!x) {
225 r = log_oom();
226 goto finish;
227 }
228 r = 1;
229
230finish:
231 munmap(buf, st.st_size);
232 *v = x;
233 return r;
234}
235
236static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) {
237 char *p;
238 _cleanup_closedir_ DIR *d = NULL;
239 struct dirent *de;
240 int r = 0, c = 0;
241
242 p = strjoina(esp_path, "/", path);
243 d = opendir(p);
244 if (!d) {
245 if (errno == ENOENT)
5eef597e 246 return 0;
663996b3 247
e3bff60a
MP
248 return log_error_errno(errno, "Failed to read \"%s\": %m", p);
249 }
250
4c89c718 251 FOREACH_DIRENT(de, d, break) {
e3bff60a
MP
252 _cleanup_close_ int fd = -1;
253 _cleanup_free_ char *v = NULL;
254
e3bff60a
MP
255 if (!endswith_no_case(de->d_name, ".efi"))
256 continue;
257
258 if (prefix && !startswith_no_case(de->d_name, prefix))
259 continue;
260
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);
264
265 r = get_file_version(fd, &v);
266 if (r < 0)
267 return r;
268 if (r > 0)
4c89c718 269 printf(" File: %s/%s/%s (%s)\n", draw_special_char(DRAW_TREE_RIGHT), path, de->d_name, v);
e3bff60a 270 else
4c89c718 271 printf(" File: %s/%s/%s\n", draw_special_char(DRAW_TREE_RIGHT), path, de->d_name);
e3bff60a
MP
272 c++;
273 }
274
275 return c;
276}
277
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)
287 log_error("systemd-boot not installed in ESP.");
288 else if (r < 0)
289 return r;
290
291 r = enumerate_binaries(esp_path, "EFI/Boot", "boot");
292 if (r == 0)
293 log_error("No default/fallback boot loader installed in ESP.");
294 else if (r < 0)
295 return r;
296
7035cd9e
MP
297 printf("\n");
298
e3bff60a
MP
299 return 0;
300}
301
302static int print_efi_option(uint16_t id, bool in_order) {
303 _cleanup_free_ char *title = NULL;
304 _cleanup_free_ char *path = NULL;
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)
311 return r;
312
313 /* print only configured entries with partition information */
314 if (!path || sd_id128_equal(partition, SD_ID128_NULL))
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));
4c89c718 323 printf(" File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), path);
e3bff60a
MP
324 printf("\n");
325
326 return 0;
327}
328
329static int status_variables(void) {
330 int n_options, n_order;
331 _cleanup_free_ uint16_t *options = NULL, *order = NULL;
332 int i;
333
334 if (!is_efi_boot()) {
335 log_notice("Not booted with EFI, not showing EFI variables.");
336 return 0;
337 }
338
339 n_options = efi_get_boot_options(&options);
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");
345
346 n_order = efi_get_boot_order(&order);
347 if (n_order == -ENOENT)
348 n_order = 0;
349 else if (n_order < 0)
350 return log_error_errno(n_order, "Failed to read EFI boot order.");
351
352 /* print entries in BootOrder first */
353 printf("Boot Loader Entries in EFI Variables:\n");
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;
360
361 for (j = 0; j < n_order; j++)
362 if (options[i] == order[j])
363 goto next;
364
365 print_efi_option(options[i], false);
366 next:
367 continue;
368 }
369
370 return 0;
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
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;
402 int r;
403
404 assert(fd >= 0);
405 assert(from);
406 assert(to);
407
408 r = get_file_version(fd, &a);
409 if (r < 0)
410 return r;
411 if (r == 0) {
412 log_error("Source file \"%s\" does not carry version information!", from);
413 return -EINVAL;
414 }
415
416 fd2 = open(to, O_RDONLY|O_CLOEXEC);
417 if (fd2 < 0) {
418 if (errno == ENOENT)
663996b3
MS
419 return 0;
420
e3bff60a
MP
421 return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", to);
422 }
663996b3 423
e3bff60a
MP
424 r = get_file_version(fd2, &b);
425 if (r < 0)
426 return r;
427 if (r == 0 || compare_product(a, b) != 0) {
428 log_notice("Skipping \"%s\", since it's owned by another boot loader.", to);
429 return -EEXIST;
430 }
431
432 if (compare_version(a, b) < 0) {
433 log_warning("Skipping \"%s\", since a newer boot loader version exists already.", to);
434 return -ESTALE;
435 }
436
437 return 0;
438}
439
440static int copy_file(const char *from, const char *to, bool force) {
441 _cleanup_fclose_ FILE *f = NULL, *g = NULL;
442 char *p;
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");
451 if (!f)
452 return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", from);
453
454 if (!force) {
455 /* If this is an update, then let's compare versions first */
456 r = version_check(fileno(f), from, to);
457 if (r < 0)
458 return r;
459 }
460
461 p = strjoina(to, "~");
462 g = fopen(p, "wxe");
463 if (!g) {
464 /* Directory doesn't exist yet? Then let's skip this... */
465 if (!force && errno == ENOENT)
466 return 0;
467
468 return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", to);
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)) {
478 r = log_error_errno(EIO, "Failed to read \"%s\": %m", from);
479 goto error;
663996b3 480 }
663996b3 481
e3bff60a
MP
482 if (k == 0)
483 break;
484
485 fwrite(buf, 1, k, g);
486 if (ferror(g)) {
487 r = log_error_errno(EIO, "Failed to write \"%s\": %m", to);
488 goto error;
489 }
490 } while (!feof(f));
491
5fd56512
MP
492 r = fflush_and_check(g);
493 if (r < 0) {
494 log_error_errno(r, "Failed to write \"%s\": %m", to);
e3bff60a
MP
495 goto error;
496 }
497
498 r = fstat(fileno(f), &st);
499 if (r < 0) {
500 r = log_error_errno(errno, "Failed to get file timestamps of \"%s\": %m", from);
501 goto error;
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) {
509 r = log_error_errno(errno, "Failed to set file timestamps on \"%s\": %m", p);
510 goto error;
511 }
512
513 if (rename(p, to) < 0) {
514 r = log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", p, to);
515 goto error;
516 }
517
518 log_info("Copied \"%s\" to \"%s\".", from, to);
519 return 0;
520
521error:
5fd56512 522 (void) unlink(p);
e3bff60a 523 return r;
663996b3
MS
524}
525
e3bff60a
MP
526static char* strupper(char *s) {
527 char *p;
663996b3 528
e3bff60a
MP
529 for (p = s; *p; p++)
530 *p = toupper(*p);
663996b3 531
e3bff60a
MP
532 return s;
533}
663996b3 534
e3bff60a
MP
535static int mkdir_one(const char *prefix, const char *suffix) {
536 char *p;
663996b3 537
e3bff60a
MP
538 p = strjoina(prefix, "/", suffix);
539 if (mkdir(p, 0700) < 0) {
540 if (errno != EEXIST)
541 return log_error_errno(errno, "Failed to create \"%s\": %m", p);
542 } else
543 log_info("Created \"%s\".", p);
663996b3 544
663996b3 545 return 0;
663996b3
MS
546}
547
e3bff60a
MP
548static const char *efi_subdirs[] = {
549 "EFI",
550 "EFI/systemd",
551 "EFI/Boot",
552 "loader",
553 "loader/entries"
554};
663996b3 555
e3bff60a
MP
556static int create_dirs(const char *esp_path) {
557 int r;
558 unsigned i;
559
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 }
565
566 return 0;
567}
568
569static int copy_one_file(const char *esp_path, const char *name, bool force) {
570 char *p, *q;
571 int r;
572
573 p = strjoina(BOOTLIBDIR "/", name);
574 q = strjoina(esp_path, "/EFI/systemd/", name);
575 r = copy_file(p, q, force);
576
577 if (startswith(name, "systemd-boot")) {
578 int k;
579 char *v;
580
581 /* Create the EFI default boot loader name (specified for removable devices) */
582 v = strjoina(esp_path, "/EFI/Boot/BOOT", name + strlen("systemd-boot"));
583 strupper(strrchr(v, '/') + 1);
584
585 k = copy_file(p, v, force);
586 if (k < 0 && r == 0)
587 r = k;
588 }
589
590 return r;
591}
592
593static int install_binaries(const char *esp_path, bool force) {
594 struct dirent *de;
595 _cleanup_closedir_ DIR *d = NULL;
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;
663996b3 607 }
e3bff60a
MP
608
609 d = opendir(BOOTLIBDIR);
610 if (!d)
611 return log_error_errno(errno, "Failed to open \""BOOTLIBDIR"\": %m");
612
4c89c718 613 FOREACH_DIRENT(de, d, break) {
e3bff60a
MP
614 int k;
615
e3bff60a
MP
616 if (!endswith_no_case(de->d_name, ".efi"))
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
624 return r;
625}
626
627static bool same_entry(uint16_t id, const sd_id128_t uuid, const char *path) {
628 _cleanup_free_ char *opath = NULL;
629 sd_id128_t ouuid;
630 int r;
631
632 r = efi_get_boot_option(id, NULL, &ouuid, &opath, NULL);
633 if (r < 0)
634 return false;
635 if (!sd_id128_equal(uuid, ouuid))
636 return false;
637 if (!streq_ptr(path, opath))
638 return false;
639
640 return true;
663996b3
MS
641}
642
e3bff60a
MP
643static int find_slot(sd_id128_t uuid, const char *path, uint16_t *id) {
644 _cleanup_free_ uint16_t *options = NULL;
645 int n, i;
646
647 n = efi_get_boot_options(&options);
648 if (n < 0)
649 return n;
650
651 /* find already existing systemd-boot entry */
652 for (i = 0; i < n; i++)
653 if (same_entry(options[i], uuid, path)) {
654 *id = options[i];
655 return 1;
656 }
657
658 /* find free slot in the sorted BootXXXX variable list */
659 for (i = 0; i < n; i++)
660 if (i != options[i]) {
661 *id = i;
662 return 1;
663 }
664
665 /* use the next one */
666 if (i == 0xffff)
667 return -ENOSPC;
668 *id = i;
669 return 0;
663996b3
MS
670}
671
e3bff60a
MP
672static int insert_into_order(uint16_t slot, bool first) {
673 _cleanup_free_ uint16_t *order = NULL;
674 uint16_t *t;
675 int n, i;
676
677 n = efi_get_boot_order(&order);
678 if (n <= 0)
679 /* no entry, add us */
680 return efi_set_boot_order(&slot, 1);
681
682 /* are we the first and only one? */
683 if (n == 1 && order[0] == slot)
684 return 0;
663996b3 685
e3bff60a
MP
686 /* are we already in the boot order? */
687 for (i = 0; i < n; i++) {
688 if (order[i] != slot)
689 continue;
690
691 /* we do not require to be the first one, all is fine */
692 if (!first)
693 return 0;
694
695 /* move us to the first slot */
696 memmove(order + 1, order, i * sizeof(uint16_t));
697 order[0] = slot;
698 return efi_set_boot_order(order, n);
699 }
700
701 /* extend array */
702 t = realloc(order, (n + 1) * sizeof(uint16_t));
703 if (!t)
663996b3 704 return -ENOMEM;
e3bff60a 705 order = t;
663996b3 706
e3bff60a
MP
707 /* add us to the top or end of the list */
708 if (first) {
709 memmove(order + 1, order, n * sizeof(uint16_t));
710 order[0] = slot;
711 } else
712 order[n] = slot;
713
714 return efi_set_boot_order(order, n + 1);
715}
716
717static int remove_from_order(uint16_t slot) {
718 _cleanup_free_ uint16_t *order = NULL;
719 int n, i;
663996b3 720
e3bff60a
MP
721 n = efi_get_boot_order(&order);
722 if (n <= 0)
723 return n;
724
725 for (i = 0; i < n; i++) {
726 if (order[i] != slot)
727 continue;
728
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);
732 }
733
734 return 0;
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) {
741 char *p;
742 uint16_t slot;
743 int r;
744
745 if (!is_efi_boot()) {
746 log_warning("Not booted with EFI, skipping EFI variable setup.");
747 return 0;
748 }
749
750 p = strjoina(esp_path, path);
751 if (access(p, F_OK) < 0) {
752 if (errno == ENOENT)
753 return 0;
663996b3 754 else
e3bff60a 755 return log_error_errno(errno, "Cannot access \"%s\": %m", p);
663996b3 756 }
663996b3 757
e3bff60a
MP
758 r = find_slot(uuid, path, &slot);
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");
764
765 if (first || r == false) {
766 r = efi_add_boot_option(slot, "Linux Boot Manager",
767 part, pstart, psize,
768 uuid, path);
769 if (r < 0)
770 return log_error_errno(r, "Failed to create EFI Boot variable entry: %m");
771
772 log_info("Created EFI boot entry \"Linux Boot Manager\".");
773 }
774
775 return insert_into_order(slot, first);
776}
777
778static int remove_boot_efi(const char *esp_path) {
779 char *p;
780 _cleanup_closedir_ DIR *d = NULL;
781 struct dirent *de;
782 int r, c = 0;
783
784 p = strjoina(esp_path, "/EFI/Boot");
785 d = opendir(p);
786 if (!d) {
787 if (errno == ENOENT)
788 return 0;
789
790 return log_error_errno(errno, "Failed to open directory \"%s\": %m", p);
791 }
792
4c89c718 793 FOREACH_DIRENT(de, d, break) {
e3bff60a
MP
794 _cleanup_close_ int fd = -1;
795 _cleanup_free_ char *v = NULL;
796
e3bff60a
MP
797 if (!endswith_no_case(de->d_name, ".efi"))
798 continue;
799
800 if (!startswith_no_case(de->d_name, "Boot"))
801 continue;
802
803 fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
86f210e9 804 if (fd < 0)
e3bff60a
MP
805 return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
806
807 r = get_file_version(fd, &v);
808 if (r < 0)
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
13d276d0 815 log_info("Removed \"%s/%s\".", p, de->d_name);
663996b3 816 }
e3bff60a
MP
817
818 c++;
819 }
820
821 return c;
822}
823
824static int rmdir_one(const char *prefix, const char *suffix) {
825 char *p;
826
827 p = strjoina(prefix, "/", suffix);
828 if (rmdir(p) < 0) {
829 if (!IN_SET(errno, ENOENT, ENOTEMPTY))
830 return log_error_errno(errno, "Failed to remove \"%s\": %m", p);
663996b3 831 } else
e3bff60a 832 log_info("Removed \"%s\".", p);
663996b3 833
e3bff60a 834 return 0;
663996b3
MS
835}
836
e3bff60a
MP
837static int remove_binaries(const char *esp_path) {
838 char *p;
839 int r, q;
840 unsigned i;
841
842 p = strjoina(esp_path, "/EFI/systemd");
843 r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
844
845 q = remove_boot_efi(esp_path);
846 if (q < 0 && r == 0)
847 r = q;
848
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 }
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)
874 return remove_from_order(slot);
875 else
876 return 0;
877}
878
879static int install_loader_config(const char *esp_path) {
880 char *p;
881 char line[64];
882 char *machine = NULL;
86f210e9 883 _cleanup_fclose_ FILE *f = NULL, *g = NULL;
e3bff60a
MP
884
885 f = fopen("/etc/machine-id", "re");
886 if (!f)
7035cd9e 887 return errno == ENOENT ? 0 : -errno;
e3bff60a
MP
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 }
e3bff60a
MP
898
899 if (!machine)
900 return -ESRCH;
901
902 p = strjoina(esp_path, "/loader/loader.conf");
86f210e9
MP
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))
e3bff60a
MP
908 return log_error_errno(EIO, "Failed to write \"%s\": %m", p);
909 }
910
911 return 0;
912}
913
914static int help(void) {
915 printf("%s [COMMAND] [OPTIONS...]\n"
916 "\n"
fb183854 917 "Install, update or remove the systemd-boot EFI boot manager.\n\n"
e3bff60a
MP
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"
86f210e9 923 "Commands:\n"
e3bff60a
MP
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",
928 program_invocation_short_name);
929
930 return 0;
931}
932
933static const char *arg_path = "/boot";
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,
663996b3
MS
941 };
942
e3bff60a
MP
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;
663996b3
MS
952
953 assert(argc >= 0);
954 assert(argv);
955
e3bff60a
MP
956 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
957 switch (c) {
663996b3 958
e3bff60a 959 case 'h':
663996b3
MS
960 help();
961 return 0;
e3bff60a
MP
962
963 case ARG_VERSION:
6300502b 964 return version();
e3bff60a
MP
965
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:
978 assert_not_reached("Unknown option");
663996b3
MS
979 }
980
e3bff60a
MP
981 return 1;
982}
983
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}
663996b3 991
e3bff60a
MP
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;
1011 uint64_t pstart = 0, psize = 0;
1012 int r, q;
1013
1014 if (argv[optind]) {
1015 unsigned i;
1016
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 }
663996b3 1023 if (i >= ELEMENTSOF(verbs)) {
e3bff60a 1024 log_error("Unknown operation \"%s\"", argv[optind]);
663996b3
MS
1025 return -EINVAL;
1026 }
1027 }
1028
e3bff60a
MP
1029 if (geteuid() != 0)
1030 return log_error_errno(EPERM, "Need to be root.");
663996b3 1031
e3bff60a
MP
1032 r = verify_esp(arg_path, &part, &pstart, &psize, &uuid);
1033 if (r == -ENODEV && !arg_path)
1034 log_notice("You might want to use --path= to indicate the path to your ESP, in case it is not mounted on /boot.");
1035 if (r < 0)
1036 return r;
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
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));
1075 if (!sd_id128_equal(loader_part_uuid, SD_ID128_NULL))
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");
1080 printf(" File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), strna(loader_path));
1081 printf("\n");
1082 } else
1083 printf("System:\n Not booted with EFI\n");
1084
1085 r = status_binaries(arg_path, uuid);
1086 if (r < 0)
1087 return r;
1088
1089 if (arg_touch_variables)
1090 r = status_variables();
663996b3 1091 break;
e3bff60a 1092 }
663996b3 1093
e3bff60a
MP
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)
1100 return r;
1101
1102 if (arg_action == ACTION_INSTALL) {
1103 r = install_loader_config(arg_path);
1104 if (r < 0)
1105 return r;
663996b3 1106 }
e3bff60a
MP
1107
1108 if (arg_touch_variables)
1109 r = install_variables(arg_path,
1110 part, pstart, psize, uuid,
1111 "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi",
1112 arg_action == ACTION_INSTALL);
663996b3
MS
1113 break;
1114
e3bff60a
MP
1115 case ACTION_REMOVE:
1116 r = remove_binaries(arg_path);
1117
1118 if (arg_touch_variables) {
1119 q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true);
1120 if (q < 0 && r == 0)
1121 r = q;
663996b3
MS
1122 }
1123 break;
663996b3
MS
1124 }
1125
e3bff60a 1126 return r;
663996b3
MS
1127}
1128
1129int main(int argc, char *argv[]) {
5eef597e 1130 int r;
663996b3
MS
1131
1132 log_parse_environment();
1133 log_open();
1134
1135 r = parse_argv(argc, argv);
5eef597e 1136 if (r <= 0)
663996b3 1137 goto finish;
663996b3
MS
1138
1139 r = bootctl_main(argc, argv);
5eef597e
MP
1140
1141 finish:
1142 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
663996b3 1143}