Skip to content

Commit

Permalink
bootctl: add boot loader and firmware interface tool
Browse files Browse the repository at this point in the history
  • Loading branch information
kaysievers committed Feb 11, 2013
1 parent c937e0d commit 7b4d7cc
Show file tree
Hide file tree
Showing 10 changed files with 869 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/TAGS
/accelerometer
/ata_id
/bootctl
/build-aux
/cdrom_id
/collect
Expand Down
16 changes: 15 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -3069,7 +3069,6 @@ timedatectl_LDADD = \

bin_PROGRAMS += \
timedatectl

endif

polkitpolicy_in_files += \
Expand All @@ -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 = \
Expand Down
171 changes: 171 additions & 0 deletions src/boot/boot-efi.c
Original file line number Diff line number Diff line change
@@ -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 changes: 131 additions & 0 deletions src/boot/boot-loader.c
Original file line number Diff line number Diff line change
@@ -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 changes: 25 additions & 0 deletions src/boot/boot-loader.h
Original file line number Diff line number Diff line change
@@ -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);
Loading

0 comments on commit 7b4d7cc

Please sign in to comment.