mirror of
https://github.com/systemd/systemd.git
synced 2025-03-31 14:50:15 +03:00
bootctl: add boot loader and firmware interface tool
This commit is contained in:
parent
c937e0d5c5
commit
7b4d7cc082
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@
|
||||
/TAGS
|
||||
/accelerometer
|
||||
/ata_id
|
||||
/bootctl
|
||||
/build-aux
|
||||
/cdrom_id
|
||||
/collect
|
||||
|
16
Makefile.am
16
Makefile.am
@ -3069,7 +3069,6 @@ timedatectl_LDADD = \
|
||||
|
||||
bin_PROGRAMS += \
|
||||
timedatectl
|
||||
|
||||
endif
|
||||
|
||||
polkitpolicy_in_files += \
|
||||
@ -3078,6 +3077,21 @@ polkitpolicy_in_files += \
|
||||
EXTRA_DIST += \
|
||||
units/systemd-timedated.service.in
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
bootctl_SOURCES = \
|
||||
src/boot/boot.h \
|
||||
src/boot/boot-loader.h \
|
||||
src/boot/bootctl.c \
|
||||
src/boot/boot-loader.c \
|
||||
src/boot/boot-efi.c
|
||||
|
||||
bootctl_LDADD = \
|
||||
libsystemd-shared.la \
|
||||
libsystemd-id128.la
|
||||
|
||||
bin_PROGRAMS += \
|
||||
bootctl
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
if HAVE_MYHOSTNAME
|
||||
libnss_myhostname_la_SOURCES = \
|
||||
|
171
src/boot/boot-efi.c
Normal file
171
src/boot/boot-efi.c
Normal file
@ -0,0 +1,171 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2013 Kay Sievers
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
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 <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <locale.h>
|
||||
#include <string.h>
|
||||
#include <fnmatch.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/timex.h>
|
||||
|
||||
#include "boot.h"
|
||||
#include "boot-loader.h"
|
||||
#include "build.h"
|
||||
#include "util.h"
|
||||
#include "strv.h"
|
||||
#include "efivars.h"
|
||||
#include "conf-files.h"
|
||||
|
||||
static int get_boot_entries(struct boot_info *info) {
|
||||
DIR *d = NULL;
|
||||
struct dirent *dent;
|
||||
int err = 0;
|
||||
|
||||
d = opendir("/sys/firmware/efi/efivars");
|
||||
if (!d)
|
||||
return -errno;
|
||||
|
||||
for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
|
||||
unsigned int id;
|
||||
struct boot_info_entry *e;
|
||||
|
||||
if (dent->d_name[0] == '.')
|
||||
continue;
|
||||
if (sscanf(dent->d_name, "Boot%04X-8be4df61-93ca-11d2-aa0d-00e098032b8c", &id) != 1)
|
||||
continue;
|
||||
|
||||
e = realloc(info->fw_entries, (info->fw_entries_count+1) * sizeof(struct boot_info_entry));
|
||||
if (!e) {
|
||||
err = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
info->fw_entries = e;
|
||||
|
||||
e = &info->fw_entries[info->fw_entries_count];
|
||||
memset(e, 0, sizeof(struct boot_info_entry));
|
||||
e->order = -1;
|
||||
|
||||
err = efi_get_boot_option(id, NULL, &e->title, &e->part_uuid, &e->path, &e->data, &e->data_size);
|
||||
if (err < 0)
|
||||
break;
|
||||
e->id = id;
|
||||
|
||||
info->fw_entries_count++;
|
||||
}
|
||||
closedir(d);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int find_active_entry(struct boot_info *info) {
|
||||
uint16_t boot_cur;
|
||||
void *buf;
|
||||
size_t l;
|
||||
size_t i;
|
||||
int err = -ENOENT;
|
||||
|
||||
err = efi_get_variable(EFI_VENDOR_GLOBAL, "BootCurrent", NULL, &buf, &l);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
memcpy(&boot_cur, buf, sizeof(uint16_t));
|
||||
for (i = 0; i < info->fw_entries_count; i++) {
|
||||
if (info->fw_entries[i].id != boot_cur)
|
||||
continue;
|
||||
info->fw_entry_active = i;
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
free(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int get_boot_order(struct boot_info *info) {
|
||||
size_t i, k;
|
||||
int err;
|
||||
|
||||
err = efi_get_boot_order(&info->fw_entries_order, &info->fw_entries_order_count);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < info->fw_entries_order_count; i++) {
|
||||
for (k = 0; k < info->fw_entries_count; k++) {
|
||||
if (info->fw_entries[k].id != info->fw_entries_order[i])
|
||||
continue;
|
||||
info->fw_entries[k].order = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_cmp(const void *a, const void *b) {
|
||||
const struct boot_info_entry *e1 = a;
|
||||
const struct boot_info_entry *e2 = b;
|
||||
|
||||
/* boot order of active entries */
|
||||
if (e1->order > 0 && e2->order > 0)
|
||||
return e1->order - e2->order;
|
||||
|
||||
/* sort active entries before inactive ones */
|
||||
if (e1->order > 0)
|
||||
return 1;
|
||||
if (e2->order > 0)
|
||||
return -1;
|
||||
|
||||
/* order of inactive entries */
|
||||
return e1->id - e2->id;
|
||||
}
|
||||
|
||||
int boot_info_query(struct boot_info *info) {
|
||||
char str[64];
|
||||
char buf[64];
|
||||
char *loader_active;
|
||||
|
||||
info->loader = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderInfo");
|
||||
|
||||
get_boot_entries(info);
|
||||
if (info->fw_entries_count > 0) {
|
||||
get_boot_order(info);
|
||||
qsort(info->fw_entries, info->fw_entries_count, sizeof(struct boot_info_entry), entry_cmp);
|
||||
find_active_entry(info);
|
||||
}
|
||||
|
||||
info->fw_type = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareType");
|
||||
info->fw_info = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareInfo");
|
||||
info->loader_image_path = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderImageIdentifier");
|
||||
efi_get_loader_device_part_uuid(&info->loader_part_uuid);
|
||||
|
||||
boot_loader_read_entries(info);
|
||||
loader_active = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntrySelected");
|
||||
if (loader_active) {
|
||||
boot_loader_find_active_entry(info, loader_active);
|
||||
free(loader_active);
|
||||
}
|
||||
|
||||
snprintf(str, sizeof(str), "LoaderEntryOptions-%s", sd_id128_to_string(info->machine_id, buf));
|
||||
info->loader_options_added = efi_get_variable_string(EFI_VENDOR_LOADER, str);
|
||||
return 0;
|
||||
}
|
131
src/boot/boot-loader.c
Normal file
131
src/boot/boot-loader.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2013 Kay Sievers
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
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 <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <locale.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/timex.h>
|
||||
|
||||
#include "boot.h"
|
||||
#include "boot-loader.h"
|
||||
#include "build.h"
|
||||
#include "util.h"
|
||||
#include "strv.h"
|
||||
#include "conf-files.h"
|
||||
|
||||
static char *loader_fragment_read_title(const char *fragment) {
|
||||
FILE *f;
|
||||
char line[LINE_MAX];
|
||||
char *title = NULL;
|
||||
|
||||
f = fopen(fragment, "re");
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
while (fgets(line, sizeof(line), f) != NULL) {
|
||||
char *s;
|
||||
size_t l;
|
||||
|
||||
l = strlen(line);
|
||||
if (l < 1)
|
||||
continue;
|
||||
if (line[l-1] == '\n')
|
||||
line[l-1] = '\0';
|
||||
|
||||
s = line;
|
||||
while (isspace(s[0]))
|
||||
s++;
|
||||
|
||||
if (s[0] == '#')
|
||||
continue;
|
||||
|
||||
if (!startswith(s, "title"))
|
||||
continue;
|
||||
|
||||
s += strlen("title");
|
||||
if (!isspace(s[0]))
|
||||
continue;
|
||||
while (isspace(s[0]))
|
||||
s++;
|
||||
|
||||
title = strdup(s);
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return title;
|
||||
}
|
||||
|
||||
int boot_loader_read_entries(struct boot_info *info) {
|
||||
_cleanup_strv_free_ char **files = NULL;
|
||||
static const char *loader_dir[] = { "/boot/loader/entries", NULL};
|
||||
unsigned int count;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
err = conf_files_list_strv(&files, ".conf", NULL, loader_dir);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
count = strv_length(files);
|
||||
info->loader_entries = new0(struct boot_info_entry, count);
|
||||
if (!info->loader_entries)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
info->loader_entries[i].title = loader_fragment_read_title(files[i]);
|
||||
info->loader_entries[i].path = strdup(files[i]);
|
||||
if (!info->loader_entries[i].title || !info->loader_entries[i].path) {
|
||||
free(info->loader_entries[i].title);
|
||||
free(info->loader_entries[i].path);
|
||||
return -ENOMEM;
|
||||
}
|
||||
info->loader_entries_count++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int boot_loader_find_active_entry(struct boot_info *info, const char *loader_active) {
|
||||
char *fn;
|
||||
unsigned int i;
|
||||
|
||||
if (!loader_active)
|
||||
return -ENOENT;
|
||||
if (info->loader_entries_count == 0)
|
||||
return -ENOENT;
|
||||
|
||||
if (asprintf(&fn, "/boot/loader/entries/%s.conf", loader_active) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < info->loader_entries_count; i++) {
|
||||
if (strcmp(fn, info->loader_entries[i].path) == 0) {
|
||||
info->loader_entry_active = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(fn);
|
||||
return 0;
|
||||
}
|
25
src/boot/boot-loader.h
Normal file
25
src/boot/boot-loader.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2013 Kay Sievers
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
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/>.
|
||||
***/
|
||||
|
||||
int boot_loader_read_entries(struct boot_info *info);
|
||||
int boot_loader_find_active_entry(struct boot_info *info, const char *loader_active);
|
64
src/boot/boot.h
Normal file
64
src/boot/boot.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2013 Kay Sievers
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
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 "sd-id128.h"
|
||||
|
||||
/*
|
||||
* Firmware and boot manager information to be filled in
|
||||
* by the platform.
|
||||
*
|
||||
* This is partly EFI specific, if you add things, keep this
|
||||
* as generic as possible to be able to re-use it on other
|
||||
* platforms.
|
||||
*/
|
||||
|
||||
struct boot_info_entry {
|
||||
uint16_t id;
|
||||
uint16_t order;
|
||||
char *title;
|
||||
sd_id128_t part_uuid;
|
||||
char *path;
|
||||
char *data;
|
||||
size_t data_size;
|
||||
};
|
||||
|
||||
struct boot_info {
|
||||
sd_id128_t machine_id;
|
||||
sd_id128_t boot_id;
|
||||
char *fw_type;
|
||||
char *fw_info;
|
||||
struct boot_info_entry *fw_entries;
|
||||
size_t fw_entries_count;
|
||||
uint16_t *fw_entries_order;
|
||||
size_t fw_entries_order_count;
|
||||
ssize_t fw_entry_active;
|
||||
char *loader;
|
||||
char *loader_image_path;
|
||||
sd_id128_t loader_part_uuid;
|
||||
struct boot_info_entry *loader_entries;
|
||||
size_t loader_entries_count;
|
||||
ssize_t loader_entry_active;
|
||||
char *loader_options_added;
|
||||
};
|
||||
|
||||
int boot_info_query(struct boot_info *info);
|
271
src/boot/bootctl.c
Normal file
271
src/boot/bootctl.c
Normal file
@ -0,0 +1,271 @@
|
||||
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||
|
||||
/***
|
||||
This file is part of systemd.
|
||||
|
||||
Copyright 2013 Kay Sievers
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
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 <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <locale.h>
|
||||
#include <string.h>
|
||||
#include <sys/timex.h>
|
||||
|
||||
#include "boot.h"
|
||||
#include "build.h"
|
||||
#include "util.h"
|
||||
#include "utf8.h"
|
||||
|
||||
static int help(void) {
|
||||
printf("%s [OPTIONS...] COMMAND ...\n\n"
|
||||
"Query or change firmware and boot mananger settings.\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
"Commands:\n"
|
||||
" status Show current time settings\n",
|
||||
program_invocation_short_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int c;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
while ((c = getopt_long(argc, argv, "+hH:P", options, NULL)) >= 0) {
|
||||
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
help();
|
||||
return 0;
|
||||
|
||||
case ARG_VERSION:
|
||||
puts(PACKAGE_STRING);
|
||||
puts(SYSTEMD_FEATURES);
|
||||
return 0;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
log_error("Unknown option code %c", c);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int boot_info_new(struct boot_info **info) {
|
||||
struct boot_info *in;
|
||||
int err;
|
||||
|
||||
in = new0(struct boot_info, 1);
|
||||
if (!in)
|
||||
return -ENOMEM;
|
||||
|
||||
err = sd_id128_get_machine(&in->machine_id);
|
||||
if (err < 0)
|
||||
goto err;
|
||||
|
||||
err = sd_id128_get_boot(&in->boot_id);
|
||||
if (err < 0)
|
||||
goto err;
|
||||
|
||||
in->fw_entry_active = -1;
|
||||
in->loader_entry_active = -1;
|
||||
|
||||
*info = in;
|
||||
return 0;
|
||||
err:
|
||||
free(in);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void boot_info_entries_free(struct boot_info_entry *entries, size_t n) {
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
free(entries[i].title);
|
||||
free(entries[i].path);
|
||||
}
|
||||
free(entries);
|
||||
}
|
||||
|
||||
static void boot_info_free(struct boot_info *info) {
|
||||
free(info->fw_type);
|
||||
free(info->fw_info);
|
||||
boot_info_entries_free(info->fw_entries, info->fw_entries_count);
|
||||
free(info->fw_entries_order);
|
||||
free(info->loader);
|
||||
free(info->loader_image_path);
|
||||
free(info->loader_options_added);
|
||||
boot_info_entries_free(info->loader_entries, info->loader_entries_count);
|
||||
free(info);
|
||||
}
|
||||
|
||||
static int show_status(char **args, unsigned n) {
|
||||
char buf[64];
|
||||
struct boot_info *info;
|
||||
int err;
|
||||
|
||||
err = boot_info_new(&info);
|
||||
if (err < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
err = boot_info_query(info);
|
||||
|
||||
printf(" Machine ID: %s\n", sd_id128_to_string(info->machine_id, buf));
|
||||
printf(" Boot ID: %s\n", sd_id128_to_string(info->boot_id, buf));
|
||||
if (info->fw_type)
|
||||
printf(" Firmware: %s (%s)\n", info->fw_type, strna(info->fw_info));
|
||||
|
||||
if (info->fw_entry_active >= 0) {
|
||||
printf("Firmware entry: %s\n", info->fw_entries[info->fw_entry_active].title);
|
||||
if (info->fw_entries[info->fw_entry_active].path)
|
||||
printf(" %s\n", info->fw_entries[info->fw_entry_active].path);
|
||||
if (!sd_id128_equal(info->fw_entries[info->fw_entry_active].part_uuid, SD_ID128_NULL))
|
||||
printf(" %s\n", sd_id128_to_string(info->fw_entries[info->fw_entry_active].part_uuid, buf));
|
||||
}
|
||||
|
||||
if (info->loader) {
|
||||
printf(" Loader: %s\n", info->loader);
|
||||
printf(" %s\n", strna(info->loader_image_path));
|
||||
if (!sd_id128_equal(info->loader_part_uuid, SD_ID128_NULL))
|
||||
printf(" %s\n", sd_id128_to_string(info->loader_part_uuid, buf));
|
||||
|
||||
if (info->loader_entry_active >= 0) {
|
||||
printf(" Loader entry: %s\n", info->loader_entries[info->loader_entry_active].title);
|
||||
printf(" %s\n", info->loader_entries[info->loader_entry_active].path);
|
||||
}
|
||||
|
||||
printf("Loader options: %s\n", strna(info->loader_options_added));
|
||||
} else
|
||||
printf("No suitable data is provided by the boot manager. See:\n"
|
||||
" http://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface\n"
|
||||
" http://www.freedesktop.org/wiki/Specifications/BootLoaderSpec\n"
|
||||
"for details.\n");
|
||||
printf("\n");
|
||||
|
||||
boot_info_free(info);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int bootctl_main(int argc, char *argv[]) {
|
||||
static const struct {
|
||||
const char* verb;
|
||||
const enum {
|
||||
MORE,
|
||||
LESS,
|
||||
EQUAL
|
||||
} argc_cmp;
|
||||
const int argc;
|
||||
int (* const dispatch)(char **args, unsigned n);
|
||||
} verbs[] = {
|
||||
{ "status", LESS, 1, show_status },
|
||||
};
|
||||
|
||||
int left;
|
||||
unsigned i;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
left = argc - optind;
|
||||
|
||||
if (left <= 0)
|
||||
/* Special rule: no arguments means "status" */
|
||||
i = 0;
|
||||
else {
|
||||
if (streq(argv[optind], "help")) {
|
||||
help();
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ELEMENTSOF(verbs); i++)
|
||||
if (streq(argv[optind], verbs[i].verb))
|
||||
break;
|
||||
|
||||
if (i >= ELEMENTSOF(verbs)) {
|
||||
log_error("Unknown operation %s", argv[optind]);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
switch (verbs[i].argc_cmp) {
|
||||
|
||||
case EQUAL:
|
||||
if (left != verbs[i].argc) {
|
||||
log_error("Invalid number of arguments.");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case MORE:
|
||||
if (left < verbs[i].argc) {
|
||||
log_error("Too few arguments.");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case LESS:
|
||||
if (left > verbs[i].argc) {
|
||||
log_error("Too many arguments.");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown comparison operator.");
|
||||
}
|
||||
|
||||
return verbs[i].dispatch(argv + optind, left);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int r, retval = EXIT_FAILURE;
|
||||
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
else if (r == 0) {
|
||||
retval = EXIT_SUCCESS;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = bootctl_main(argc, argv);
|
||||
retval = r < 0 ? EXIT_FAILURE : r;
|
||||
finish:
|
||||
return retval;
|
||||
}
|
@ -20,14 +20,13 @@
|
||||
***/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "utf8.h"
|
||||
#include "efivars.h"
|
||||
|
||||
#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
|
||||
|
||||
bool is_efiboot(void) {
|
||||
return access("/sys/firmware/efi", F_OK) >= 0;
|
||||
}
|
||||
@ -93,6 +92,188 @@ int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, v
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *efi_get_variable_string(sd_id128_t vendor, const char *name) {
|
||||
_cleanup_free_ void *s = NULL;
|
||||
size_t ss;
|
||||
int err;
|
||||
|
||||
err = efi_get_variable(vendor, name, NULL, &s, &ss);
|
||||
if (err < 0)
|
||||
return NULL;
|
||||
return utf16_to_utf8(s, ss);
|
||||
}
|
||||
|
||||
static size_t utf16_size(const uint16_t *s) {
|
||||
size_t l = 0;
|
||||
|
||||
while (s[l] > 0)
|
||||
l++;
|
||||
return (l+1) * sizeof(uint16_t);
|
||||
}
|
||||
|
||||
static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) {
|
||||
struct uuid {
|
||||
uint32_t u1;
|
||||
uint16_t u2;
|
||||
uint16_t u3;
|
||||
uint8_t u4[8];
|
||||
} _packed_;
|
||||
const struct uuid *uuid = guid;
|
||||
|
||||
id128->bytes[0] = (uuid->u1 >> 24) & 0xff;
|
||||
id128->bytes[1] = (uuid->u1 >> 16) & 0xff;
|
||||
id128->bytes[2] = (uuid->u1 >> 8) & 0xff;
|
||||
id128->bytes[3] = (uuid->u1) & 0xff;
|
||||
id128->bytes[4] = (uuid->u2 >> 8) & 0xff;
|
||||
id128->bytes[5] = (uuid->u2) & 0xff;
|
||||
id128->bytes[6] = (uuid->u3 >> 8) & 0xff;
|
||||
id128->bytes[7] = (uuid->u3) & 0xff;
|
||||
memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4));
|
||||
}
|
||||
|
||||
int efi_get_boot_option(uint32_t id, uint32_t *attributes, char **title, sd_id128_t *part_uuid, char **path, char **data, size_t *data_size) {
|
||||
struct boot_option {
|
||||
uint32_t attr;
|
||||
uint16_t path_len;
|
||||
uint16_t title[];
|
||||
} _packed_;
|
||||
|
||||
struct drive_path {
|
||||
uint32_t part_nr;
|
||||
uint64_t part_start;
|
||||
uint64_t part_size;
|
||||
char signature[16];
|
||||
uint8_t mbr_type;
|
||||
uint8_t signature_type;
|
||||
} _packed_;
|
||||
|
||||
struct device_path {
|
||||
uint8_t type;
|
||||
uint8_t sub_type;
|
||||
uint16_t length;
|
||||
union {
|
||||
uint16_t path[0];
|
||||
struct drive_path drive;
|
||||
};
|
||||
} _packed_;
|
||||
|
||||
char boot_id[32];
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
size_t l;
|
||||
struct boot_option *header;
|
||||
size_t title_size;
|
||||
char *s = NULL;
|
||||
char *p = NULL;
|
||||
sd_id128_t p_uuid = SD_ID128_NULL;
|
||||
char *d = NULL;
|
||||
size_t d_size = 0;
|
||||
int err;
|
||||
|
||||
snprintf(boot_id, sizeof(boot_id), "Boot%04X", id);
|
||||
err = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (l < sizeof(struct boot_option))
|
||||
return -ENOENT;
|
||||
|
||||
header = (struct boot_option *)buf;
|
||||
title_size = utf16_size(header->title);
|
||||
if (title_size > l - offsetof(struct boot_option, title))
|
||||
return -EINVAL;
|
||||
|
||||
s = utf16_to_utf8(header->title, title_size);
|
||||
if (!s) {
|
||||
err = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (header->path_len > 0) {
|
||||
char *dbuf;
|
||||
size_t dnext;
|
||||
|
||||
dbuf = buf + offsetof(struct boot_option, title) + title_size;
|
||||
dnext = 0;
|
||||
while (dnext < header->path_len) {
|
||||
struct device_path *dpath;
|
||||
|
||||
dpath = (struct device_path *)(dbuf + dnext);
|
||||
if (dpath->length < 4)
|
||||
break;
|
||||
|
||||
/* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */
|
||||
if (dpath->type == 0x7f && dpath->sub_type == 0xff)
|
||||
break;
|
||||
|
||||
dnext += dpath->length;
|
||||
|
||||
/* Type 0x04 – Media Device Path */
|
||||
if (dpath->type != 0x04)
|
||||
continue;
|
||||
|
||||
/* Sub-Type 1 – Hard Drive */
|
||||
if (dpath->sub_type == 0x01) {
|
||||
/* 0x02 – GUID Partition Table */
|
||||
if (dpath->drive.mbr_type != 0x02)
|
||||
continue;
|
||||
|
||||
/* 0x02 – GUID signature */
|
||||
if (dpath->drive.signature_type != 0x02)
|
||||
continue;
|
||||
|
||||
efi_guid_to_id128(dpath->drive.signature, &p_uuid);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Sub-Type 4 – File Path */
|
||||
if (dpath->sub_type == 0x04) {
|
||||
p = utf16_to_utf8(dpath->path, dpath->length-4);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*title = s;
|
||||
if (part_uuid)
|
||||
*part_uuid = p_uuid;
|
||||
if (path)
|
||||
*path = p;
|
||||
if (data)
|
||||
*data = d;
|
||||
if (data_size)
|
||||
*data_size = d_size;
|
||||
return 0;
|
||||
err:
|
||||
free(s);
|
||||
free(p);
|
||||
free(d);
|
||||
return err;
|
||||
}
|
||||
|
||||
int efi_get_boot_order(uint16_t **order, size_t *count) {
|
||||
void *buf;
|
||||
size_t l;
|
||||
int err;
|
||||
|
||||
err = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (l == 0) {
|
||||
free(buf);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if ((l % sizeof(uint16_t) > 0)) {
|
||||
free(buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*order = buf;
|
||||
*count = l / sizeof(uint16_t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_usec(sd_id128_t vendor, const char *name, usec_t *u) {
|
||||
_cleanup_free_ void *i = NULL;
|
||||
_cleanup_free_ char *j = NULL;
|
||||
|
@ -28,9 +28,16 @@
|
||||
#include "sd-id128.h"
|
||||
#include "time-util.h"
|
||||
|
||||
#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
|
||||
#define EFI_VENDOR_GLOBAL SD_ID128_MAKE(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
|
||||
|
||||
bool is_efiboot(void);
|
||||
|
||||
int efi_get_variable(sd_id128_t vendor, const char *name, uint32_t *attribute, void **value, size_t *size);
|
||||
char *efi_get_variable_string(sd_id128_t vendor, const char *name);
|
||||
|
||||
int efi_get_boot_option(uint32_t nr, uint32_t *attributes, char **title, sd_id128_t *partuuid, char **path, char **data, size_t *data_size);
|
||||
int efi_get_boot_order(uint16_t **order, size_t *count);
|
||||
|
||||
int efi_get_boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader);
|
||||
|
||||
|
@ -317,6 +317,6 @@ char *utf16_to_utf8(const void *s, size_t length) {
|
||||
}
|
||||
|
||||
*t = 0;
|
||||
return r;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user