Merge pull request #10495 from poettering/login-boot-hookup
add "systemctl reboot --boot-loader-menu=" and "systemctl reboot --boot-loader-entry="
This commit is contained in:
@@ -91,17 +91,12 @@ Features:
|
||||
show state of these flags, and optionally trigger such a factory reset on
|
||||
next boot by setting the flag.
|
||||
|
||||
* sd-boot: add "oneshot boot timeout" variable support
|
||||
|
||||
* sd-boot: automatically load EFI modules from some drop-in dir, so that people
|
||||
can add in file system drivers and such
|
||||
|
||||
* sd-boot: optionally, show boot menu when previous default boot item has
|
||||
non-zero "tries done" count
|
||||
|
||||
* logind: add "boot into bootmenu" API, and possibly even "boot into windows"
|
||||
and "boot into macos".
|
||||
|
||||
* maybe set a special xattr on cgroups that have delegate=yes set, to make it
|
||||
easy to mark cut points
|
||||
|
||||
|
||||
@@ -107,6 +107,51 @@ systemd-udevd:
|
||||
with `:` in which case the kernel command line option takes precedence, if it
|
||||
is specified as well.
|
||||
|
||||
* `$SYSTEMD_REBOOT_TO_FIRMWARE_SETUP` — if set overrides systemd-logind's
|
||||
built-in EFI logic of requesting a reboot into the firmware. Takes a
|
||||
boolean. If set to false the functionality is turned off entirely. If set to
|
||||
true instead of requesting a reboot into the firmware setup UI through EFI a
|
||||
file `/run/systemd/reboot-to-firmware-setup` is created whenever this is
|
||||
requested. This file may be checked for by services run during system
|
||||
shutdown in order to request the appropriate operation from the firmware in
|
||||
an alternative fashion.
|
||||
|
||||
* `$SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU` — similar to the above, allows
|
||||
overriding of systemd-logind's built-in EFI logic of requesting a reboot into
|
||||
the boot loader menu. Takes a boolean. If set to false the functionality is
|
||||
turned off entirely. If set to true instead of requesting a reboot into the
|
||||
boot loader menu through EFI a file `/run/systemd/reboot-to-boot-loader-menu`
|
||||
is created whenever this is requested. The file contains the requested boot
|
||||
loader menu timeout in µs, formatted in ASCII decimals, or zero in case no
|
||||
time-out is requested. This file may be checked for by services run during
|
||||
system shutdown in order to request the appropriate operation from the boot
|
||||
loader in an alternative fashion.
|
||||
|
||||
* `$SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY` — similar to the above, allows
|
||||
overriding of systemd-logind's built-in EFI logic of requesting a reboot into
|
||||
a specific boot loader entry. Takes a boolean. If set to false the
|
||||
functionality is turned off entirely. If set to true instead of requesting a
|
||||
reboot into a specific boot loader entry through EFI a file
|
||||
`/run/systemd/reboot-to-boot-loader-entry` is created whenever this is
|
||||
requested. The file contains the requested boot loader entry identifier. This
|
||||
file may be checked for by services run during system shutdown in order to
|
||||
request the appropriate operation from the boot loader in an alternative
|
||||
fashion. Note that by default only boot loader entries which follow the [Boot
|
||||
Loader Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION) and are
|
||||
placed in the ESP or the Extended Boot Loader partition may be selected this
|
||||
way. However, if a directory `/run/boot-loader-entries/` exists, the entries
|
||||
are loaded from there instead. The directory should contain the usual
|
||||
directory hierarchy mandated by the Boot Loader Specification, i.e. the entry
|
||||
drop-ins should be placed in
|
||||
`/run/boot-loader-entries/loader/entries/*.conf`, and the files referenced by
|
||||
the drop-ins (including the kernels and initrds) somewhere else below
|
||||
`/run/boot-loader-entries/`. Note that all these files may be (and are
|
||||
supposed to be) symlinks. systemd-logind will load these files on-demand,
|
||||
these files can hence be updated (ideally atomically) whenever the boot
|
||||
loader configuration changes. A foreign boot loader installer script should
|
||||
hence synthesize drop-in snippets and symlinks for all boot entries at boot
|
||||
or whenever they change if it wants to integrate with systemd-logind's APIs.
|
||||
|
||||
installed systemd tests:
|
||||
|
||||
* `$SYSTEMD_TEST_DATA` — override the location of test data. This is useful if
|
||||
|
||||
+24
-5
@@ -617,11 +617,30 @@
|
||||
<term><option>--firmware-setup</option></term>
|
||||
|
||||
<listitem>
|
||||
<para>When used with the <command>reboot</command> command,
|
||||
indicate to the system's firmware to boot into setup
|
||||
mode. Note that this is currently only supported on some EFI
|
||||
systems and only if the system was booted in EFI
|
||||
mode.</para>
|
||||
<para>When used with the <command>reboot</command> command, indicate to the system's firmware to reboot into
|
||||
the firmware setup interface. Note that this functionality is not available on all systems.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--boot-loader-menu=</option></term>
|
||||
|
||||
<listitem>
|
||||
<para>When used with the <command>reboot</command> command, indicate to the system's boot loader to show the
|
||||
boot loader menu on the following boot. Takes a time value as parameter — indicating the menu time-out. Pass
|
||||
zero in order to disable the menu time-out. Note that not all boot loaders support this
|
||||
functionality.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--boot-loader-entry=</option></term>
|
||||
|
||||
<listitem>
|
||||
<para>When used with the <command>reboot</command> command, indicate to the system's boot loader to boot into
|
||||
a specific boot loader entry on the following boot. Takes a boot loader entry identifier as argument, or
|
||||
<literal>help</literal> in order to list available entries. Note that not all boot loaders support this
|
||||
functionality.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
+7
-18
@@ -311,13 +311,15 @@ static int status_variables(void) {
|
||||
static int boot_entry_show(const BootEntry *e, bool show_as_default) {
|
||||
assert(e);
|
||||
|
||||
printf(" title: %s%s%s%s%s%s\n",
|
||||
printf(" title: %s%s%s%s%s%s\n"
|
||||
" type: %s\n",
|
||||
ansi_highlight(),
|
||||
boot_entry_title(e),
|
||||
ansi_normal(),
|
||||
ansi_highlight_green(),
|
||||
show_as_default ? " (default)" : "",
|
||||
ansi_normal());
|
||||
ansi_normal(),
|
||||
boot_entry_type_to_string(e->type));
|
||||
|
||||
if (e->id)
|
||||
printf(" id: %s\n", e->id);
|
||||
@@ -1173,7 +1175,6 @@ 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;
|
||||
int r;
|
||||
|
||||
/* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn
|
||||
@@ -1196,9 +1197,7 @@ static int verb_list(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
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");
|
||||
(void) boot_entries_augment_from_loader(&config, false);
|
||||
|
||||
if (config.n_entries == 0)
|
||||
log_info("No boot loader entries found.");
|
||||
@@ -1214,21 +1213,11 @@ static int verb_list(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
puts("");
|
||||
|
||||
strv_remove(found_by_loader, config.entries[n].id);
|
||||
if (n+1 < config.n_entries)
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
if (!strv_isempty(found_by_loader)) {
|
||||
char **i;
|
||||
|
||||
printf("Automatic/Other Entries Found by Boot Loader:\n\n");
|
||||
|
||||
STRV_FOREACH(i, found_by_loader)
|
||||
puts(*i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+537
-31
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "audit-util.h"
|
||||
#include "bootspec.h"
|
||||
#include "bus-common-errors.h"
|
||||
#include "bus-error.h"
|
||||
#include "bus-unit-util.h"
|
||||
@@ -18,6 +19,7 @@
|
||||
#include "device-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "efivars.h"
|
||||
#include "env-util.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio-label.h"
|
||||
@@ -27,11 +29,13 @@
|
||||
#include "logind.h"
|
||||
#include "missing_capability.h"
|
||||
#include "mkdir.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "selinux-util.h"
|
||||
#include "sleep-config.h"
|
||||
#include "special.h"
|
||||
#include "stdio-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
@@ -2392,9 +2396,24 @@ static int property_get_reboot_to_firmware_setup(
|
||||
assert(reply);
|
||||
assert(userdata);
|
||||
|
||||
r = efi_get_reboot_to_firmware();
|
||||
if (r < 0 && r != -EOPNOTSUPP)
|
||||
log_warning_errno(r, "Failed to determine reboot-to-firmware state: %m");
|
||||
r = getenv_bool("SYSTEMD_REBOOT_TO_FIRMWARE_SETUP");
|
||||
if (r == -ENXIO) {
|
||||
/* EFI case: let's see what is currently configured in the EFI variables */
|
||||
r = efi_get_reboot_to_firmware();
|
||||
if (r < 0 && r != -EOPNOTSUPP)
|
||||
log_warning_errno(r, "Failed to determine reboot-to-firmware-setup state: %m");
|
||||
} else if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP: %m");
|
||||
else if (r > 0) {
|
||||
/* Non-EFI case: let's see whether /run/systemd/reboot-to-firmware-setup exists. */
|
||||
if (access("/run/systemd/reboot-to-firmware-setup", F_OK) < 0) {
|
||||
if (errno != ENOENT)
|
||||
log_warning_errno(errno, "Failed to check whether /run/systemd/reboot-to-firmware-setup exists: %m");
|
||||
|
||||
r = false;
|
||||
} else
|
||||
r = true;
|
||||
}
|
||||
|
||||
return sd_bus_message_append(reply, "b", r > 0);
|
||||
}
|
||||
@@ -2404,8 +2423,9 @@ static int method_set_reboot_to_firmware_setup(
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
int b, r;
|
||||
Manager *m = userdata;
|
||||
bool use_efi;
|
||||
int b, r;
|
||||
|
||||
assert(message);
|
||||
assert(m);
|
||||
@@ -2414,6 +2434,29 @@ static int method_set_reboot_to_firmware_setup(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = getenv_bool("SYSTEMD_REBOOT_TO_FIRMWARE_SETUP");
|
||||
if (r == -ENXIO) {
|
||||
/* EFI case: let's see what the firmware supports */
|
||||
|
||||
r = efi_reboot_to_firmware_supported();
|
||||
if (r == -EOPNOTSUPP)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Firmware does not support boot into firmware.");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
use_efi = true;
|
||||
|
||||
} else if (r <= 0) {
|
||||
/* non-EFI case: $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP is set to off */
|
||||
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP: %m");
|
||||
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Firmware does not support boot into firmware.");
|
||||
} else
|
||||
/* non-EFI case: $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP is set to on */
|
||||
use_efi = false;
|
||||
|
||||
r = bus_verify_polkit_async(message,
|
||||
CAP_SYS_ADMIN,
|
||||
"org.freedesktop.login1.set-reboot-to-firmware-setup",
|
||||
@@ -2427,41 +2470,37 @@ static int method_set_reboot_to_firmware_setup(
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
|
||||
r = efi_set_reboot_to_firmware(b);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (use_efi) {
|
||||
r = efi_set_reboot_to_firmware(b);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
if (b) {
|
||||
r = touch("/run/systemd/reboot-to-firmware-setup");
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
if (unlink("/run/systemd/reboot-to-firmware-setup") < 0 && errno != ENOENT)
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
return sd_bus_reply_method_return(message, NULL);
|
||||
}
|
||||
|
||||
static int method_can_reboot_to_firmware_setup(
|
||||
static int return_test_polkit(
|
||||
sd_bus_message *message,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
int capability,
|
||||
const char *action,
|
||||
const char **details,
|
||||
uid_t good_user,
|
||||
sd_bus_error *e) {
|
||||
|
||||
int r;
|
||||
bool challenge;
|
||||
const char *result;
|
||||
Manager *m = userdata;
|
||||
bool challenge;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(m);
|
||||
|
||||
r = efi_reboot_to_firmware_supported();
|
||||
if (r < 0) {
|
||||
if (r != -EOPNOTSUPP)
|
||||
log_warning_errno(r, "Failed to determine whether reboot to firmware is supported: %m");
|
||||
|
||||
return sd_bus_reply_method_return(message, "s", "na");
|
||||
}
|
||||
|
||||
r = bus_test_polkit(message,
|
||||
CAP_SYS_ADMIN,
|
||||
"org.freedesktop.login1.set-reboot-to-firmware-setup",
|
||||
NULL,
|
||||
UID_INVALID,
|
||||
&challenge,
|
||||
error);
|
||||
r = bus_test_polkit(message, capability, action, details, good_user, &challenge, e);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -2475,6 +2514,466 @@ static int method_can_reboot_to_firmware_setup(
|
||||
return sd_bus_reply_method_return(message, "s", result);
|
||||
}
|
||||
|
||||
static int method_can_reboot_to_firmware_setup(
|
||||
sd_bus_message *message,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Manager *m = userdata;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(m);
|
||||
|
||||
r = getenv_bool("SYSTEMD_REBOOT_TO_FIRMWARE_SETUP");
|
||||
if (r == -ENXIO) {
|
||||
/* EFI case: let's see what the firmware supports */
|
||||
|
||||
r = efi_reboot_to_firmware_supported();
|
||||
if (r < 0) {
|
||||
if (r != -EOPNOTSUPP)
|
||||
log_warning_errno(r, "Failed to determine whether reboot to firmware is supported: %m");
|
||||
|
||||
return sd_bus_reply_method_return(message, "s", "na");
|
||||
}
|
||||
|
||||
} else if (r <= 0) {
|
||||
/* Non-EFI case: let's trust $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP */
|
||||
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP: %m");
|
||||
|
||||
return sd_bus_reply_method_return(message, "s", "na");
|
||||
}
|
||||
|
||||
return return_test_polkit(
|
||||
message,
|
||||
CAP_SYS_ADMIN,
|
||||
"org.freedesktop.login1.set-reboot-to-firmware-setup",
|
||||
NULL,
|
||||
UID_INVALID,
|
||||
error);
|
||||
}
|
||||
|
||||
static int property_get_reboot_to_boot_loader_menu(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
uint64_t x = UINT64_MAX;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
assert(userdata);
|
||||
|
||||
r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU");
|
||||
if (r == -ENXIO) {
|
||||
_cleanup_free_ char *v = NULL;
|
||||
|
||||
/* EFI case: returns the current value of LoaderConfigTimeoutOneShot. Three cases are distuingished:
|
||||
*
|
||||
* 1. Variable not set, boot into boot loader menu is not enabled (we return UINT64_MAX to the user)
|
||||
* 2. Variable set to "0", boot into boot loader menu is enabled with no timeout (we return 0 to the user)
|
||||
* 3. Variable set to numeric value formatted in ASCII, boot into boot loader menu with the specified timeout in seconds
|
||||
*/
|
||||
|
||||
r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot", &v);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
log_warning_errno(r, "Failed to read LoaderConfigTimeoutOneShot variable: %m");
|
||||
} else {
|
||||
uint64_t sec;
|
||||
|
||||
r = safe_atou64(v, &sec);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse LoaderConfigTimeoutOneShot value '%s': %m", v);
|
||||
else if (sec > (USEC_INFINITY / USEC_PER_SEC))
|
||||
log_warning("LoaderConfigTimeoutOneShot too large, ignoring: %m");
|
||||
else
|
||||
x = sec * USEC_PER_SEC; /* return in µs */
|
||||
}
|
||||
|
||||
} else if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU: %m");
|
||||
else if (r > 0) {
|
||||
_cleanup_free_ char *v = NULL;
|
||||
|
||||
/* Non-EFI case, let's process /run/systemd/reboot-to-boot-loader-menu. */
|
||||
|
||||
r = read_one_line_file("/run/systemd/reboot-to-boot-loader-menu", &v);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
log_warning_errno(r, "Failed to read /run/systemd/reboot-to-boot-loader-menu: %m");
|
||||
} else {
|
||||
r = safe_atou64(v, &x);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse /run/systemd/reboot-to-boot-loader-menu: %m");
|
||||
}
|
||||
}
|
||||
|
||||
return sd_bus_message_append(reply, "t", x);
|
||||
}
|
||||
|
||||
static int method_set_reboot_to_boot_loader_menu(
|
||||
sd_bus_message *message,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Manager *m = userdata;
|
||||
bool use_efi;
|
||||
uint64_t x;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(m);
|
||||
|
||||
r = sd_bus_message_read(message, "t", &x);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU");
|
||||
if (r == -ENXIO) {
|
||||
uint64_t features;
|
||||
|
||||
/* EFI case: let's see if booting into boot loader menu is supported. */
|
||||
|
||||
r = efi_loader_get_features(&features);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to determine whether reboot to boot loader menu is supported: %m");
|
||||
if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader does not support boot into boot loader menu.");
|
||||
|
||||
use_efi = true;
|
||||
|
||||
} else if (r <= 0) {
|
||||
/* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU is set to off */
|
||||
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU: %m");
|
||||
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader does not support boot into boot loader menu.");
|
||||
} else
|
||||
/* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU is set to on */
|
||||
use_efi = false;
|
||||
|
||||
r = bus_verify_polkit_async(message,
|
||||
CAP_SYS_ADMIN,
|
||||
"org.freedesktop.login1.set-reboot-to-boot-loader-menu",
|
||||
NULL,
|
||||
false,
|
||||
UID_INVALID,
|
||||
&m->polkit_registry,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
|
||||
if (use_efi) {
|
||||
if (x == UINT64_MAX)
|
||||
r = efi_set_variable(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot", NULL, 0);
|
||||
else {
|
||||
char buf[DECIMAL_STR_MAX(uint64_t) + 1];
|
||||
xsprintf(buf, "%" PRIu64, DIV_ROUND_UP(x, USEC_PER_SEC)); /* second granularity */
|
||||
|
||||
r = efi_set_variable_string(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot", buf);
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
if (x == UINT64_MAX) {
|
||||
if (unlink("/run/systemd/reboot-to-loader-menu") < 0 && errno != ENOENT)
|
||||
return -errno;
|
||||
} else {
|
||||
char buf[DECIMAL_STR_MAX(uint64_t) + 1];
|
||||
|
||||
xsprintf(buf, "%" PRIu64, x); /* µs granularity */
|
||||
|
||||
r = write_string_file_atomic_label("/run/systemd/reboot-to-loader-menu", buf);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return sd_bus_reply_method_return(message, NULL);
|
||||
}
|
||||
|
||||
static int method_can_reboot_to_boot_loader_menu(
|
||||
sd_bus_message *message,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Manager *m = userdata;
|
||||
int r;
|
||||
|
||||
|
||||
assert(message);
|
||||
assert(m);
|
||||
|
||||
r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU");
|
||||
if (r == -ENXIO) {
|
||||
uint64_t features = 0;
|
||||
|
||||
/* EFI case, let's see if booting into boot loader menu is supported. */
|
||||
|
||||
r = efi_loader_get_features(&features);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to determine whether reboot to boot loader menu is supported: %m");
|
||||
if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT))
|
||||
return sd_bus_reply_method_return(message, "s", "na");
|
||||
|
||||
} else if (r <= 0) {
|
||||
/* Non-EFI case: let's trust $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU */
|
||||
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU: %m");
|
||||
|
||||
return sd_bus_reply_method_return(message, "s", "na");
|
||||
}
|
||||
|
||||
return return_test_polkit(
|
||||
message,
|
||||
CAP_SYS_ADMIN,
|
||||
"org.freedesktop.login1.set-reboot-to-boot-loader-menu",
|
||||
NULL,
|
||||
UID_INVALID,
|
||||
error);
|
||||
}
|
||||
|
||||
static int property_get_reboot_to_boot_loader_entry(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_free_ char *v = NULL;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
assert(userdata);
|
||||
|
||||
r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY");
|
||||
if (r == -ENXIO) {
|
||||
/* EFI case: let's read the LoaderEntryOneShot variable */
|
||||
|
||||
r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &v);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
log_warning_errno(r, "Failed to read LoaderEntryOneShot variable: %m");
|
||||
} else if (!efi_loader_entry_name_valid(v)) {
|
||||
log_warning("LoaderEntryOneShot contains invalid entry name '%s', ignoring.", v);
|
||||
v = mfree(v);
|
||||
}
|
||||
} else if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY: %m");
|
||||
else if (r > 0) {
|
||||
|
||||
/* Non-EFI case, let's process /run/systemd/reboot-to-boot-loader-entry. */
|
||||
|
||||
r = read_one_line_file("/run/systemd/reboot-to-boot-loader-entry", &v);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
log_warning_errno(r, "Failed to read /run/systemd/reboot-to-boot-loader-entry: %m");
|
||||
} else if (!efi_loader_entry_name_valid(v)) {
|
||||
log_warning("/run/systemd/reboot-to-boot-loader-entry is not valid, ignoring.");
|
||||
v = mfree(v);
|
||||
}
|
||||
}
|
||||
|
||||
return sd_bus_message_append(reply, "s", v);
|
||||
}
|
||||
|
||||
static int boot_loader_entry_exists(const char *id) {
|
||||
_cleanup_(boot_config_free) BootConfig config = {};
|
||||
int r;
|
||||
|
||||
assert(id);
|
||||
|
||||
r = boot_entries_load_config_auto(NULL, NULL, &config);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) boot_entries_augment_from_loader(&config, true);
|
||||
|
||||
return boot_config_has_entry(&config, id);
|
||||
}
|
||||
|
||||
static int method_set_reboot_to_boot_loader_entry(
|
||||
sd_bus_message *message,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Manager *m = userdata;
|
||||
bool use_efi;
|
||||
const char *v;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(m);
|
||||
|
||||
r = sd_bus_message_read(message, "s", &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (isempty(v))
|
||||
v = NULL;
|
||||
else if (efi_loader_entry_name_valid(v)) {
|
||||
r = boot_loader_entry_exists(v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader entry '%s' is not known.", v);
|
||||
} else
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Boot loader entry name '%s' is not valid, refusing.", v);
|
||||
|
||||
r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY");
|
||||
if (r == -ENXIO) {
|
||||
uint64_t features;
|
||||
|
||||
/* EFI case: let's see if booting into boot loader entry is supported. */
|
||||
|
||||
r = efi_loader_get_features(&features);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to determine whether reboot into boot loader entry is supported: %m");
|
||||
if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_ENTRY_ONESHOT))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Loader does not support boot into boot loader entry.");
|
||||
|
||||
use_efi = true;
|
||||
|
||||
} else if (r <= 0) {
|
||||
/* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY is set to off */
|
||||
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY: %m");
|
||||
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Loader does not support boot into boot loader entry.");
|
||||
} else
|
||||
/* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY is set to on */
|
||||
use_efi = false;
|
||||
|
||||
r = bus_verify_polkit_async(message,
|
||||
CAP_SYS_ADMIN,
|
||||
"org.freedesktop.login1.set-reboot-to-boot-loader-entry",
|
||||
NULL,
|
||||
false,
|
||||
UID_INVALID,
|
||||
&m->polkit_registry,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
|
||||
if (use_efi) {
|
||||
if (isempty(v))
|
||||
/* Delete item */
|
||||
r = efi_set_variable(EFI_VENDOR_LOADER, "LoaderEntryOneShot", NULL, 0);
|
||||
else
|
||||
r = efi_set_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
if (isempty(v)) {
|
||||
if (unlink("/run/systemd/reboot-to-boot-loader-entry") < 0 && errno != ENOENT)
|
||||
return -errno;
|
||||
} else {
|
||||
r = write_string_file_atomic_label("/run/systemd/reboot-boot-to-loader-entry", v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return sd_bus_reply_method_return(message, NULL);
|
||||
}
|
||||
|
||||
static int method_can_reboot_to_boot_loader_entry(
|
||||
sd_bus_message *message,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Manager *m = userdata;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(m);
|
||||
|
||||
r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY");
|
||||
if (r == -ENXIO) {
|
||||
uint64_t features = 0;
|
||||
|
||||
/* EFI case, let's see if booting into boot loader entry is supported. */
|
||||
|
||||
r = efi_loader_get_features(&features);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to determine whether reboot to boot loader entry is supported: %m");
|
||||
if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_ENTRY_ONESHOT))
|
||||
return sd_bus_reply_method_return(message, "s", "na");
|
||||
|
||||
} else if (r <= 0) {
|
||||
/* Non-EFI case: let's trust $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY */
|
||||
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY: %m");
|
||||
|
||||
return sd_bus_reply_method_return(message, "s", "na");
|
||||
}
|
||||
|
||||
return return_test_polkit(
|
||||
message,
|
||||
CAP_SYS_ADMIN,
|
||||
"org.freedesktop.login1.set-reboot-to-boot-loader-entry",
|
||||
NULL,
|
||||
UID_INVALID,
|
||||
error);
|
||||
}
|
||||
|
||||
static int property_get_boot_loader_entries(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_(boot_config_free) BootConfig config = {};
|
||||
size_t i;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
assert(userdata);
|
||||
|
||||
r = boot_entries_load_config_auto(NULL, NULL, &config);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) boot_entries_augment_from_loader(&config, true);
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "s");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (i = 0; i < config.n_entries; i++) {
|
||||
BootEntry *e = config.entries + i;
|
||||
|
||||
r = sd_bus_message_append(reply, "s", e->id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int method_set_wall_message(
|
||||
sd_bus_message *message,
|
||||
void *userdata,
|
||||
@@ -2640,6 +3139,9 @@ const sd_bus_vtable manager_vtable[] = {
|
||||
SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("KillUserProcesses", "b", NULL, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RebootToFirmwareSetup", "b", property_get_reboot_to_firmware_setup, 0, 0),
|
||||
SD_BUS_PROPERTY("RebootToBootLoaderMenu", "t", property_get_reboot_to_boot_loader_menu, 0, 0),
|
||||
SD_BUS_PROPERTY("RebootToBootLoaderEntry", "s", property_get_reboot_to_boot_loader_entry, 0, 0),
|
||||
SD_BUS_PROPERTY("BootLoaderEntries", "as", property_get_boot_loader_entries, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
@@ -2714,6 +3216,10 @@ const sd_bus_vtable manager_vtable[] = {
|
||||
SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("CanRebootToBootLoaderMenu", NULL, "s", method_can_reboot_to_boot_loader_menu, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetRebootToBootLoaderMenu", "t", NULL, method_set_reboot_to_boot_loader_menu, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("CanRebootToBootLoaderEntry", NULL, "s", method_can_reboot_to_boot_loader_entry, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetRebootToBootLoaderEntry", "s", NULL, method_set_reboot_to_boot_loader_entry, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("SetWallMessage", "sb", NULL, method_set_wall_message, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
|
||||
SD_BUS_SIGNAL("SessionNew", "so", 0),
|
||||
|
||||
@@ -198,6 +198,22 @@
|
||||
send_interface="org.freedesktop.login1.Manager"
|
||||
send_member="SetRebootToFirmwareSetup"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.login1"
|
||||
send_interface="org.freedesktop.login1.Manager"
|
||||
send_member="CanRebootToBootLoaderMenu"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.login1"
|
||||
send_interface="org.freedesktop.login1.Manager"
|
||||
send_member="SetRebootToBootLoaderMenu"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.login1"
|
||||
send_interface="org.freedesktop.login1.Manager"
|
||||
send_member="CanRebootToBootLoaderEntry"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.login1"
|
||||
send_interface="org.freedesktop.login1.Manager"
|
||||
send_member="SetRebootToBootLoaderEntry"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.login1"
|
||||
send_interface="org.freedesktop.login1.Manager"
|
||||
send_member="SetWallMessage"/>
|
||||
|
||||
@@ -338,7 +338,7 @@
|
||||
</action>
|
||||
|
||||
<action id="org.freedesktop.login1.set-reboot-to-firmware-setup">
|
||||
<description gettext-domain="systemd">Allow indication to the firmware to boot to setup interface</description>
|
||||
<description gettext-domain="systemd">Indicate to the firmware to boot to setup interface</description>
|
||||
<message gettext-domain="systemd">Authentication is required to indicate to the firmware to boot to setup interface.</message>
|
||||
<defaults>
|
||||
<allow_any>auth_admin_keep</allow_any>
|
||||
@@ -348,6 +348,28 @@
|
||||
<annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.reboot</annotate>
|
||||
</action>
|
||||
|
||||
<action id="org.freedesktop.login1.set-reboot-to-boot-loader-menu">
|
||||
<description gettext-domain="systemd">Indicate to the boot loader to boot to the boot loader menu</description>
|
||||
<message gettext-domain="systemd">Authentication is required to indicate to the boot loader to boot to the boot loader menu.</message>
|
||||
<defaults>
|
||||
<allow_any>auth_admin_keep</allow_any>
|
||||
<allow_inactive>auth_admin_keep</allow_inactive>
|
||||
<allow_active>yes</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.reboot</annotate>
|
||||
</action>
|
||||
|
||||
<action id="org.freedesktop.login1.set-reboot-to-boot-loader-entry">
|
||||
<description gettext-domain="systemd">Indicate to the boot loader to boot a specific entry</description>
|
||||
<message gettext-domain="systemd">Authentication is required to indicate to the boot loader to boot into a specific boot loader entry.</message>
|
||||
<defaults>
|
||||
<allow_any>auth_admin_keep</allow_any>
|
||||
<allow_inactive>auth_admin_keep</allow_inactive>
|
||||
<allow_active>yes</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.reboot</annotate>
|
||||
</action>
|
||||
|
||||
<action id="org.freedesktop.login1.set-wall-message">
|
||||
<description gettext-domain="systemd">Set a wall message</description>
|
||||
<message gettext-domain="systemd">Authentication is required to set a wall message</message>
|
||||
|
||||
+134
-36
@@ -22,6 +22,7 @@
|
||||
#include "path-util.h"
|
||||
#include "pe-header.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "unaligned.h"
|
||||
@@ -50,7 +51,10 @@ static int boot_entry_load(
|
||||
const char *path,
|
||||
BootEntry *entry) {
|
||||
|
||||
_cleanup_(boot_entry_free) BootEntry tmp = {};
|
||||
_cleanup_(boot_entry_free) BootEntry tmp = {
|
||||
.type = BOOT_ENTRY_CONF,
|
||||
};
|
||||
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
unsigned line = 1;
|
||||
char *b, *c;
|
||||
@@ -62,13 +66,16 @@ static int boot_entry_load(
|
||||
|
||||
c = endswith_no_case(path, ".conf");
|
||||
if (!c)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry filename: %s", path);
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry file suffix: %s", path);
|
||||
|
||||
b = basename(path);
|
||||
tmp.id = strndup(b, c - b);
|
||||
if (!tmp.id)
|
||||
return log_oom();
|
||||
|
||||
if (!efi_loader_entry_name_valid(tmp.id))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry filename: %s", path);
|
||||
|
||||
tmp.path = strdup(path);
|
||||
if (!tmp.path)
|
||||
return log_oom();
|
||||
@@ -272,7 +279,9 @@ static int boot_entry_load_unified(
|
||||
BootEntry *ret) {
|
||||
|
||||
_cleanup_free_ char *os_pretty_name = NULL, *os_id = NULL, *version_id = NULL, *build_id = NULL;
|
||||
_cleanup_(boot_entry_free) BootEntry tmp = {};
|
||||
_cleanup_(boot_entry_free) BootEntry tmp = {
|
||||
.type = BOOT_ENTRY_UNIFIED,
|
||||
};
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
const char *k;
|
||||
int r;
|
||||
@@ -304,6 +313,9 @@ static int boot_entry_load_unified(
|
||||
if (!tmp.id)
|
||||
return log_oom();
|
||||
|
||||
if (!efi_loader_entry_name_valid(tmp.id))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry: %s", tmp.id);
|
||||
|
||||
tmp.path = strdup(path);
|
||||
if (!tmp.path)
|
||||
return log_oom();
|
||||
@@ -476,7 +488,7 @@ static int boot_entries_find_unified(
|
||||
|
||||
r = fd_verify_regular(fd);
|
||||
if (r < 0) {
|
||||
log_warning_errno(errno, "File %s/%s is not regular, ignoring: %m", dir, de->d_name);
|
||||
log_warning_errno(r, "File %s/%s is not regular, ignoring: %m", dir, de->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -664,6 +676,118 @@ int boot_entries_load_config(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int boot_entries_load_config_auto(
|
||||
const char *override_esp_path,
|
||||
const char *override_xbootldr_path,
|
||||
BootConfig *config) {
|
||||
|
||||
_cleanup_free_ char *esp_where = NULL, *xbootldr_where = NULL;
|
||||
int r;
|
||||
|
||||
assert(config);
|
||||
|
||||
/* This function is similar to boot_entries_load_config(), however we automatically search for the
|
||||
* ESP and the XBOOTLDR partition unless it is explicitly specified. Also, if the user did not pass
|
||||
* an ESP or XBOOTLDR path directly, let's see if /run/boot-loader-entries/ exists. If so, let's
|
||||
* read data from there, as if it was an ESP (i.e. loading both entries and loader.conf data from
|
||||
* it). This allows other boot loaders to pass boot loader entry information to our tools if they
|
||||
* want to. */
|
||||
|
||||
if (!override_esp_path && !override_xbootldr_path) {
|
||||
|
||||
if (access("/run/boot-loader-entries/", F_OK) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return log_error_errno(errno, "Failed to determine whether /run/boot-loader-entries/ exists: %m");
|
||||
} else {
|
||||
r = boot_entries_load_config("/run/boot-loader-entries/", NULL, config);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
r = find_esp_and_warn(override_esp_path, false, &esp_where, NULL, NULL, NULL, NULL);
|
||||
if (r == -ENOKEY) /* find_esp_and_warn() doesn't warn about this case */
|
||||
return log_error_errno(r, "Cannot find the ESP partition mount point.");
|
||||
if (r < 0) /* But it logs about all these cases, hence don't log here again */
|
||||
return r;
|
||||
|
||||
r = find_xbootldr_and_warn(override_xbootldr_path, false, &xbootldr_where, NULL);
|
||||
if (r < 0 && r != -ENOKEY)
|
||||
return r; /* It's fine if the XBOOTLDR partition doesn't exist, hence we ignore ENOKEY here */
|
||||
|
||||
r = boot_entries_load_config(esp_where, xbootldr_where, config);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) {
|
||||
|
||||
static const char * const title_table[] = {
|
||||
/* Pretty names for a few well-known automatically discovered entries. */
|
||||
"auto-osx", "macOS",
|
||||
"auto-windows", "Windows Boot Manager",
|
||||
"auto-efi-shell", "EFI Shell",
|
||||
"auto-efi-default", "EFI Default Loader",
|
||||
"auto-reboot-to-firmware-setup", "Reboot Into Firmware Interface",
|
||||
};
|
||||
|
||||
_cleanup_free_ char **found_by_loader = NULL;
|
||||
size_t n_allocated;
|
||||
char **i;
|
||||
int r;
|
||||
|
||||
assert(config);
|
||||
|
||||
/* Let's add the entries discovered by the boot loader to the end of our list, unless they are
|
||||
* already included there. */
|
||||
|
||||
r = efi_loader_get_entries(&found_by_loader);
|
||||
if (IN_SET(r, -ENOENT, -EOPNOTSUPP))
|
||||
return log_debug_errno(r, "Boot loader reported no entries.");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine entries reported by boot loader: %m");
|
||||
|
||||
n_allocated = config->n_entries;
|
||||
|
||||
STRV_FOREACH(i, found_by_loader) {
|
||||
_cleanup_free_ char *c = NULL, *t = NULL;
|
||||
char **a, **b;
|
||||
|
||||
if (boot_config_has_entry(config, *i))
|
||||
continue;
|
||||
|
||||
if (only_auto && !startswith(*i, "auto-"))
|
||||
continue;
|
||||
|
||||
c = strdup(*i);
|
||||
if (!c)
|
||||
return log_oom();
|
||||
|
||||
STRV_FOREACH_PAIR(a, b, (char**) title_table)
|
||||
if (streq(*a, *i)) {
|
||||
t = strdup(*b);
|
||||
if (!t)
|
||||
return log_oom();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!GREEDY_REALLOC0(config->entries, n_allocated, config->n_entries + 1))
|
||||
return log_oom();
|
||||
|
||||
config->entries[config->n_entries++] = (BootEntry) {
|
||||
.type = BOOT_ENTRY_LOADER,
|
||||
.id = TAKE_PTR(c),
|
||||
.title = TAKE_PTR(t),
|
||||
};
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********************************************************************************/
|
||||
|
||||
static int verify_esp_blkid(
|
||||
@@ -1294,36 +1418,10 @@ found:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int find_default_boot_entry(
|
||||
const char *esp_path,
|
||||
const char *xbootldr_path,
|
||||
BootConfig *config,
|
||||
const BootEntry **e) {
|
||||
static const char* const boot_entry_type_table[_BOOT_ENTRY_MAX] = {
|
||||
[BOOT_ENTRY_CONF] = "conf",
|
||||
[BOOT_ENTRY_UNIFIED] = "unified",
|
||||
[BOOT_ENTRY_LOADER] = "loader",
|
||||
};
|
||||
|
||||
_cleanup_free_ char *esp_where = NULL, *xbootldr_where = NULL;
|
||||
int r;
|
||||
|
||||
assert(config);
|
||||
assert(e);
|
||||
|
||||
r = find_esp_and_warn(esp_path, false, &esp_where, NULL, NULL, NULL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = find_xbootldr_and_warn(xbootldr_path, false, &xbootldr_where, NULL);
|
||||
if (r < 0 && r != -ENOKEY)
|
||||
return r;
|
||||
|
||||
r = boot_entries_load_config(esp_where, xbootldr_where, config);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load boot loader entries: %m");
|
||||
|
||||
if (config->default_entry < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
|
||||
"No boot loader entry suitable as default, refusing to guess.");
|
||||
|
||||
*e = &config->entries[config->default_entry];
|
||||
log_debug("Found default boot loader entry in file \"%s\"", (*e)->path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_STRING_TABLE_LOOKUP(boot_entry_type, BootEntryType);
|
||||
|
||||
+32
-1
@@ -8,7 +8,18 @@
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "string-util.h"
|
||||
|
||||
typedef enum BootEntryType {
|
||||
BOOT_ENTRY_CONF, /* Type #1 entries: *.conf files */
|
||||
BOOT_ENTRY_UNIFIED, /* Type #2 entries: *.efi files */
|
||||
BOOT_ENTRY_LOADER, /* Additional entries augmented from LoaderEntries EFI var */
|
||||
_BOOT_ENTRY_MAX,
|
||||
_BOOT_ENTRY_INVALID = -1,
|
||||
} BootEntryType;
|
||||
|
||||
typedef struct BootEntry {
|
||||
BootEntryType type;
|
||||
char *id; /* This is the file basename without extension */
|
||||
char *path; /* This is the full path to the drop-in file */
|
||||
char *root; /* The root path in which the drop-in was found, i.e. to which 'kernel', 'efi' and 'initrd' are relative */
|
||||
@@ -40,8 +51,27 @@ typedef struct BootConfig {
|
||||
ssize_t default_entry;
|
||||
} BootConfig;
|
||||
|
||||
static inline bool boot_config_has_entry(BootConfig *config, const char *id) {
|
||||
size_t j;
|
||||
|
||||
for (j = 0; j < config->n_entries; j++)
|
||||
if (streq(config->entries[j].id, id))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline BootEntry* boot_config_default_entry(BootConfig *config) {
|
||||
if (config->default_entry < 0)
|
||||
return NULL;
|
||||
|
||||
return config->entries + config->default_entry;
|
||||
}
|
||||
|
||||
void boot_config_free(BootConfig *config);
|
||||
int boot_entries_load_config(const char *esp_path, const char *xbootldr_path, BootConfig *config);
|
||||
int boot_entries_load_config_auto(const char *override_esp_path, const char *override_xbootldr_path, BootConfig *config);
|
||||
int boot_entries_augment_from_loader(BootConfig *config, bool only_auto);
|
||||
|
||||
static inline const char* boot_entry_title(const BootEntry *entry) {
|
||||
return entry->show_title ?: entry->title ?: entry->id;
|
||||
@@ -50,4 +80,5 @@ static inline const char* boot_entry_title(const BootEntry *entry) {
|
||||
int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid);
|
||||
int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path,sd_id128_t *ret_uuid);
|
||||
|
||||
int find_default_boot_entry(const char *esp_path, const char *xbootldr_path, BootConfig *config, const BootEntry **e);
|
||||
const char* boot_entry_type_to_string(BootEntryType t) _const_;
|
||||
BootEntryType boot_entry_type_from_string(const char *s) _pure_;
|
||||
|
||||
@@ -796,7 +796,7 @@ bool efi_loader_entry_name_valid(const char *s) {
|
||||
if (strlen(s) > FILENAME_MAX) /* Make sure entry names fit in filenames */
|
||||
return false;
|
||||
|
||||
return in_charset(s, ALPHANUMERICAL "-");
|
||||
return in_charset(s, ALPHANUMERICAL "-_.");
|
||||
}
|
||||
|
||||
int efi_loader_get_entries(char ***ret) {
|
||||
|
||||
+153
-27
@@ -170,6 +170,8 @@ static unsigned arg_lines = 10;
|
||||
static OutputMode arg_output = OUTPUT_SHORT;
|
||||
static bool arg_plain = false;
|
||||
static bool arg_firmware_setup = false;
|
||||
static usec_t arg_boot_loader_menu = USEC_INFINITY;
|
||||
static const char *arg_boot_loader_entry = NULL;
|
||||
static bool arg_now = false;
|
||||
static bool arg_jobs_before = false;
|
||||
static bool arg_jobs_after = false;
|
||||
@@ -3473,7 +3475,11 @@ static int logind_check_inhibitors(enum action a) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static int logind_prepare_firmware_setup(void) {
|
||||
static int prepare_firmware_setup(void) {
|
||||
|
||||
if (!arg_firmware_setup)
|
||||
return 0;
|
||||
|
||||
#if ENABLE_LOGIND
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
sd_bus *bus;
|
||||
@@ -3497,27 +3503,75 @@ static int logind_prepare_firmware_setup(void) {
|
||||
|
||||
return 0;
|
||||
#else
|
||||
log_error("Cannot remotely indicate to EFI to boot into setup mode.");
|
||||
log_error("Booting into firmware setup not supported.");
|
||||
return -ENOSYS;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int prepare_firmware_setup(void) {
|
||||
int r;
|
||||
static int prepare_boot_loader_menu(void) {
|
||||
|
||||
if (!arg_firmware_setup)
|
||||
if (arg_boot_loader_menu == USEC_INFINITY)
|
||||
return 0;
|
||||
|
||||
if (arg_transport == BUS_TRANSPORT_LOCAL) {
|
||||
#if ENABLE_LOGIND
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
sd_bus *bus;
|
||||
int r;
|
||||
|
||||
r = efi_set_reboot_to_firmware(true);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Cannot indicate to EFI to boot into setup mode, will retry via logind: %m");
|
||||
else
|
||||
return r;
|
||||
}
|
||||
r = acquire_bus(BUS_FULL, &bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return logind_prepare_firmware_setup();
|
||||
r = sd_bus_call_method(
|
||||
bus,
|
||||
"org.freedesktop.login1",
|
||||
"/org/freedesktop/login1",
|
||||
"org.freedesktop.login1.Manager",
|
||||
"SetRebootToBootLoaderMenu",
|
||||
&error,
|
||||
NULL,
|
||||
"t", arg_boot_loader_menu);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Cannot indicate to boot loader to enter boot loader entry menu: %s", bus_error_message(&error, r));
|
||||
|
||||
return 0;
|
||||
#else
|
||||
log_error("Booting into boot loader menu not supported.");
|
||||
return -ENOSYS;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int prepare_boot_loader_entry(void) {
|
||||
|
||||
if (!arg_boot_loader_entry)
|
||||
return 0;
|
||||
|
||||
#if ENABLE_LOGIND
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
sd_bus *bus;
|
||||
int r;
|
||||
|
||||
r = acquire_bus(BUS_FULL, &bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_call_method(
|
||||
bus,
|
||||
"org.freedesktop.login1",
|
||||
"/org/freedesktop/login1",
|
||||
"org.freedesktop.login1.Manager",
|
||||
"SetRebootToBootLoaderEntry",
|
||||
&error,
|
||||
NULL,
|
||||
"s", arg_boot_loader_entry);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Cannot set boot into loader entry '%s': %s", arg_boot_loader_entry, bus_error_message(&error, r));
|
||||
|
||||
return 0;
|
||||
#else
|
||||
log_error("Booting into boot loader entry not supported.");
|
||||
return -ENOSYS;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int load_kexec_kernel(void) {
|
||||
@@ -3535,15 +3589,22 @@ static int load_kexec_kernel(void) {
|
||||
if (access(KEXEC, X_OK) < 0)
|
||||
return log_error_errno(errno, KEXEC" is not available: %m");
|
||||
|
||||
r = find_default_boot_entry(NULL, NULL, &config, &e);
|
||||
if (r == -ENOKEY) /* find_default_boot_entry() doesn't warn about this case */
|
||||
return log_error_errno(r, "Cannot find the ESP partition mount point.");
|
||||
r = boot_entries_load_config_auto(NULL, NULL, &config);
|
||||
if (r < 0)
|
||||
/* But it logs about all these cases, hence don't log here again */
|
||||
return r;
|
||||
|
||||
e = boot_config_default_entry(&config);
|
||||
if (!e)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
|
||||
"No boot loader entry suitable as default, refusing to guess.");
|
||||
|
||||
log_debug("Found default boot loader entry in file \"%s\"", e->path);
|
||||
|
||||
if (!e->kernel)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Boot entry does not refer to Linux kernel, which is not supported currently.");
|
||||
if (strv_length(e->initrd) > 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Boot entry specifies multiple initrds, which is not supported currently.");
|
||||
|
||||
kernel = path_join(e->root, e->kernel);
|
||||
@@ -3551,7 +3612,7 @@ static int load_kexec_kernel(void) {
|
||||
return log_oom();
|
||||
|
||||
if (!strv_isempty(e->initrd)) {
|
||||
initrd = path_join(e->root, *e->initrd);
|
||||
initrd = path_join(e->root, e->initrd[0]);
|
||||
if (!initrd)
|
||||
return log_oom();
|
||||
}
|
||||
@@ -3569,7 +3630,7 @@ static int load_kexec_kernel(void) {
|
||||
if (arg_dry_run)
|
||||
return 0;
|
||||
|
||||
r = safe_fork("(kexec)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
|
||||
r = safe_fork("(kexec)", FORK_WAIT|FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
@@ -3578,19 +3639,14 @@ static int load_kexec_kernel(void) {
|
||||
"--load", kernel,
|
||||
"--append", options,
|
||||
initrd ? "--initrd" : NULL, initrd,
|
||||
NULL };
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Child */
|
||||
execv(args[0], (char * const *) args);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
r = wait_for_terminate_and_check("kexec", pid, WAIT_LOG);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
/* Command failed */
|
||||
return -EPROTO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3642,6 +3698,14 @@ static int start_special(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = prepare_boot_loader_menu();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = prepare_boot_loader_entry();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (a == ACTION_REBOOT && argc > 1) {
|
||||
r = update_reboot_parameter_and_warn(argv[1]);
|
||||
if (r < 0)
|
||||
@@ -7520,6 +7584,10 @@ static int systemctl_help(void) {
|
||||
" short-monotonic, short-unix,\n"
|
||||
" verbose, export, json, json-pretty, json-sse, cat)\n"
|
||||
" --firmware-setup Tell the firmware to show the setup menu on next boot\n"
|
||||
" --boot-loader-menu=TIME\n"
|
||||
" Boot into boot loader menu on next boot\n"
|
||||
" --boot-loader-entry=NAME\n"
|
||||
" Boot into a specific boot loader entry on next boot\n"
|
||||
" --plain Print unit dependencies as a list instead of a tree\n\n"
|
||||
"Unit Commands:\n"
|
||||
" list-units [PATTERN...] List units currently in memory\n"
|
||||
@@ -7774,6 +7842,39 @@ static void help_states(void) {
|
||||
DUMP_STRING_TABLE(timer_state, TimerState, _TIMER_STATE_MAX);
|
||||
}
|
||||
|
||||
static int help_boot_loader_entry(void) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_free_ char **l = NULL;
|
||||
sd_bus *bus;
|
||||
char **i;
|
||||
int r;
|
||||
|
||||
r = acquire_bus(BUS_FULL, &bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_get_property_strv(
|
||||
bus,
|
||||
"org.freedesktop.login1",
|
||||
"/org/freedesktop/login1",
|
||||
"org.freedesktop.login1.Manager",
|
||||
"BootLoaderEntries",
|
||||
&error,
|
||||
&l);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enumerate boot loader entries: %s", bus_error_message(&error, r));
|
||||
|
||||
if (strv_isempty(l)) {
|
||||
log_error("No boot loader entries discovered.");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
puts(*i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
enum {
|
||||
ARG_FAIL = 0x100,
|
||||
@@ -7804,6 +7905,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
ARG_JOB_MODE,
|
||||
ARG_PRESET_MODE,
|
||||
ARG_FIRMWARE_SETUP,
|
||||
ARG_BOOT_LOADER_MENU,
|
||||
ARG_BOOT_LOADER_ENTRY,
|
||||
ARG_NOW,
|
||||
ARG_MESSAGE,
|
||||
ARG_WAIT,
|
||||
@@ -7853,6 +7956,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
{ "recursive", no_argument, NULL, 'r' },
|
||||
{ "preset-mode", required_argument, NULL, ARG_PRESET_MODE },
|
||||
{ "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP },
|
||||
{ "boot-loader-menu", required_argument, NULL, ARG_BOOT_LOADER_MENU },
|
||||
{ "boot-loader-entry", required_argument, NULL, ARG_BOOT_LOADER_ENTRY },
|
||||
{ "now", no_argument, NULL, ARG_NOW },
|
||||
{ "message", required_argument, NULL, ARG_MESSAGE },
|
||||
{}
|
||||
@@ -8126,6 +8231,27 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
arg_firmware_setup = true;
|
||||
break;
|
||||
|
||||
case ARG_BOOT_LOADER_MENU:
|
||||
|
||||
r = parse_sec(optarg, &arg_boot_loader_menu);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse --boot-loader-menu= argument '%s': %m", optarg);
|
||||
|
||||
break;
|
||||
|
||||
case ARG_BOOT_LOADER_ENTRY:
|
||||
|
||||
if (streq(optarg, "help")) { /* Yes, this means, "help" is not a valid boot loader entry name we can deal with */
|
||||
r = help_boot_loader_entry();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
arg_boot_loader_entry = empty_to_null(optarg);
|
||||
break;
|
||||
|
||||
case ARG_STATE: {
|
||||
if (isempty(optarg))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
|
||||
@@ -21,7 +21,7 @@ After=dbus.socket
|
||||
|
||||
[Service]
|
||||
BusName=org.freedesktop.login1
|
||||
CapabilityBoundingSet=CAP_SYS_ADMIN CAP_MAC_ADMIN CAP_AUDIT_CONTROL CAP_CHOWN CAP_KILL CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER CAP_SYS_TTY_CONFIG
|
||||
CapabilityBoundingSet=CAP_SYS_ADMIN CAP_MAC_ADMIN CAP_AUDIT_CONTROL CAP_CHOWN CAP_KILL CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER CAP_SYS_TTY_CONFIG CAP_LINUX_IMMUTABLE
|
||||
ExecStart=@rootlibexecdir@/systemd-logind
|
||||
FileDescriptorStoreMax=512
|
||||
IPAddressDeny=any
|
||||
|
||||
Reference in New Issue
Block a user