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