bootctl.c revision 68cc17f13102bcb9aa425ef5f15feda1c2853ec8
/*-*- 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 <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"
#include "rm-rf.h"
#include "blkid-util.h"
static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid) {
_cleanup_free_ char *t = NULL;
int r;
const char *v, *t2;
log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
return -ENODEV;
}
log_error("Block device node of %p is invalid.", p);
return -ENODEV;
}
if (r < 0)
log_error("Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p);
return -ENODEV;
}
if (r < 0)
return log_oom();
errno = 0;
b = blkid_new_probe_from_filename(t);
if (!b) {
if (errno == 0)
return log_oom();
}
errno = 0;
r = blkid_do_safeprobe(b);
if (r == -2) {
log_error("File system \"%s\" is ambigious.", p);
return -ENODEV;
} else if (r == 1) {
log_error("File system \"%s\" does not contain a label.", p);
return -ENODEV;
} else if (r != 0) {
return log_error_errno(r, "Failed to probe file system \"%s\": %m", p);
}
errno = 0;
if (r != 0) {
return log_error_errno(r, "Failed to probe file system type \"%s\": %m", p);
}
if (!streq(v, "vfat")) {
log_error("File system \"%s\" is not FAT.", p);
return -ENODEV;
}
errno = 0;
if (r != 0) {
return log_error_errno(r, "Failed to probe partition scheme \"%s\": %m", p);
}
if (!streq(v, "gpt")) {
log_error("File system \"%s\" is not on a GPT partition table.", p);
return -ENODEV;
}
errno = 0;
if (r != 0) {
return log_error_errno(r, "Failed to probe partition type UUID \"%s\": %m", p);
}
if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) {
log_error("File system \"%s\" has wrong type for an EFI System Partition (ESP).", p);
return -ENODEV;
}
errno = 0;
if (r != 0) {
return log_error_errno(r, "Failed to probe partition entry UUID \"%s\": %m", p);
}
r = sd_id128_from_string(v, uuid);
if (r < 0) {
log_error("Partition \"%s\" has invalid UUID \"%s\".", p, v);
return -EIO;
}
errno = 0;
if (r != 0) {
return log_error_errno(r, "Failed to probe partition number \"%s\": m", p);
}
errno = 0;
if (r != 0) {
return log_error_errno(r, "Failed to probe partition offset \"%s\": %m", p);
}
errno = 0;
if (r != 0) {
return log_error_errno(r, "Failed to probe partition size \"%s\": %m", p);
}
return 0;
}
/* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */
static int get_file_version(int fd, char **v) {
char *buf;
const char *s, *e;
char *x = NULL;
int r = 0;
assert(v);
return -errno;
return 0;
if (buf == MAP_FAILED)
return -errno;
if (!s)
goto finish;
s += 17;
if (!e || e - s < 3) {
log_error("Malformed version string.");
r = -EINVAL;
goto finish;
}
x = strndup(s, e - s);
if (!x) {
r = log_oom();
goto finish;
}
r = 1;
*v = x;
return r;
}
char *p;
int r = 0, c = 0;
d = opendir(p);
if (!d) {
return 0;
}
_cleanup_free_ char *v = NULL;
continue;
continue;
continue;
if (fd < 0)
r = get_file_version(fd, &v);
if (r < 0)
return r;
if (r > 0)
else
c++;
}
return c;
}
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)
log_error("systemd-boot not installed in ESP.");
else if (r < 0)
return r;
if (r == 0)
else if (r < 0)
return r;
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 i;
if (!is_efi_boot()) {
log_notice("Not booted with EFI, not showing EFI variables.");
return 0;
}
else if (n_options < 0)
n_order = 0;
else if (n_order < 0)
/* print entries in BootOrder first */
printf("Boot Loader Entries in EFI Variables:\n");
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;
for (j = 0; j < n_order; j++)
goto next;
print_efi_option(options[i], false);
next:
continue;
}
return 0;
}
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;
r = get_file_version(fd, &a);
if (r < 0)
return r;
if (r == 0) {
return -EINVAL;
}
if (fd2 < 0) {
return 0;
}
r = get_file_version(fd2, &b);
if (r < 0)
return r;
if (r == 0 || compare_product(a, b) != 0) {
return -EEXIST;
}
if (compare_version(a, b) < 0) {
return -ESTALE;
}
return 0;
}
char *p;
int r;
struct timespec t[2];
if (!f)
if (!force) {
/* If this is an update, then let's compare versions first */
if (r < 0)
return r;
}
g = fopen(p, "wxe");
if (!g) {
/* Directory doesn't exist yet? Then let's skip this... */
return 0;
}
rewind(f);
do {
size_t k;
if (ferror(f)) {
goto error;
}
if (k == 0)
break;
if (ferror(g)) {
goto error;
}
} while (!feof(f));
fflush(g);
if (ferror(g)) {
goto error;
}
if (r < 0) {
goto error;
}
if (r < 0) {
goto error;
}
goto error;
}
return 0;
unlink(p);
return r;
}
static char* strupper(char *s) {
char *p;
for (p = s; *p; p++)
*p = toupper(*p);
return s;
}
char *p;
if (mkdir(p, 0700) < 0) {
} else
log_info("Created \"%s\".", p);
return 0;
}
static const char *efi_subdirs[] = {
"EFI",
"loader",
};
static int create_dirs(const char *esp_path) {
int r;
unsigned i;
for (i = 0; i < ELEMENTSOF(efi_subdirs); i++) {
if (r < 0)
return r;
}
return 0;
}
char *p, *q;
int r;
int k;
char *v;
/* Create the EFI default boot loader name (specified for removable devices) */
if (k < 0 && r == 0)
r = k;
}
return r;
}
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)
int k;
continue;
continue;
if (k < 0 && r == 0)
r = k;
}
return r;
}
int r;
if (r < 0)
return false;
return false;
return false;
return true;
}
int n, i;
n = efi_get_boot_options(&options);
if (n < 0)
return n;
/* find already existing systemd-boot entry */
for (i = 0; i < n; i++)
return 1;
}
/* find free slot in the sorted BootXXXX variable list */
for (i = 0; i < n; i++)
if (i != options[i]) {
*id = i;
return 1;
}
/* use the next one */
if (i == 0xffff)
return -ENOSPC;
*id = i;
return 0;
}
uint16_t *t;
int n, i;
n = efi_get_boot_order(&order);
if (n <= 0)
/* no entry, add us */
/* are we the first and only one? */
return 0;
/* are we already in the boot order? */
for (i = 0; i < n; i++) {
continue;
/* we do not require to be the first one, all is fine */
if (!first)
return 0;
/* move us to the first slot */
return efi_set_boot_order(order, n);
}
/* extend array */
if (!t)
return -ENOMEM;
order = t;
/* add us to the top or end of the list */
if (first) {
} else
}
int n, i;
n = efi_get_boot_order(&order);
if (n <= 0)
return n;
for (i = 0; i < n; i++) {
continue;
if (i + 1 < n)
}
return 0;
}
static int install_variables(const char *esp_path,
bool first) {
char *p;
int r;
if (!is_efi_boot()) {
log_warning("Not booted with EFI, skipping EFI variable setup.");
return 0;
}
return 0;
else
}
if (r < 0)
return log_error_errno(r,
r == -ENOENT ?
"Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" :
"Failed to determine current boot order: %m");
if (first || r == false) {
if (r < 0)
return log_error_errno(r, "Failed to create EFI Boot variable entry: %m");
log_info("Created EFI boot entry \"Linux Boot Manager\".");
}
}
static int remove_boot_efi(const char *esp_path) {
char *p;
int r, c = 0;
d = opendir(p);
if (!d) {
return 0;
}
_cleanup_free_ char *v = NULL;
continue;
continue;
continue;
if (fd < 0)
r = get_file_version(fd, &v);
if (r < 0)
return r;
if (r > 0 && startswith(v, "systemd-boot ")) {
if (r < 0)
}
c++;
}
return c;
}
char *p;
if (rmdir(p) < 0) {
} else
log_info("Removed \"%s\".", p);
return 0;
}
static int remove_binaries(const char *esp_path) {
char *p;
int r, q;
unsigned i;
q = remove_boot_efi(esp_path);
if (q < 0 && r == 0)
r = q;
for (i = ELEMENTSOF(efi_subdirs); i > 0; i--) {
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 remove_from_order(slot);
else
return 0;
}
static int install_loader_config(const char *esp_path) {
char *p;
char line[64];
if (!f)
return -errno;
char *s;
if (s)
s[0] = '\0';
}
if (!machine)
return -ESRCH;
g = fopen(p, "wxe");
if (g) {
fprintf(g, "#timeout 3\n");
if (ferror(g))
}
return 0;
}
static int help(void) {
printf("%s [COMMAND] [OPTIONS...]\n"
"\n"
"Install, update or remove the systemd-boot 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"
"Commands:\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 const char *arg_path = "/boot";
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:
assert_not_reached("Unknown option");
}
return 1;
}
int r;
if (r < 0 && r != -ENOENT)
}
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 = {};
int r, q;
unsigned i;
for (i = 0; i < ELEMENTSOF(verbs); i++) {
continue;
break;
}
if (i >= ELEMENTSOF(verbs)) {
return -EINVAL;
}
}
if (geteuid() != 0)
log_notice("You might want to use --path= to indicate the path to your ESP, in case it is not mounted on /boot.");
if (r < 0)
return r;
switch (arg_action) {
case ACTION_STATUS: {
sd_id128_t loader_part_uuid = {};
if (is_efi_boot()) {
if (loader_path)
if (r < 0 && r == -ENOENT)
log_warning_errno(r, "Failed to read EFI variable LoaderDevicePartUUID: %m");
printf("System:\n");
r = is_efi_secure_boot();
if (r < 0)
log_warning_errno(r, "Failed to query secure boot status: %m");
else
if (r < 0)
log_warning_errno(r, "Failed to query secure boot mode: %m");
else
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");
} else
printf("System:\n Not booted with EFI\n");
if (r < 0)
return r;
if (arg_touch_variables)
r = status_variables();
break;
}
case ACTION_INSTALL:
case ACTION_UPDATE:
umask(0002);
if (r < 0)
return r;
if (arg_action == ACTION_INSTALL) {
if (r < 0)
return r;
}
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;
}
int r;
log_open();
if (r <= 0)
goto finish;
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}