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:
Zbigniew Jędrzejewski-Szmek
2019-03-05 20:38:41 +01:00
committed by GitHub
12 changed files with 973 additions and 126 deletions
-5
View File
@@ -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
+45
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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),
+16
View File
@@ -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"/>
+23 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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_;
+1 -1
View File
@@ -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
View File
@@ -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),
+1 -1
View File
@@ -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