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