Merge pull request #10175 from poettering/sd-boot-count-bootctl
the bootctl changes from PR #9437 (the boot counting PR)
This commit is contained in:
commit
304d6b1886
|
@ -112,6 +112,13 @@ systemd-timedated:
|
|||
first existing unit listed in the environment variable, and
|
||||
`timedatectl set-ntp off` disables and stops all listed units.
|
||||
|
||||
bootctl and other tools that access the EFI System Partition (ESP):
|
||||
|
||||
* `$SYSTEMD_RELAX_ESP_CHECKS=1` — if set, the ESP validation checks are
|
||||
relaxed. Specifically, validation checks that ensure the specified ESP path
|
||||
is a FAT file system are turned off, as are checks that the path is located
|
||||
on a GPT partition with the correct type UUID.
|
||||
|
||||
systemd itself:
|
||||
|
||||
* `$SYSTEMD_ACTIVATION_UNIT` — set for all NSS and PAM module invocations that
|
||||
|
|
|
@ -34,10 +34,10 @@
|
|||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>bootctl</command> can check the EFI boot loader status, list
|
||||
available entries, and install, update, or remove the
|
||||
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
boot loader on the current system.</para>
|
||||
<para><command>bootctl</command> can check the EFI boot loader status, list available boot loaders and boot loader
|
||||
entries, and install, update, or remove the
|
||||
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> boot loader on the
|
||||
current system.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -45,8 +45,6 @@
|
|||
<para>The following options are understood:</para>
|
||||
|
||||
<variablelist>
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
<varlistentry>
|
||||
<term><option>--path=</option></term>
|
||||
<listitem><para>Path to the EFI System Partition (ESP). If not specified, <filename>/efi</filename>,
|
||||
|
@ -64,8 +62,12 @@
|
|||
|
||||
<varlistentry>
|
||||
<term><option>--no-variables</option></term>
|
||||
<listitem><para>Do not touch the EFI boot variables.</para></listitem>
|
||||
<listitem><para>Do not touch the firmware's boot loader list stored in EFI variables.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="no-pager"/>
|
||||
<xi:include href="standard-options.xml" xpointer="help"/>
|
||||
<xi:include href="standard-options.xml" xpointer="version"/>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
|
@ -76,42 +78,54 @@
|
|||
<varlistentry>
|
||||
<term><option>status</option></term>
|
||||
|
||||
<listitem><para>Shows the currently installed versions of the boot loader binaries and all current
|
||||
EFI boot variables. If no command is specified, this is the implied default.</para></listitem>
|
||||
<listitem><para>Shows brief information about the system firmware, the boot loader that was used to boot the
|
||||
system, the boot loaders currently available in the ESP, the boot loaders listed in the firmware's list of boot
|
||||
loaders and the current default boot loader entry. If no command is specified, this is the implied
|
||||
default.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>list</option></term>
|
||||
<term><option>install</option></term>
|
||||
|
||||
<listitem><para>Shows all configured boot loader entries.</para></listitem>
|
||||
<listitem><para>Installs systemd-boot into the EFI system partition. A copy of <command>systemd-boot</command>
|
||||
will be stored as the EFI default/fallback loader at
|
||||
<filename><replaceable>ESP</replaceable>/EFI/BOOT/BOOT*.EFI</filename>. The boot loader is then added to the
|
||||
top of the firmware's boot loader list.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>update</option></term>
|
||||
|
||||
<listitem><para>Updates all installed versions of
|
||||
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
|
||||
if the current version is newer than the version installed in the EFI system
|
||||
partition. This also includes the EFI default/fallback loader at
|
||||
<filename><replaceable>ESP</replaceable>/EFI/BOOT/BOOT*.EFI</filename>. A
|
||||
systemd-boot entry in the EFI boot variables is created if there is no current
|
||||
entry. The created entry will be added to the end of the boot order list.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>install</option></term>
|
||||
|
||||
<listitem><para>Installs systemd-boot into the EFI system partition. A copy of systemd-boot will
|
||||
be stored as the EFI default/fallback loader at
|
||||
<filename><replaceable>ESP</replaceable>/EFI/BOOT/BOOT*.EFI</filename>. A systemd-boot entry in
|
||||
the EFI boot variables is created and added to the top of the boot order list.</para></listitem>
|
||||
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>, if the
|
||||
available version is newer than the version installed in the EFI system partition. This also includes the EFI
|
||||
default/fallback loader at <filename><replaceable>ESP</replaceable>/EFI/BOOT/BOOT*.EFI</filename>. The boot
|
||||
loader is then added to end of the firmware's boot loader list if missing.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>remove</option></term>
|
||||
|
||||
<listitem><para>Removes all installed versions of systemd-boot from the EFI system partition,
|
||||
and removes systemd-boot from the EFI boot variables.</para></listitem>
|
||||
<listitem><para>Removes all installed versions of <command>systemd-boot</command> from the EFI system partition
|
||||
and the firmware's boot loader list.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>list</option></term>
|
||||
|
||||
<listitem><para>Shows all available boot loader entries implementing the <ulink
|
||||
url="https://github.com/systemd/systemd/blob/master/docs/BOOT_LOADER_SPECIFICATION.md">Boot Loader
|
||||
Specification</ulink>, as well as any other entries discovered or automatically generated by the boot
|
||||
loader.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>set-default</option> <replaceable>ID</replaceable></term>
|
||||
<term><option>set-oneshot</option> <replaceable>ID</replaceable></term>
|
||||
|
||||
<listitem><para>Sets the default boot loader entry. Takes a single boot loader entry ID string as argument. The
|
||||
<option>set-oneshot</option> command will set the default entry only for the next boot, the
|
||||
<option>set-default</option> will set it persistently for all future boots.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
@ -122,6 +136,13 @@
|
|||
<para>On success, 0 is returned, a non-zero failure code otherwise.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Environment</title>
|
||||
<para>If <varname>$SYSTEMD_RELAX_ESP_CHECKS=1</varname> is set the validation checks for the ESP are relaxed, and
|
||||
the path specified with <option>--path=</option> may refer to any kind of file system on any kind of
|
||||
partition.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
|
|
|
@ -1715,7 +1715,7 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
|
|||
* it: the IMMUTABLE bit. Let's use this here, if this is requested. */
|
||||
|
||||
if (flags & BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE)
|
||||
(void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL);
|
||||
(void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
|
||||
} else {
|
||||
r = btrfs_subvol_set_read_only(new_path, true);
|
||||
if (r < 0)
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include "fd-util.h"
|
||||
#include "macro.h"
|
||||
|
||||
int chattr_fd(int fd, unsigned value, unsigned mask) {
|
||||
int chattr_fd(int fd, unsigned value, unsigned mask, unsigned *previous) {
|
||||
unsigned old_attr, new_attr;
|
||||
struct stat st;
|
||||
|
||||
|
@ -28,23 +28,29 @@ int chattr_fd(int fd, unsigned value, unsigned mask) {
|
|||
if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode))
|
||||
return -ENOTTY;
|
||||
|
||||
if (mask == 0)
|
||||
if (mask == 0 && !previous)
|
||||
return 0;
|
||||
|
||||
if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0)
|
||||
return -errno;
|
||||
|
||||
new_attr = (old_attr & ~mask) | (value & mask);
|
||||
if (new_attr == old_attr)
|
||||
if (new_attr == old_attr) {
|
||||
if (previous)
|
||||
*previous = old_attr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0)
|
||||
return -errno;
|
||||
|
||||
if (previous)
|
||||
*previous = old_attr;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int chattr_path(const char *p, unsigned value, unsigned mask) {
|
||||
int chattr_path(const char *p, unsigned value, unsigned mask, unsigned *previous) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
assert(p);
|
||||
|
@ -56,7 +62,7 @@ int chattr_path(const char *p, unsigned value, unsigned mask) {
|
|||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
return chattr_fd(fd, value, mask);
|
||||
return chattr_fd(fd, value, mask, previous);
|
||||
}
|
||||
|
||||
int read_attr_fd(int fd, unsigned *ret) {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
int chattr_fd(int fd, unsigned value, unsigned mask);
|
||||
int chattr_path(const char *p, unsigned value, unsigned mask);
|
||||
int chattr_fd(int fd, unsigned value, unsigned mask, unsigned *previous);
|
||||
int chattr_path(const char *p, unsigned value, unsigned mask, unsigned *previous);
|
||||
|
||||
int read_attr_fd(int fd, unsigned *ret);
|
||||
int read_attr_path(const char *p, unsigned *ret);
|
||||
|
|
|
@ -695,7 +695,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
|
|||
}
|
||||
|
||||
if (chattr_flags != 0)
|
||||
(void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
|
||||
(void) chattr_fd(fdt, chattr_flags, (unsigned) -1, NULL);
|
||||
|
||||
r = copy_file_fd(from, fdt, copy_flags);
|
||||
if (r < 0) {
|
||||
|
@ -743,7 +743,7 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned cha
|
|||
}
|
||||
|
||||
if (chattr_flags != 0)
|
||||
(void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
|
||||
(void) chattr_fd(fdt, chattr_flags, (unsigned) -1, NULL);
|
||||
|
||||
r = copy_file_fd(from, fdt, copy_flags);
|
||||
if (r < 0)
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "locale-util.h"
|
||||
#include "pager.h"
|
||||
#include "parse-util.h"
|
||||
#include "rm-rf.h"
|
||||
#include "stat-util.h"
|
||||
|
@ -36,6 +37,7 @@
|
|||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "umask-util.h"
|
||||
#include "utf8.h"
|
||||
#include "util.h"
|
||||
#include "verbs.h"
|
||||
#include "virt.h"
|
||||
|
@ -43,6 +45,7 @@
|
|||
static char *arg_path = NULL;
|
||||
static bool arg_print_path = false;
|
||||
static bool arg_touch_variables = true;
|
||||
static bool arg_no_pager = false;
|
||||
|
||||
static int acquire_esp(
|
||||
bool unprivileged_mode,
|
||||
|
@ -155,7 +158,7 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char
|
|||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
printf(" File: %s/%s/%s (%s)\n", special_glyph(TREE_RIGHT), path, de->d_name, v);
|
||||
printf(" File: %s/%s/%s (%s%s%s)\n", special_glyph(TREE_RIGHT), path, de->d_name, ansi_highlight(), v, ansi_normal());
|
||||
else
|
||||
printf(" File: %s/%s/%s\n", special_glyph(TREE_RIGHT), path, de->d_name);
|
||||
c++;
|
||||
|
@ -167,7 +170,7 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char
|
|||
static int status_binaries(const char *esp_path, sd_id128_t partition) {
|
||||
int r;
|
||||
|
||||
printf("Boot Loader Binaries:\n");
|
||||
printf("Available Boot Loaders on ESP:\n");
|
||||
|
||||
if (!esp_path) {
|
||||
printf(" ESP: Cannot find or access mount point of ESP.\n\n");
|
||||
|
@ -181,13 +184,13 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) {
|
|||
|
||||
r = enumerate_binaries(esp_path, "EFI/systemd", NULL);
|
||||
if (r == 0)
|
||||
log_error("systemd-boot not installed in ESP.");
|
||||
log_info("systemd-boot not installed in ESP.");
|
||||
else if (r < 0)
|
||||
return r;
|
||||
|
||||
r = enumerate_binaries(esp_path, "EFI/BOOT", "boot");
|
||||
if (r == 0)
|
||||
log_error("No default/fallback boot loader installed in ESP.");
|
||||
log_info("No default/fallback boot loader installed in ESP.");
|
||||
else if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -213,7 +216,7 @@ static int print_efi_option(uint16_t id, bool in_order) {
|
|||
|
||||
efi_tilt_backslashes(path);
|
||||
|
||||
printf(" Title: %s\n", strna(title));
|
||||
printf(" Title: %s%s%s\n", ansi_highlight(), strna(title), ansi_normal());
|
||||
printf(" ID: 0x%04X\n", id);
|
||||
printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : "");
|
||||
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));
|
||||
|
@ -224,9 +227,8 @@ static int print_efi_option(uint16_t id, bool in_order) {
|
|||
}
|
||||
|
||||
static int status_variables(void) {
|
||||
int n_options, n_order;
|
||||
_cleanup_free_ uint16_t *options = NULL, *order = NULL;
|
||||
int i;
|
||||
int n_options, n_order, i;
|
||||
|
||||
n_options = efi_get_boot_options(&options);
|
||||
if (n_options == -ENOENT)
|
||||
|
@ -243,7 +245,7 @@ static int status_variables(void) {
|
|||
return log_error_errno(n_order, "Failed to read EFI boot order.");
|
||||
|
||||
/* print entries in BootOrder first */
|
||||
printf("Boot Loader Entries in EFI Variables:\n");
|
||||
printf("Boot Loaders Listed in EFI Variables:\n");
|
||||
for (i = 0; i < n_order; i++)
|
||||
print_efi_option(order[i], true);
|
||||
|
||||
|
@ -264,49 +266,65 @@ static int status_variables(void) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int boot_entry_show(const BootEntry *e, bool show_as_default) {
|
||||
assert(e);
|
||||
|
||||
printf(" title: %s%s%s%s%s%s\n",
|
||||
ansi_highlight(),
|
||||
boot_entry_title(e),
|
||||
ansi_normal(),
|
||||
ansi_highlight_green(),
|
||||
show_as_default ? " (default)" : "",
|
||||
ansi_normal());
|
||||
|
||||
if (e->id)
|
||||
printf(" id: %s\n", e->id);
|
||||
if (e->version)
|
||||
printf(" version: %s\n", e->version);
|
||||
if (e->machine_id)
|
||||
printf(" machine-id: %s\n", e->machine_id);
|
||||
if (e->architecture)
|
||||
printf(" architecture: %s\n", e->architecture);
|
||||
if (e->kernel)
|
||||
printf(" linux: %s\n", e->kernel);
|
||||
if (!strv_isempty(e->initrd)) {
|
||||
_cleanup_free_ char *t;
|
||||
|
||||
t = strv_join(e->initrd, " ");
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
printf(" initrd: %s\n", t);
|
||||
}
|
||||
if (!strv_isempty(e->options)) {
|
||||
_cleanup_free_ char *t;
|
||||
|
||||
t = strv_join(e->options, " ");
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
printf(" options: %s\n", t);
|
||||
}
|
||||
if (e->device_tree)
|
||||
printf(" devicetree: %s\n", e->device_tree);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int status_entries(const char *esp_path, sd_id128_t partition) {
|
||||
int r;
|
||||
|
||||
_cleanup_(boot_config_free) BootConfig config = {};
|
||||
|
||||
printf("Default Boot Entry:\n");
|
||||
int r;
|
||||
|
||||
r = boot_entries_load_config(esp_path, &config);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m",
|
||||
esp_path);
|
||||
return r;
|
||||
|
||||
if (config.default_entry < 0)
|
||||
printf("%zu entries, no entry suitable as default\n", config.n_entries);
|
||||
printf("%zu entries, no entry could be determined as default.\n", config.n_entries);
|
||||
else {
|
||||
const BootEntry *e = &config.entries[config.default_entry];
|
||||
printf("Default Boot Loader Entry:\n");
|
||||
|
||||
printf(" title: %s\n", boot_entry_title(e));
|
||||
if (e->version)
|
||||
printf(" version: %s\n", e->version);
|
||||
if (e->kernel)
|
||||
printf(" linux: %s\n", e->kernel);
|
||||
if (!strv_isempty(e->initrd)) {
|
||||
_cleanup_free_ char *t;
|
||||
|
||||
t = strv_join(e->initrd, " ");
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
printf(" initrd: %s\n", t);
|
||||
}
|
||||
if (!strv_isempty(e->options)) {
|
||||
_cleanup_free_ char *t;
|
||||
|
||||
t = strv_join(e->options, " ");
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
printf(" options: %s\n", t);
|
||||
}
|
||||
if (e->device_tree)
|
||||
printf(" devicetree: %s\n", e->device_tree);
|
||||
puts("");
|
||||
boot_entry_show(config.entries + config.default_entry, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -806,9 +824,9 @@ static int install_loader_config(const char *esp_path) {
|
|||
return log_oom();
|
||||
}
|
||||
|
||||
fprintf(f, "#timeout 3\n");
|
||||
fprintf(f, "#console-mode keep\n");
|
||||
fprintf(f, "default %s-*\n", sd_id128_to_string(machine_id, machine_string));
|
||||
fprintf(f, "#timeout 3\n"
|
||||
"#console-mode keep\n"
|
||||
"default %s-*\n", sd_id128_to_string(machine_id, machine_string));
|
||||
|
||||
r = fflush_sync_and_check(f);
|
||||
if (r < 0)
|
||||
|
@ -840,16 +858,19 @@ static int help(int argc, char *argv[], void *userdata) {
|
|||
" --path=PATH Path to the EFI System Partition (ESP)\n"
|
||||
" -p --print-path Print path to the EFI partition\n"
|
||||
" --no-variables Don't touch EFI variables\n"
|
||||
"\nCommands:\n"
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
"\nBoot Loader Commands:\n"
|
||||
" status Show status of installed systemd-boot and EFI variables\n"
|
||||
" list List boot entries\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"
|
||||
"\nBoot Loader Entries Commands:\n"
|
||||
" list List boot loader entries\n"
|
||||
" set-default ID Set default boot loader entry\n"
|
||||
" set-oneshot ID Set default boot loader entry, for next boot only\n"
|
||||
"\nSee the %s for details.\n"
|
||||
, program_invocation_short_name
|
||||
, link
|
||||
);
|
||||
, link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -859,6 +880,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
ARG_PATH = 0x100,
|
||||
ARG_VERSION,
|
||||
ARG_NO_VARIABLES,
|
||||
ARG_NO_PAGER,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
|
@ -867,7 +889,8 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
{ "path", required_argument, NULL, ARG_PATH },
|
||||
{ "print-path", no_argument, NULL, 'p' },
|
||||
{ "no-variables", no_argument, NULL, ARG_NO_VARIABLES },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{}
|
||||
};
|
||||
|
||||
int c, r;
|
||||
|
@ -899,6 +922,10 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
arg_touch_variables = false;
|
||||
break;
|
||||
|
||||
case ARG_NO_PAGER:
|
||||
arg_no_pager = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -938,6 +965,8 @@ static int verb_status(int argc, char *argv[], void *userdata) {
|
|||
r = 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just show what we
|
||||
* can show */
|
||||
|
||||
(void) pager_open(arg_no_pager, false);
|
||||
|
||||
if (is_efi_boot()) {
|
||||
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
|
||||
sd_id128_t loader_part_uuid = SD_ID128_NULL;
|
||||
|
@ -956,13 +985,13 @@ static int verb_status(int argc, char *argv[], void *userdata) {
|
|||
r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m");
|
||||
|
||||
printf("System:\n");
|
||||
printf(" Firmware: %s (%s)\n", strna(fw_type), strna(fw_info));
|
||||
printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal());
|
||||
printf(" Secure Boot: %sd\n", enable_disable(is_efi_secure_boot()));
|
||||
printf(" Setup Mode: %s\n", is_efi_secure_boot_setup_mode() ? "setup" : "user");
|
||||
printf("\n");
|
||||
|
||||
printf("Current Loader:\n");
|
||||
printf(" Product: %s\n", strna(loader));
|
||||
printf("Current Boot Loader:\n");
|
||||
printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal());
|
||||
if (stub)
|
||||
printf(" Stub: %s\n", stub);
|
||||
if (!sd_id128_is_null(loader_part_uuid))
|
||||
|
@ -998,8 +1027,8 @@ static int verb_status(int argc, char *argv[], void *userdata) {
|
|||
|
||||
static int verb_list(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(boot_config_free) BootConfig config = {};
|
||||
_cleanup_free_ char **found_by_loader = NULL;
|
||||
sd_id128_t uuid = SD_ID128_NULL;
|
||||
unsigned n;
|
||||
int r;
|
||||
|
||||
/* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn
|
||||
|
@ -1014,58 +1043,60 @@ static int verb_list(int argc, char *argv[], void *userdata) {
|
|||
|
||||
r = boot_entries_load_config(arg_path, &config);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load bootspec config from \"%s/loader\": %m",
|
||||
arg_path);
|
||||
return r;
|
||||
|
||||
printf("Available boot entries:\n");
|
||||
r = efi_loader_get_entries(&found_by_loader);
|
||||
if (r < 0 && !IN_SET(r, -ENOENT, -EOPNOTSUPP))
|
||||
log_debug_errno(r, "Failed to acquire boot loader discovered entries: %m");
|
||||
|
||||
for (n = 0; n < config.n_entries; n++) {
|
||||
const BootEntry *e = &config.entries[n];
|
||||
if (config.n_entries == 0)
|
||||
log_info("No boot loader entries found.");
|
||||
else {
|
||||
size_t n;
|
||||
|
||||
printf(" title: %s%s%s%s%s%s\n",
|
||||
ansi_highlight(),
|
||||
boot_entry_title(e),
|
||||
ansi_normal(),
|
||||
ansi_highlight_green(),
|
||||
n == (unsigned) config.default_entry ? " (default)" : "",
|
||||
ansi_normal());
|
||||
if (e->id)
|
||||
printf(" id: %s\n", e->id);
|
||||
if (e->version)
|
||||
printf(" version: %s\n", e->version);
|
||||
if (e->machine_id)
|
||||
printf(" machine-id: %s\n", e->machine_id);
|
||||
if (e->architecture)
|
||||
printf(" architecture: %s\n", e->architecture);
|
||||
if (e->kernel)
|
||||
printf(" linux: %s\n", e->kernel);
|
||||
if (!strv_isempty(e->initrd)) {
|
||||
_cleanup_free_ char *t;
|
||||
(void) pager_open(arg_no_pager, false);
|
||||
|
||||
t = strv_join(e->initrd, " ");
|
||||
if (!t)
|
||||
return log_oom();
|
||||
printf("Boot Loader Entries:\n");
|
||||
|
||||
printf(" initrd: %s\n", t);
|
||||
for (n = 0; n < config.n_entries; n++) {
|
||||
r = boot_entry_show(config.entries + n, n == (size_t) config.default_entry);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
puts("");
|
||||
|
||||
strv_remove(found_by_loader, config.entries[n].id);
|
||||
}
|
||||
if (!strv_isempty(e->options)) {
|
||||
_cleanup_free_ char *t;
|
||||
}
|
||||
|
||||
t = strv_join(e->options, " ");
|
||||
if (!t)
|
||||
return log_oom();
|
||||
if (!strv_isempty(found_by_loader)) {
|
||||
char **i;
|
||||
|
||||
printf(" options: %s\n", t);
|
||||
}
|
||||
if (e->device_tree)
|
||||
printf(" devicetree: %s\n", e->device_tree);
|
||||
printf("Automatic/Other Entries Found by Boot Loader:\n\n");
|
||||
|
||||
puts("");
|
||||
STRV_FOREACH(i, found_by_loader)
|
||||
puts(*i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sync_esp(void) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
if (!arg_path)
|
||||
return 0;
|
||||
|
||||
fd = open(arg_path, O_CLOEXEC|O_DIRECTORY|O_RDONLY);
|
||||
if (fd < 0)
|
||||
return log_error_errno(errno, "Couldn't open ESP '%s' for synchronization: %m", arg_path);
|
||||
|
||||
if (syncfs(fd) < 0)
|
||||
return log_error_errno(errno, "Failed to synchronize the ESP '%s': %m", arg_path);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int verb_install(int argc, char *argv[], void *userdata) {
|
||||
|
||||
sd_id128_t uuid = SD_ID128_NULL;
|
||||
|
@ -1092,6 +1123,8 @@ static int verb_install(int argc, char *argv[], void *userdata) {
|
|||
}
|
||||
}
|
||||
|
||||
(void) sync_esp();
|
||||
|
||||
if (arg_touch_variables)
|
||||
r = install_variables(arg_path,
|
||||
part, pstart, psize, uuid,
|
||||
|
@ -1111,6 +1144,8 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
|
|||
|
||||
r = remove_binaries(arg_path);
|
||||
|
||||
(void) sync_esp();
|
||||
|
||||
if (arg_touch_variables) {
|
||||
int q;
|
||||
|
||||
|
@ -1122,15 +1157,66 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
|
|||
return r;
|
||||
}
|
||||
|
||||
static int verb_set_default(int argc, char *argv[], void *userdata) {
|
||||
const char *name;
|
||||
int r;
|
||||
|
||||
if (!is_efi_boot()) {
|
||||
log_error("Not booted with UEFI.");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (access("/sys/firmware/efi/efivars/LoaderInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f", F_OK) < 0) {
|
||||
if (errno == ENOENT) {
|
||||
log_error_errno(errno, "Not booted with a supported boot loader.");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return log_error_errno(errno, "Failed to detect whether boot loader supports '%s' operation: %m", argv[0]);
|
||||
}
|
||||
|
||||
if (detect_container() > 0) {
|
||||
log_error("'%s' operation not supported in a container.", argv[0]);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (!arg_touch_variables) {
|
||||
log_error("'%s' operation cannot be combined with --touch-variables=no.", argv[0]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
name = streq(argv[0], "set-default") ? "LoaderEntryDefault" : "LoaderEntryOneShot";
|
||||
|
||||
if (isempty(argv[1])) {
|
||||
r = efi_set_variable(EFI_VENDOR_LOADER, name, NULL, 0);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to remove EFI variale: %m");
|
||||
} else {
|
||||
_cleanup_free_ char16_t *encoded = NULL;
|
||||
|
||||
encoded = utf8_to_utf16(argv[1], strlen(argv[1]));
|
||||
if (!encoded)
|
||||
return log_oom();
|
||||
|
||||
r = efi_set_variable(EFI_VENDOR_LOADER, name, encoded, char16_strlen(encoded) * 2 + 2);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to update EFI variable: %m");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bootctl_main(int argc, char *argv[]) {
|
||||
|
||||
static const Verb verbs[] = {
|
||||
{ "help", VERB_ANY, VERB_ANY, 0, help },
|
||||
{ "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
|
||||
{ "list", VERB_ANY, 1, 0, verb_list },
|
||||
{ "install", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
|
||||
{ "update", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
|
||||
{ "remove", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_remove },
|
||||
{ "help", VERB_ANY, VERB_ANY, 0, help },
|
||||
{ "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
|
||||
{ "install", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
|
||||
{ "update", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
|
||||
{ "remove", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_remove },
|
||||
{ "list", VERB_ANY, 1, 0, verb_list },
|
||||
{ "set-default", 2, 2, VERB_MUST_BE_ROOT, verb_set_default },
|
||||
{ "set-oneshot", 2, 2, VERB_MUST_BE_ROOT, verb_set_default },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -1154,6 +1240,8 @@ int main(int argc, char *argv[]) {
|
|||
r = bootctl_main(argc, argv);
|
||||
|
||||
finish:
|
||||
pager_close();
|
||||
free(arg_path);
|
||||
|
||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -175,7 +175,7 @@ static int raw_import_maybe_convert_qcow2(RawImport *i) {
|
|||
if (converted_fd < 0)
|
||||
return log_error_errno(errno, "Failed to create %s: %m", t);
|
||||
|
||||
r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL);
|
||||
r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to set file attributes on %s: %m", t);
|
||||
|
||||
|
@ -260,7 +260,7 @@ static int raw_import_open_disk(RawImport *i) {
|
|||
if (i->output_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open destination %s: %m", i->temp_path);
|
||||
|
||||
r = chattr_fd(i->output_fd, FS_NOCOW_FL, FS_NOCOW_FL);
|
||||
r = chattr_fd(i->output_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path);
|
||||
|
||||
|
|
|
@ -237,7 +237,7 @@ static int raw_pull_maybe_convert_qcow2(RawPull *i) {
|
|||
if (converted_fd < 0)
|
||||
return log_error_errno(errno, "Failed to create %s: %m", t);
|
||||
|
||||
r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL);
|
||||
r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to set file attributes on %s: %m", t);
|
||||
|
||||
|
@ -353,7 +353,7 @@ static int raw_pull_make_local_copy(RawPull *i) {
|
|||
* performance on COW file systems like btrfs, since it
|
||||
* reduces fragmentation caused by not allowing in-place
|
||||
* writes. */
|
||||
r = chattr_fd(dfd, FS_NOCOW_FL, FS_NOCOW_FL);
|
||||
r = chattr_fd(dfd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to set file attributes on %s: %m", tp);
|
||||
|
||||
|
@ -595,7 +595,7 @@ static int raw_pull_job_on_open_disk_raw(PullJob *j) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL);
|
||||
r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to set file attributes on %s, ignoring: %m", i->temp_path);
|
||||
|
||||
|
|
|
@ -372,7 +372,7 @@ JournalFile* journal_file_close(JournalFile *f) {
|
|||
* reenable all the good bits COW usually provides
|
||||
* (such as data checksumming). */
|
||||
|
||||
(void) chattr_fd(f->fd, 0, FS_NOCOW_FL);
|
||||
(void) chattr_fd(f->fd, 0, FS_NOCOW_FL, NULL);
|
||||
(void) btrfs_defrag_fd(f->fd);
|
||||
}
|
||||
|
||||
|
@ -3563,7 +3563,7 @@ int journal_file_open_reliably(
|
|||
/* btrfs doesn't cope well with our write pattern and
|
||||
* fragments heavily. Let's defrag all files we rotate */
|
||||
|
||||
(void) chattr_path(p, 0, FS_NOCOW_FL);
|
||||
(void) chattr_path(p, 0, FS_NOCOW_FL, NULL);
|
||||
(void) btrfs_defrag(p);
|
||||
|
||||
log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
|
||||
|
|
|
@ -1732,7 +1732,7 @@ static int setup_keys(void) {
|
|||
|
||||
/* Enable secure remove, exclusion from dump, synchronous
|
||||
* writing and in-place updating */
|
||||
r = chattr_fd(fd, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL);
|
||||
r = chattr_fd(fd, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL, NULL);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to set file attributes: %m");
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "def.h"
|
||||
#include "device-nodes.h"
|
||||
#include "efivars.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "parse-util.h"
|
||||
|
@ -20,7 +21,7 @@
|
|||
#include "strv.h"
|
||||
#include "virt.h"
|
||||
|
||||
void boot_entry_free(BootEntry *entry) {
|
||||
static void boot_entry_free(BootEntry *entry) {
|
||||
assert(entry);
|
||||
|
||||
free(entry->id);
|
||||
|
@ -37,7 +38,7 @@ void boot_entry_free(BootEntry *entry) {
|
|||
free(entry->device_tree);
|
||||
}
|
||||
|
||||
int boot_entry_load(const char *path, BootEntry *entry) {
|
||||
static int boot_entry_load(const char *path, BootEntry *entry) {
|
||||
_cleanup_(boot_entry_free) BootEntry tmp = {};
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
unsigned line = 1;
|
||||
|
@ -144,7 +145,7 @@ void boot_config_free(BootConfig *config) {
|
|||
free(config->entries);
|
||||
}
|
||||
|
||||
int boot_loader_read_conf(const char *path, BootConfig *config) {
|
||||
static int boot_loader_read_conf(const char *path, BootConfig *config) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
unsigned line = 1;
|
||||
int r;
|
||||
|
@ -153,8 +154,12 @@ int boot_loader_read_conf(const char *path, BootConfig *config) {
|
|||
assert(config);
|
||||
|
||||
f = fopen(path, "re");
|
||||
if (!f)
|
||||
if (!f) {
|
||||
if (errno == ENOENT)
|
||||
return 0;
|
||||
|
||||
return log_error_errno(errno, "Failed to open \"%s\": %m", path);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *buf = NULL, *field = NULL;
|
||||
|
@ -204,14 +209,14 @@ int boot_loader_read_conf(const char *path, BootConfig *config) {
|
|||
return log_error_errno(r, "%s:%u: Error while reading: %m", path, line);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
|
||||
return str_verscmp(a->id, b->id);
|
||||
}
|
||||
|
||||
int boot_entries_find(const char *dir, BootEntry **ret_entries, size_t *ret_n_entries) {
|
||||
static int boot_entries_find(const char *dir, BootEntry **ret_entries, size_t *ret_n_entries) {
|
||||
_cleanup_strv_free_ char **files = NULL;
|
||||
char **f;
|
||||
int r;
|
||||
|
@ -362,24 +367,26 @@ int boot_entries_load_config(const char *esp_path, BootConfig *config) {
|
|||
p = strjoina(esp_path, "/loader/loader.conf");
|
||||
r = boot_loader_read_conf(p, config);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read boot config from \"%s\": %m", p);
|
||||
return r;
|
||||
|
||||
p = strjoina(esp_path, "/loader/entries");
|
||||
r = boot_entries_find(p, &config->entries, &config->n_entries);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read boot entries from \"%s\": %m", p);
|
||||
return r;
|
||||
|
||||
r = boot_entries_uniquify(config->entries, config->n_entries);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to uniquify boot entries: %m");
|
||||
|
||||
r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &config->entry_oneshot);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to read EFI var \"LoaderEntryOneShot\": %m");
|
||||
if (is_efi_boot()) {
|
||||
r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &config->entry_oneshot);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to read EFI var \"LoaderEntryOneShot\": %m");
|
||||
|
||||
r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryDefault", &config->entry_default);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to read EFI var \"LoaderEntryDefault\": %m");
|
||||
r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryDefault", &config->entry_default);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to read EFI var \"LoaderEntryDefault\": %m");
|
||||
}
|
||||
|
||||
config->default_entry = boot_entries_select_default(config);
|
||||
return 0;
|
||||
|
@ -406,28 +413,33 @@ static int verify_esp(
|
|||
struct statfs sfs;
|
||||
sd_id128_t uuid = SD_ID128_NULL;
|
||||
uint32_t part = 0;
|
||||
bool relax_checks;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
||||
relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0;
|
||||
|
||||
/* Non-root user can only check the status, so if an error occured in the following, it does not cause any
|
||||
* issues. Let's also, silence the error messages. */
|
||||
|
||||
if (statfs(p, &sfs) < 0) {
|
||||
/* If we are searching for the mount point, don't generate a log message if we can't find the path */
|
||||
if (errno == ENOENT && searching)
|
||||
return -ENOENT;
|
||||
if (!relax_checks) {
|
||||
if (statfs(p, &sfs) < 0) {
|
||||
/* If we are searching for the mount point, don't generate a log message if we can't find the path */
|
||||
if (errno == ENOENT && searching)
|
||||
return -ENOENT;
|
||||
|
||||
return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
|
||||
"Failed to check file system type of \"%s\": %m", p);
|
||||
}
|
||||
return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno,
|
||||
"Failed to check file system type of \"%s\": %m", p);
|
||||
}
|
||||
|
||||
if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) {
|
||||
if (searching)
|
||||
return -EADDRNOTAVAIL;
|
||||
if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) {
|
||||
if (searching)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
|
||||
return -ENODEV;
|
||||
log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
if (stat(p, &st) < 0)
|
||||
|
@ -452,7 +464,7 @@ static int verify_esp(
|
|||
|
||||
/* In a container we don't have access to block devices, skip this part of the verification, we trust the
|
||||
* container manager set everything up correctly on its own. Also skip the following verification for non-root user. */
|
||||
if (detect_container() > 0 || unprivileged_mode)
|
||||
if (detect_container() > 0 || unprivileged_mode || relax_checks)
|
||||
goto finish;
|
||||
|
||||
#if HAVE_BLKID
|
||||
|
|
|
@ -39,11 +39,6 @@ typedef struct BootConfig {
|
|||
ssize_t default_entry;
|
||||
} BootConfig;
|
||||
|
||||
void boot_entry_free(BootEntry *entry);
|
||||
int boot_entry_load(const char *path, BootEntry *entry);
|
||||
int boot_entries_find(const char *dir, BootEntry **entries, size_t *n_entries);
|
||||
|
||||
int boot_loader_read_conf(const char *path, BootConfig *config);
|
||||
void boot_config_free(BootConfig *config);
|
||||
int boot_entries_load_config(const char *esp_path, BootConfig *config);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <linux/fs.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -13,6 +14,7 @@
|
|||
#include "sd-id128.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "chattr-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "efivars.h"
|
||||
#include "fd-util.h"
|
||||
|
@ -20,6 +22,7 @@
|
|||
#include "macro.h"
|
||||
#include "parse-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "strv.h"
|
||||
#include "time-util.h"
|
||||
#include "utf8.h"
|
||||
#include "util.h"
|
||||
|
@ -63,7 +66,10 @@ struct device_path {
|
|||
} _packed_;
|
||||
|
||||
bool is_efi_boot(void) {
|
||||
return access("/sys/firmware/efi", F_OK) >= 0;
|
||||
if (detect_container() > 0)
|
||||
return false;
|
||||
|
||||
return access("/sys/firmware/efi/", F_OK) >= 0;
|
||||
}
|
||||
|
||||
static int read_flag(const char *varname) {
|
||||
|
@ -72,6 +78,9 @@ static int read_flag(const char *varname) {
|
|||
size_t s;
|
||||
int r;
|
||||
|
||||
if (!is_efi_boot()) /* If this is not an EFI boot, assume the queried flags are zero */
|
||||
return 0;
|
||||
|
||||
r = efi_get_variable(EFI_VENDOR_GLOBAL, varname, NULL, &v, &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -80,7 +89,7 @@ static int read_flag(const char *varname) {
|
|||
return -EINVAL;
|
||||
|
||||
b = *(uint8_t *)v;
|
||||
return b > 0;
|
||||
return !!b;
|
||||
}
|
||||
|
||||
bool is_efi_secure_boot(void) {
|
||||
|
@ -97,7 +106,7 @@ int efi_reboot_to_firmware_supported(void) {
|
|||
size_t s;
|
||||
int r;
|
||||
|
||||
if (!is_efi_boot() || detect_container() > 0)
|
||||
if (!is_efi_boot())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s);
|
||||
|
@ -120,21 +129,18 @@ static int get_os_indications(uint64_t *os_indication) {
|
|||
size_t s;
|
||||
int r;
|
||||
|
||||
/* Let's verify general support first */
|
||||
r = efi_reboot_to_firmware_supported();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s);
|
||||
if (r == -ENOENT) {
|
||||
/* Some firmware implementations that do support
|
||||
* OsIndications and report that with
|
||||
* OsIndicationsSupported will remove the
|
||||
* OsIndications variable when it is unset. Let's
|
||||
* pretend it's 0 then, to hide this implementation
|
||||
* detail. Note that this call will return -ENOENT
|
||||
* then only if the support for OsIndications is
|
||||
* missing entirely, as determined by
|
||||
* efi_reboot_to_firmware_supported() above. */
|
||||
/* Some firmware implementations that do support OsIndications and report that with
|
||||
* OsIndicationsSupported will remove the OsIndications variable when it is unset. Let's pretend it's 0
|
||||
* then, to hide this implementation detail. Note that this call will return -ENOENT then only if the
|
||||
* support for OsIndications is missing entirely, as determined by efi_reboot_to_firmware_supported()
|
||||
* above. */
|
||||
*os_indication = 0;
|
||||
return 0;
|
||||
} else if (r < 0)
|
||||
|
@ -252,6 +258,9 @@ int efi_set_variable(
|
|||
} _packed_ * _cleanup_free_ buf = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
bool saved_flags_valid = false;
|
||||
unsigned saved_flags;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
assert(value || size == 0);
|
||||
|
@ -261,24 +270,60 @@ int efi_set_variable(
|
|||
name, SD_ID128_FORMAT_VAL(vendor)) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Newer efivarfs protects variables that are not in a whitelist with FS_IMMUTABLE_FL by default, to protect
|
||||
* them for accidental removal and modification. We are not changing these variables accidentally however,
|
||||
* hence let's unset the bit first. */
|
||||
|
||||
r = chattr_path(p, 0, FS_IMMUTABLE_FL, &saved_flags);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_debug_errno(r, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p);
|
||||
|
||||
saved_flags_valid = r >= 0;
|
||||
|
||||
if (size == 0) {
|
||||
if (unlink(p) < 0)
|
||||
return -errno;
|
||||
if (unlink(p) < 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
if (fd < 0) {
|
||||
r = -errno;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
buf = malloc(sizeof(uint32_t) + size);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
if (!buf) {
|
||||
r = -ENOMEM;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
buf->attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
|
||||
memcpy(buf->buf, value, size);
|
||||
|
||||
return loop_write(fd, buf, sizeof(uint32_t) + size, false);
|
||||
r = loop_write(fd, buf, sizeof(uint32_t) + size, false);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = 0;
|
||||
|
||||
finish:
|
||||
if (saved_flags_valid) {
|
||||
int q;
|
||||
|
||||
/* Restore the original flags field, just in case */
|
||||
if (fd < 0)
|
||||
q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL, NULL);
|
||||
else
|
||||
q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL, NULL);
|
||||
if (q < 0)
|
||||
log_debug_errno(q, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int efi_get_variable_string(sd_id128_t vendor, const char *name, char **p) {
|
||||
|
@ -344,6 +389,9 @@ int efi_get_boot_option(
|
|||
sd_id128_t p_uuid = SD_ID128_NULL;
|
||||
int r;
|
||||
|
||||
if (!is_efi_boot())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
xsprintf(boot_id, "Boot%04X", id);
|
||||
r = efi_get_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, (void **)&buf, &l);
|
||||
if (r < 0)
|
||||
|
@ -455,23 +503,30 @@ static uint16_t *tilt_slashes(uint16_t *s) {
|
|||
return s;
|
||||
}
|
||||
|
||||
int efi_add_boot_option(uint16_t id, const char *title,
|
||||
uint32_t part, uint64_t pstart, uint64_t psize,
|
||||
sd_id128_t part_uuid, const char *path) {
|
||||
char boot_id[9];
|
||||
size_t size;
|
||||
size_t title_len;
|
||||
size_t path_len;
|
||||
int efi_add_boot_option(
|
||||
uint16_t id,
|
||||
const char *title,
|
||||
uint32_t part,
|
||||
uint64_t pstart,
|
||||
uint64_t psize,
|
||||
sd_id128_t part_uuid,
|
||||
const char *path) {
|
||||
|
||||
size_t size, title_len, path_len;
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
struct boot_option *option;
|
||||
struct device_path *devicep;
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
char boot_id[9];
|
||||
|
||||
if (!is_efi_boot())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
title_len = (strlen(title)+1) * 2;
|
||||
path_len = (strlen(path)+1) * 2;
|
||||
|
||||
buf = calloc(sizeof(struct boot_option) + title_len +
|
||||
sizeof(struct drive_path) +
|
||||
sizeof(struct device_path) + path_len, 1);
|
||||
buf = malloc0(sizeof(struct boot_option) + title_len +
|
||||
sizeof(struct drive_path) +
|
||||
sizeof(struct device_path) + path_len);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -520,6 +575,9 @@ int efi_add_boot_option(uint16_t id, const char *title,
|
|||
int efi_remove_boot_option(uint16_t id) {
|
||||
char boot_id[9];
|
||||
|
||||
if (!is_efi_boot())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
xsprintf(boot_id, "Boot%04X", id);
|
||||
return efi_set_variable(EFI_VENDOR_GLOBAL, boot_id, NULL, 0);
|
||||
}
|
||||
|
@ -529,6 +587,9 @@ int efi_get_boot_order(uint16_t **order) {
|
|||
size_t l;
|
||||
int r;
|
||||
|
||||
if (!is_efi_boot())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = efi_get_variable(EFI_VENDOR_GLOBAL, "BootOrder", NULL, &buf, &l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -545,12 +606,15 @@ int efi_get_boot_order(uint16_t **order) {
|
|||
}
|
||||
|
||||
int efi_set_boot_order(uint16_t *order, size_t n) {
|
||||
|
||||
if (!is_efi_boot())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return efi_set_variable(EFI_VENDOR_GLOBAL, "BootOrder", order, n * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
static int boot_id_hex(const char s[4]) {
|
||||
int i;
|
||||
int id = 0;
|
||||
int id = 0, i;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
if (s[i] >= '0' && s[i] <= '9')
|
||||
|
@ -569,13 +633,16 @@ static int cmp_uint16(const uint16_t *a, const uint16_t *b) {
|
|||
|
||||
int efi_get_boot_options(uint16_t **options) {
|
||||
_cleanup_closedir_ DIR *dir = NULL;
|
||||
struct dirent *de;
|
||||
_cleanup_free_ uint16_t *list = NULL;
|
||||
struct dirent *de;
|
||||
size_t alloc = 0;
|
||||
int count = 0;
|
||||
|
||||
assert(options);
|
||||
|
||||
if (!is_efi_boot())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
dir = opendir("/sys/firmware/efi/efivars/");
|
||||
if (!dir)
|
||||
return -errno;
|
||||
|
@ -636,6 +703,9 @@ int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
|
|||
assert(firmware);
|
||||
assert(loader);
|
||||
|
||||
if (!is_efi_boot())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = read_usec(EFI_VENDOR_LOADER, "LoaderTimeInitUSec", &x);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -660,6 +730,9 @@ int efi_loader_get_device_part_uuid(sd_id128_t *u) {
|
|||
_cleanup_free_ char *p = NULL;
|
||||
int r, parsed[16];
|
||||
|
||||
if (!is_efi_boot())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -681,6 +754,56 @@ int efi_loader_get_device_part_uuid(sd_id128_t *u) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int efi_loader_get_entries(char ***ret) {
|
||||
_cleanup_free_ char16_t *entries = NULL;
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
size_t size, i, start;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (!is_efi_boot())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = efi_get_variable(EFI_VENDOR_LOADER, "LoaderEntries", NULL, (void**) &entries, &size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* The variable contains a series of individually NUL terminated UTF-16 strings. */
|
||||
|
||||
for (i = 0, start = 0;; i++) {
|
||||
char *decoded;
|
||||
bool end;
|
||||
|
||||
/* Is this the end of the variable's data? */
|
||||
end = i * sizeof(char16_t) >= size;
|
||||
|
||||
/* Are we in the middle of a string? (i.e. not at the end of the variable, nor at a NUL terminator?) If
|
||||
* so, let's go to the next entry. */
|
||||
if (!end && entries[i] != 0)
|
||||
continue;
|
||||
|
||||
/* We reached the end of a string, let's decode it into UTF-8 */
|
||||
decoded = utf16_to_utf8(entries + start, (i - start) * sizeof(char16_t));
|
||||
if (!decoded)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_consume(&l, decoded);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* We reached the end of the variable */
|
||||
if (end)
|
||||
break;
|
||||
|
||||
/* Continue after the NUL byte */
|
||||
start = i + 1;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
char *efi_tilt_backslashes(char *s) {
|
||||
|
|
|
@ -41,6 +41,8 @@ int efi_get_boot_options(uint16_t **options);
|
|||
int efi_loader_get_device_part_uuid(sd_id128_t *u);
|
||||
int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader);
|
||||
|
||||
int efi_loader_get_entries(char ***ret);
|
||||
|
||||
#else
|
||||
|
||||
static inline bool is_efi_boot(void) {
|
||||
|
@ -111,6 +113,10 @@ static inline int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int efi_loader_get_entries(char ***ret) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
char *efi_tilt_backslashes(char *s);
|
||||
|
|
|
@ -637,7 +637,7 @@ int image_remove(Image *i) {
|
|||
|
||||
case IMAGE_DIRECTORY:
|
||||
/* Allow deletion of read-only directories */
|
||||
(void) chattr_path(i->path, 0, FS_IMMUTABLE_FL);
|
||||
(void) chattr_path(i->path, 0, FS_IMMUTABLE_FL, NULL);
|
||||
r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -736,7 +736,7 @@ int image_rename(Image *i, const char *new_name) {
|
|||
(void) read_attr_path(i->path, &file_attr);
|
||||
|
||||
if (file_attr & FS_IMMUTABLE_FL)
|
||||
(void) chattr_path(i->path, 0, FS_IMMUTABLE_FL);
|
||||
(void) chattr_path(i->path, 0, FS_IMMUTABLE_FL, NULL);
|
||||
|
||||
_fallthrough_;
|
||||
case IMAGE_SUBVOLUME:
|
||||
|
@ -777,7 +777,7 @@ int image_rename(Image *i, const char *new_name) {
|
|||
|
||||
/* Restore the immutable bit, if it was set before */
|
||||
if (file_attr & FS_IMMUTABLE_FL)
|
||||
(void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL);
|
||||
(void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL, NULL);
|
||||
|
||||
free_and_replace(i->path, new_path);
|
||||
free_and_replace(i->name, nn);
|
||||
|
@ -927,7 +927,7 @@ int image_read_only(Image *i, bool b) {
|
|||
a read-only subvolume, but at least something, and
|
||||
we can read the value back. */
|
||||
|
||||
r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL);
|
||||
r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
|
@ -8,19 +8,15 @@
|
|||
#include "util.h"
|
||||
|
||||
static int test_acpi_fpdt(void) {
|
||||
usec_t loader_start;
|
||||
usec_t loader_exit;
|
||||
char ts_start[FORMAT_TIMESPAN_MAX];
|
||||
char ts_exit[FORMAT_TIMESPAN_MAX];
|
||||
char ts_span[FORMAT_TIMESPAN_MAX];
|
||||
char ts_start[FORMAT_TIMESPAN_MAX], ts_exit[FORMAT_TIMESPAN_MAX], ts_span[FORMAT_TIMESPAN_MAX];
|
||||
usec_t loader_start, loader_exit;
|
||||
int r;
|
||||
|
||||
r = acpi_get_boot_usec(&loader_start, &loader_exit);
|
||||
if (r < 0) {
|
||||
bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES) || r == -ENODATA;
|
||||
|
||||
log_full_errno(ok ? LOG_DEBUG : LOG_ERR,
|
||||
r, "Failed to read ACPI FPDT: %m");
|
||||
log_full_errno(ok ? LOG_DEBUG : LOG_ERR, r, "Failed to read ACPI FPDT: %m");
|
||||
return ok ? 0 : r;
|
||||
}
|
||||
|
||||
|
@ -32,19 +28,15 @@ static int test_acpi_fpdt(void) {
|
|||
}
|
||||
|
||||
static int test_efi_loader(void) {
|
||||
usec_t loader_start;
|
||||
usec_t loader_exit;
|
||||
char ts_start[FORMAT_TIMESPAN_MAX];
|
||||
char ts_exit[FORMAT_TIMESPAN_MAX];
|
||||
char ts_span[FORMAT_TIMESPAN_MAX];
|
||||
char ts_start[FORMAT_TIMESPAN_MAX], ts_exit[FORMAT_TIMESPAN_MAX], ts_span[FORMAT_TIMESPAN_MAX];
|
||||
usec_t loader_start, loader_exit;
|
||||
int r;
|
||||
|
||||
r = efi_loader_get_boot_usec(&loader_start, &loader_exit);
|
||||
if (r < 0) {
|
||||
bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES);
|
||||
bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES) || r == -EOPNOTSUPP;
|
||||
|
||||
log_full_errno(ok ? LOG_DEBUG : LOG_ERR,
|
||||
r, "Failed to read EFI loader data: %m");
|
||||
log_full_errno(ok ? LOG_DEBUG : LOG_ERR, r, "Failed to read EFI loader data: %m");
|
||||
return ok ? 0 : r;
|
||||
}
|
||||
|
||||
|
@ -57,17 +49,16 @@ static int test_efi_loader(void) {
|
|||
|
||||
static int test_boot_timestamps(void) {
|
||||
char s[MAX(FORMAT_TIMESPAN_MAX, FORMAT_TIMESTAMP_MAX)];
|
||||
int r;
|
||||
dual_timestamp fw, l, k;
|
||||
int r;
|
||||
|
||||
dual_timestamp_from_monotonic(&k, 0);
|
||||
|
||||
r = boot_timestamps(NULL, &fw, &l);
|
||||
if (r < 0) {
|
||||
bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES);
|
||||
bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES) || r == -EOPNOTSUPP;
|
||||
|
||||
log_full_errno(ok ? LOG_DEBUG : LOG_ERR,
|
||||
r, "Failed to read variables: %m");
|
||||
log_full_errno(ok ? LOG_DEBUG : LOG_ERR, r, "Failed to read variables: %m");
|
||||
return ok ? 0 : r;
|
||||
}
|
||||
|
||||
|
|
|
@ -1232,7 +1232,7 @@ static int fd_set_attribute(Item *item, int fd, const char *path, const struct s
|
|||
if (procfs_fd < 0)
|
||||
return log_error_errno(procfs_fd, "Failed to re-open '%s': %m", path);
|
||||
|
||||
r = chattr_fd(procfs_fd, f, item->attribute_mask);
|
||||
r = chattr_fd(procfs_fd, f, item->attribute_mask, NULL);
|
||||
if (r < 0)
|
||||
log_full_errno(IN_SET(r, -ENOTTY, -EOPNOTSUPP) ? LOG_DEBUG : LOG_WARNING,
|
||||
r,
|
||||
|
|
Loading…
Reference in New Issue