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