bootctl.c revision 7cb0f263adacbd12f5be4f7f23d7f596485adb37
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013-2015 Kay Sievers
Copyright 2013 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdio.h>
#include <getopt.h>
#include <errno.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <ctype.h>
#include <limits.h>
#include <ftw.h>
#include <stdbool.h>
#include "efivars.h"
#include "build.h"
#include "util.h"
static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid) {
char *t;
blkid_probe b = NULL;
int r;
const char *v;
return -errno;
}
return -ENODEV;
}
return -errno;
}
return -ENODEV;
}
r = asprintf(&t, "%s/..", p);
if (r < 0) {
return -ENOMEM;
}
free(t);
if (r < 0) {
return -errno;
}
fprintf(stderr, "Directory %s is not the root of the EFI System Partition (ESP) file system.\n", p);
return -ENODEV;
}
if (r < 0) {
return -ENOMEM;
}
errno = 0;
b = blkid_new_probe_from_filename(t);
free(t);
if (!b) {
if (errno != 0) {
return -errno;
}
return -ENOMEM;
}
errno = 0;
r = blkid_do_safeprobe(b);
if (r == -2) {
r = -ENODEV;
goto fail;
} else if (r == 1) {
r = -ENODEV;
goto fail;
} else if (r != 0) {
goto fail;
}
errno = 0;
if (r != 0) {
goto fail;
}
if (strcmp(v, "vfat") != 0) {
fprintf(stderr, "File system %s is not a FAT EFI System Partition (ESP) file system after all.\n", p);
r = -ENODEV;
goto fail;
}
errno = 0;
if (r != 0) {
goto fail;
}
if (strcmp(v, "gpt") != 0) {
r = -ENODEV;
goto fail;
}
errno = 0;
if (r != 0) {
goto fail;
}
if (strcmp(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b") != 0) {
r = -ENODEV;
goto fail;
}
errno = 0;
if (r != 0) {
goto fail;
}
sd_id128_from_string(v, uuid);
errno = 0;
if (r != 0) {
goto fail;
}
errno = 0;
if (r != 0) {
goto fail;
}
errno = 0;
if (r != 0) {
goto fail;
}
blkid_free_probe(b);
return 0;
fail:
if (b)
blkid_free_probe(b);
return r;
}
/* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */
static int get_file_version(FILE *f, char **v) {
char *buf;
const char *s, *e;
char *x = NULL;
int r = 0;
assert(f);
assert(v);
return -errno;
return 0;
if (buf == MAP_FAILED)
return -errno;
if (!s)
goto finish;
s += 17;
if (!e || e - s < 3) {
r = -EINVAL;
goto finish;
}
x = strndup(s, e - s);
if (!x) {
r = -ENOMEM;
goto finish;
}
r = 1;
*v = x;
return r;
}
int r = 0, c = 0;
r = -ENOMEM;
goto finish;
}
d = opendir(p);
if (!d) {
r = 0;
goto finish;
}
r = -errno;
goto finish;
}
char *v;
size_t n;
FILE *f;
continue;
continue;
continue;
free(q);
q = NULL;
r = -ENOMEM;
goto finish;
}
f = fopen(q, "re");
if (!f) {
r = -errno;
goto finish;
}
r = get_file_version(f, &v);
fclose(f);
if (r < 0)
goto finish;
if (r > 0)
else
c++;
free(v);
}
r = c;
if (d)
closedir(d);
free(p);
free(q);
return r;
}
int r;
printf("Boot Loader Binaries:\n");
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));
if (r == 0)
else if (r < 0)
return r;
if (r == 0)
else if (r < 0)
return r;
printf("\n");
return 0;
}
bool active;
int r = 0;
if (r < 0)
return r;
/* print only configured entries with partition information */
return 0;
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));
printf("\n");
return 0;
}
static int status_variables(void) {
int r, i;
if (!is_efi_boot()) {
return 0;
}
if (n_options < 0) {
else
r = n_options;
goto finish;
}
printf("Boot Loader Entries in EFI Variables:\n");
n_order = 0;
} else if (n_order < 0) {
r = n_order;
goto finish;
}
/* print entries in BootOrder first */
for (i = 0; i < n_order; i++)
print_efi_option(order[i], true);
/* print remaining entries */
for (i = 0; i < n_options; i++) {
int j;
bool found = false;
for (j = 0; j < n_order; j++)
found = true;
break;
}
if (found)
continue;
print_efi_option(options[i], false);
}
r = 0;
return r;
}
static int compare_product(const char *a, const char *b) {
size_t x, y;
assert(a);
assert(b);
x = strcspn(a, " ");
y = strcspn(b, " ");
if (x != y)
return x < y ? -1 : x > y ? 1 : 0;
return strncmp(a, b, x);
}
static int compare_version(const char *a, const char *b) {
assert(a);
assert(b);
a += strcspn(a, " ");
a += strspn(a, " ");
b += strcspn(b, " ");
b += strspn(b, " ");
return strverscmp(a, b);
}
int r;
assert(f);
r = get_file_version(f, &a);
if (r < 0)
goto finish;
if (r == 0) {
r = -EINVAL;
goto finish;
}
if (!g) {
r = 0;
goto finish;
}
r = -errno;
goto finish;
}
r = get_file_version(g, &b);
if (r < 0)
goto finish;
if (r == 0 || compare_product(a, b) != 0) {
r = -EEXIST;
goto finish;
}
if (compare_version(a, b) < 0) {
r = -EEXIST;
goto finish;
}
r = 0;
free(a);
free(b);
if (g)
fclose(g);
return r;
}
char *p = NULL;
int r;
struct timespec t[2];
if (!f) {
return -errno;
}
if (!force) {
/* If this is an update, then let's compare versions first */
if (r < 0)
goto finish;
}
r = -ENOMEM;
goto finish;
}
g = fopen(p, "wxe");
if (!g) {
/* Directory doesn't exist yet? Then let's skip this... */
r = 0;
goto finish;
}
r = -errno;
goto finish;
}
rewind(f);
do {
size_t k;
if (ferror(f)) {
r = -errno;
goto finish;
}
if (k == 0)
break;
if (ferror(g)) {
r = -errno;
goto finish;
}
} while (!feof(f));
fflush(g);
if (ferror(g)) {
r = -errno;
goto finish;
}
if (r < 0) {
r = -errno;
goto finish;
}
if (r < 0) {
r = -errno;
goto finish;
}
r = -errno;
goto finish;
}
free(p);
p = NULL;
r = 0;
if (f)
fclose(f);
if (g)
fclose(g);
if (p) {
unlink(p);
free(p);
}
return r;
}
static char* strupper(char *s) {
char *p;
for (p = s; *p; p++)
*p = toupper(*p);
return s;
}
char *p;
return -ENOMEM;
}
if (mkdir(p, 0700) < 0) {
free(p);
return -errno;
}
} else
free(p);
return 0;
}
static int create_dirs(const char *esp_path) {
int r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
_cleanup_free_ char *p = NULL;
_cleanup_free_ char *q = NULL;
_cleanup_free_ char *v = NULL;
int r;
return -ENOMEM;
}
return -ENOMEM;
}
int k;
/* Create the EFI default boot loader name (specified for removable devices) */
return -ENOMEM;
}
if (k < 0 && r == 0)
return k;
}
return r;
}
DIR *d;
int r = 0;
if (force) {
/* Don't create any of these directories when we are
* just updating. When we update we'll drop-in our
* files (unless there are newer ones already), but we
* won't create the directories for them in the first
* place. */
r = create_dirs(esp_path);
if (r < 0)
return r;
}
d = opendir(BOOTLIBDIR);
if (!d) {
return -errno;
}
size_t n;
int k;
continue;
continue;
if (k < 0 && r == 0)
r = k;
}
closedir(d);
return r;
}
int err;
bool same = false;
if (err < 0)
return false;
goto finish;
goto finish;
same = true;
return same;
}
int n_options;
int i;
bool existing = false;
if (n_options < 0)
return n_options;
/* find already existing systemd-boot entry */
for (i = 0; i < n_options; i++)
existing = true;
goto finish;
}
/* find free slot in the sorted BootXXXX variable list */
for (i = 0; i < n_options; i++)
if (i != options[i]) {
new_id = i;
goto finish;
}
/* use the next one */
if (i == 0xffff)
return -ENOSPC;
new_id = i;
return existing;
}
int n_order;
int i;
int err = 0;
if (n_order <= 0) {
/* no entry, add us */
goto finish;
}
/* are we the first and only one? */
goto finish;
/* are we already in the boot order? */
for (i = 0; i < n_order; i++) {
continue;
/* we do not require to be the first one, all is fine */
if (!first)
goto finish;
/* move us to the first slot */
goto finish;
}
/* extend array */
if (!new_order) {
goto finish;
}
/* add us to the top or end of the list */
if (first) {
} else
return err;
}
int n_order;
int i;
int err = 0;
if (n_order < 0)
return n_order;
if (n_order == 0)
return 0;
for (i = 0; i < n_order; i++) {
continue;
if (i+1 < n_order)
break;
}
return err;
}
static int install_variables(const char *esp_path,
bool first) {
char *p = NULL;
int r;
if (!is_efi_boot()) {
return 0;
}
return -ENOMEM;
}
r = 0;
else
r = -errno;
goto finish;
}
if (r < 0) {
if (r == -ENOENT)
else
goto finish;
}
if (first || r == false) {
if (r < 0) {
goto finish;
}
}
free(p);
return r;
}
static int remove_boot_efi(const char *esp_path) {
int r = 0, c = 0;
return -ENOMEM;
}
d = opendir(p);
if (!d) {
r = 0;
goto finish;
}
r = -errno;
goto finish;
}
char *v;
size_t n;
FILE *f;
continue;
continue;
continue;
free(q);
q = NULL;
r = -ENOMEM;
goto finish;
}
f = fopen(q, "re");
if (!f) {
r = -errno;
goto finish;
}
r = get_file_version(f, &v);
fclose(f);
if (r < 0)
goto finish;
r = unlink(q);
if (r < 0) {
r = -errno;
free(v);
goto finish;
} else
}
c++;
free(v);
}
r = c;
if (d)
closedir(d);
free(p);
free(q);
return r;
}
char *p;
return -ENOMEM;
}
if (rmdir(p) < 0) {
free(p);
return -errno;
}
} else
free(p);
return 0;
}
static int remove_binaries(const char *esp_path) {
char *p;
int r, q;
return -ENOMEM;
}
r = rm_rf(p, false, false, false);
free(p);
q = remove_boot_efi(esp_path);
if (q < 0 && r == 0)
r = q;
if (q < 0 && r == 0)
r = q;
if (q < 0 && r == 0)
r = q;
if (q < 0 && r == 0)
r = q;
if (q < 0 && r == 0)
r = q;
if (q < 0 && r == 0)
r = q;
return r;
}
int r;
if (!is_efi_boot())
return 0;
if (r != 1)
return 0;
r = efi_remove_boot_option(slot);
if (r < 0)
return r;
if (in_order)
return 0;
}
static int install_loader_config(const char *esp_path) {
char *p = NULL;
char line[64];
FILE *f;
if (!f)
return -errno;
char *s;
if (s)
s[0] = '\0';
}
fclose(f);
if (!machine)
return -ESRCH;
return -ENOMEM;
}
f = fopen(p, "wxe");
if (f) {
fprintf(f, "#timeout 3\n");
fclose(f);
}
free(p);
return 0;
}
static int help(void) {
printf("%s [COMMAND] [OPTIONS...]\n"
"\n"
"Install, update or remove the sdboot EFI boot manager.\n\n"
" -h --help Show this help\n"
" --version Print version\n"
" --path=PATH Path to the EFI System Partition (ESP)\n"
" --no-variables Don't touch EFI variables\n"
"\n"
"Comands:\n"
" status Show status of installed systemd-boot and EFI variables\n"
" install Install systemd-boot to the ESP and EFI variables\n"
" update Update systemd-boot in the ESP and EFI variables\n"
" remove Remove systemd-boot from the ESP and EFI variables\n",
return 0;
}
static bool arg_touch_variables = true;
enum {
ARG_PATH = 0x100,
};
};
int c;
switch (c) {
case 'h':
help();
return 0;
case ARG_VERSION:
return 0;
case ARG_PATH:
break;
case ARG_NO_VARIABLES:
arg_touch_variables = false;
break;
case '?':
return -EINVAL;
default:
return -EINVAL;
}
}
return 1;
}
enum action {
} arg_action = ACTION_STATUS;
static const struct {
const char* verb;
} verbs[] = {
{ "status", ACTION_STATUS },
{ "install", ACTION_INSTALL },
{ "update", ACTION_UPDATE },
{ "remove", ACTION_REMOVE },
};
sd_id128_t uuid = {};
unsigned int i;
int q;
int r;
if (r <= 0)
goto finish;
for (i = 0; i < ELEMENTSOF(verbs); i++) {
continue;
break;
}
if (i >= ELEMENTSOF(verbs)) {
r = -EINVAL;
goto finish;
}
}
if (!arg_path)
arg_path = "/boot";
if (geteuid() != 0) {
r = -EPERM;
goto finish;
}
fprintf(stderr, "You might want to use --path= to indicate the path to your ESP, in case it is not mounted to /boot.\n");
if (r < 0)
goto finish;
switch (arg_action) {
case ACTION_STATUS: {
sd_id128_t loader_part_uuid = {};
printf("System:\n");
printf("\n");
printf("Loader:\n");
printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
else
printf(" Partition: n/a\n");
printf("\n");
if (r < 0)
goto finish;
if (arg_touch_variables)
r = status_variables();
break;
}
case ACTION_INSTALL:
case ACTION_UPDATE:
umask(0002);
if (r < 0)
goto finish;
if (arg_action == ACTION_INSTALL)
if (arg_touch_variables)
arg_action == ACTION_INSTALL);
break;
case ACTION_REMOVE:
r = remove_binaries(arg_path);
if (arg_touch_variables) {
if (q < 0 && r == 0)
r = q;
}
break;
}
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
int r;
log_open();
if (r <= 0)
goto finish;
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}