1
0
mirror of https://github.com/systemd/systemd synced 2026-04-23 15:34:50 +02:00

Compare commits

...

21 Commits

Author SHA1 Message Date
Luca Boccassi
f72f802118 update NEWS
Fix typo and list all user session settings that now are documented
to work
2022-03-24 22:46:14 +00:00
Lennart Poettering
60a777b575 update NEWS 2022-03-24 22:41:39 +01:00
Frantisek Sumsal
a73c7a87ae
Merge pull request #22855 from keszybz/test-68-reload-reload
TEST-68: replace daemon-reload with separate handler units
2022-03-24 19:52:16 +00:00
Lennart Poettering
3824ce0522
Merge pull request #22840 from poettering/efivars-tweaks
util-lib: efi variable access refactorings
2022-03-24 18:39:46 +01:00
Lennart Poettering
e1f0c1367c NEWS: various tweaks 2022-03-24 17:59:25 +01:00
Yu Watanabe
c02fb80479 udev: try to reload selinux label database less frequently
Previously, `event_run()` was called repeatedly in one `event_queue_start()`
invocation. Hence, the SELinux label database is reloaded many times needlessly.
Other settings, e.g. udev rules or hwdata, are tried to be reloaded in the
beginning of `event_queue_start()`. Let's also do so for the SELinux database.
2022-03-24 16:20:42 +01:00
Yu Watanabe
2d40f02ee4 udev: assume there is no blocker when failed to check event dependencies
Previously, if udevd failed to resolve event dependency, the event is
ignored and libudev listeners did not receive the event. This is
inconsistent with the case when a worker failed to process a event,
in that case, the original uevent sent by the kernel is broadcasted to
listeners.
2022-03-24 16:20:32 +01:00
Yu Watanabe
ef400c3878 udev: only ignore ENOENT or friends which suggest the block device is not exist
The ENOENT, ENXIO, and ENODEV error can happen easily when a block
device appears and soon removed. So, it is reasonable to ignore the
error. But other errors should not occur here, and hence let's handle
them as critical.
2022-03-24 16:20:08 +01:00
Lennart Poettering
3f2ada89f3 errno-util: add ERRNO_IS_DEVICE_ABSENT() macro
Inspired by: https://github.com/systemd/systemd/pull/22717#discussion_r834254495
2022-03-24 16:19:48 +01:00
Yu Watanabe
4029328014 udev: remove /run/udev/queue in on_post()
When the last queued event is processed, information about subsequent
events may be already queued in the netlink socket of sd-device-monitor.
In that case, previously we once removed /run/udev/queue and touch the
file soon later, and `udevadm settle` mistakenly considered all events
are processed.

To mitigate such situation, this makes /run/udev/queue removed in on_post().
2022-03-24 16:19:36 +01:00
Yu Watanabe
9612da361a udev: use sd_event_source_disable_unref()
This should not change any behavior, as the event sources are not
shared. Just for safety.
2022-03-24 16:19:20 +01:00
Yu Watanabe
87afc766d1 udev: update comment and log message 2022-03-24 16:19:05 +01:00
Lennart Poettering
7be4b23649 efi-loader: split efi-api.[ch] from efi-loader.[ch]
Some refactoring: split efi-loader.[ch] in two: isolate the calls that
implement out boot loader interface spec, and those which implement
access to upstream UEFI firmware features.

They are quite different in nature and behaviour, and even semantically
it makes to keep these two separate. At the very least because the
previous name "efi-loader.[ch]" suggests all was about loader-specific
APIs, but much of it is generic uefi stuff...

While we are at it, I renamed a bunch of return parameters to follow our
usual ret_xyz naming. But besides renaming no real code changes.
2022-03-24 15:21:09 +01:00
Lennart Poettering
8fc5c44426 efivars: define efi variable flags less weirdly
The flags are actually 32bit values, but aligned with zeroes befitting a
64bit value. Let's fix that.
2022-03-24 15:21:03 +01:00
Lennart Poettering
187513fd3a efivarfs: rename a couple of return params to ret_xyz/ret 2022-03-24 15:20:57 +01:00
Lennart Poettering
afd1a45a16 efivars: downgrade log level in systemd_efi_options_efivarfs_if_newer()
The only caller logs anyway, let's avoid duplicate logging above
LOG_DEBUG.
2022-03-24 15:20:53 +01:00
Lennart Poettering
83fe0be170 efivars: no need to convert ENOENT → ENODATA twice
read_efi_options_variable() already does this, don#t do it again.
2022-03-24 15:20:49 +01:00
Lennart Poettering
bc5eb90015 efivars: tweak debug log message in efi_get_secure_boot_mode()
mention what we'll do as effect of the error we are seeing and eat up.
2022-03-24 15:20:45 +01:00
Lennart Poettering
3e09ad57c6 efivars: cache ENOENT as no efi secure boot
On systems lacking EFI or the SecureBoot efi var the caching of this
info didn#t work, since we'd see ENOENT when reading the var, and cache
that, which we then use as reason to retry next time.

Let's fix that and convert ENOENT to "secure boot", because that's what
it really means. All other errors are left as is (and reason to retry).
But let's add some debug logging for that case.
2022-03-24 15:18:52 +01:00
Zbigniew Jędrzejewski-Szmek
74fbb24f64 TEST-68: instead of calling daemon-reload, just use different cleanup units
On a very slow machine, things are executed out-of-order, and something
pins the previously-exited unit. Instead of fighting with this with daemon-reload,
let's just use a different cleanup unit.

Hopefully fixes #22755.
2022-03-24 14:11:54 +01:00
Zbigniew Jędrzejewski-Szmek
6741235430 TEST-68: get rid of unnecessary descriptions
The name of the unit already says all, no need to duplicate this.
And the comments can easily get out of date, as they did.
2022-03-24 13:45:38 +01:00
21 changed files with 869 additions and 774 deletions

180
NEWS
View File

@ -15,8 +15,8 @@ CHANGES WITH 251 in spe:
when a Condition*= check does not succeed, restoring the JobRemoved when a Condition*= check does not succeed, restoring the JobRemoved
signal to the behaviour it had before v250. signal to the behaviour it had before v250.
* The org.freedesktop.portable1 methods GetMetadataWithExtensions and * The org.freedesktop.portable1 methods GetMetadataWithExtensions() and
GetImageMetadataWithExtensions have been fixed to provide an extra GetImageMetadataWithExtensions() have been fixed to provide an extra
return parameter, containing the actual extension release metadata. return parameter, containing the actual extension release metadata.
The current implementation was judged to be broken and unusable, and The current implementation was judged to be broken and unusable, and
thus the usual procedure of adding a new set of methods was skipped, thus the usual procedure of adding a new set of methods was skipped,
@ -54,13 +54,15 @@ CHANGES WITH 251 in spe:
of pcap. of pcap.
* An udev rule that imported hwdb matches for USB devices with * An udev rule that imported hwdb matches for USB devices with
lowercase hexadecimal digits was added in systemd 250. This has been lowercase hexadecimal vendor/product ID digits was added in systemd
reverted, since uppercase hexadecimal digits are supposed to be used, 250. This has been reverted, since uppercase hexadecimal digits are
and we already had a rule that with the appropriate match. supposed to be used, and we already had a rule that with the
appropriate match.
Users might need to adjust their local hwdb entries. Users might need to adjust their local hwdb entries.
* arch_prctl(2) was moved to the @default set in the syscall filters. * arch_prctl(2) has been moved to the @default set in the syscall filters
(as exposed via the SystemCallFilter= setting in service unit files).
It is apparently used by the linker now. It is apparently used by the linker now.
New functionality and other changes: New functionality and other changes:
@ -73,7 +75,7 @@ CHANGES WITH 251 in spe:
entries are named after the local machine ID. However, in "golden entries are named after the local machine ID. However, in "golden
image" environments, where the machine ID shall be initialized on image" environments, where the machine ID shall be initialized on
first boot (as opposed to at installation time before first boot) the first boot (as opposed to at installation time before first boot) the
machine ID is not be available at build time. In this case the machine ID will not be available at build time. In this case the
--entry-token= switch to bootctl (or the /etc/kernel/entry-token --entry-token= switch to bootctl (or the /etc/kernel/entry-token
file) may be used to override the "token" for the entries, for file) may be used to override the "token" for the entries, for
example the IMAGE_ID= or ID= fields from /etc/os-release. This will example the IMAGE_ID= or ID= fields from /etc/os-release. This will
@ -85,21 +87,23 @@ CHANGES WITH 251 in spe:
Summary: if you are building golden images that shall acquire Summary: if you are building golden images that shall acquire
identity information exclusively on first boot, make sure to both identity information exclusively on first boot, make sure to both
remove /etc/machine-id *and* to write /etc/kernel/entry-token to the remove /etc/machine-id *and* to write /etc/kernel/entry-token to the
value of the IMAGE_ID or ID field of /etc/os-release or another value of the IMAGE_ID= or ID= field of /etc/os-release or another
suitable identifier before deploying the image. suitable identifier before deploying the image.
* The Boot Loader Specification has been extended with * The Boot Loader Specification has been extended with
/loader/entries.srel file that disambiguates the format of the /loader/entries.srel file located in the EFI System Partition (ESP)
entries in the /loader/entries directory. For entries that follow the that disambiguates the format of the entries in the /loader/entries/
Specification, "type1" should be used. directory (in order to discern them from incompatible uses of this
directory by other projects). For entries that follow the
Specification, the string "type1" is stored in this file.
bootctl will now write this file automatically when creating Type #1 bootctl will now write this file automatically when installing the
entries. systemd-boot boot loader.
* kernel-install supports a new initrd_generator= setting in * kernel-install supports a new initrd_generator= setting in
/etc/kernel/install.conf, that is exported as /etc/kernel/install.conf, that is exported as
$KERNEL_INSTALL_INITRD_GENERATOR to kernel-install plugins. This $KERNEL_INSTALL_INITRD_GENERATOR to kernel-install plugins. This
allows a different initrd generator to be hooked up. allows choosing different initrd generators.
* kernel-install will now create a "staging area" (an initially-empty * kernel-install will now create a "staging area" (an initially-empty
directory to gather files for a Boot Loader Specification Type #1 directory to gather files for a Boot Loader Specification Type #1
@ -146,7 +150,7 @@ CHANGES WITH 251 in spe:
* The journal JSON export format has been added to listed of stable * The journal JSON export format has been added to listed of stable
interfaces (https://systemd.io/PORTABILITY_AND_STABILITY/). interfaces (https://systemd.io/PORTABILITY_AND_STABILITY/).
* /etc/locale.conf is now populated through tmpfiles.d factory /etc * /etc/locale.conf is now populated through tmpfiles.d factory /etc/
handling with the values that were configured during systemd build handling with the values that were configured during systemd build
(if /etc/locale.conf has not been created through some other (if /etc/locale.conf has not been created through some other
mechanism). This means that /etc/locale.conf should always have mechanism). This means that /etc/locale.conf should always have
@ -155,16 +159,16 @@ CHANGES WITH 251 in spe:
* A new libsystemd-core-<version>.so private shared library is * A new libsystemd-core-<version>.so private shared library is
installed under /usr/lib/systemd/system, mirroring the existing installed under /usr/lib/systemd/system, mirroring the existing
libsystemd-shared-<version>.so library. This allows the total libsystemd-shared-<version>.so library. This allows the total
installation size to be reduced by code reuse. installation size to be reduced by binary code reuse.
* The <version> tag used by libsystemd-shared.so and libsystemd-core.so * The <version> tag used in the name of libsystemd-shared.so and
can be configured. Distributions may build subsequent versions of the libsystemd-core.so can be configured. Distributions may build
systemd package with unique tags (e.g. the full package version), subsequent versions of the systemd package with unique tags (e.g. the
thus allowing multiple installations of those shared libraries to be full package version), thus allowing multiple installations of those
available at the same time. This is intended to fix an issue where shared libraries to be available at the same time. This is intended
programs that link to those libraries would fail to execute because to fix an issue where programs that link to those libraries would
they were installed earlier or later than the appropriate version of fail to execute because they were installed earlier or later than the
the library. appropriate version of the library.
* A new set of service monitor environment variables will be passed to * A new set of service monitor environment variables will be passed to
OnFailure=/OnSuccess= handlers, but only if exactly one unit lists the OnFailure=/OnSuccess= handlers, but only if exactly one unit lists the
@ -173,31 +177,38 @@ CHANGES WITH 251 in spe:
$MONITOR_INVOCATION_ID and $MONITOR_UNIT. For cases when a single $MONITOR_INVOCATION_ID and $MONITOR_UNIT. For cases when a single
handler needs to watch multiple units, use a templated handler. handler needs to watch multiple units, use a templated handler.
* A new ExtensionDirectories= setting allows system extensions to be * A new ExtensionDirectories= setting in service unit files allows
loaded from a directory. (It is similar to ExtensionImages=, but system extensions to be loaded from a directory. (It is similar to
takes a path to a directory, instead of an image.) ExtensionImages=, but takes paths to directories, instead of
disk image files.)
'portablectl attach --extension' now also accepts directory paths. 'portablectl attach --extension=' now also accepts directory paths.
* VENDOR= and MODEL= can be set in /etc/machine-info to override the * VENDOR= and MODEL= can be set in /etc/machine-info to override the
values gleaned from the hwdb. values gleaned from the hwdb.
* A ID_CHASSIS property can be set in the hwdb (for the DMI modalias) * A ID_CHASSIS property can be set in the hwdb (for the DMI device
to override the chassis that is reported by hostnamed. /sys/class/dmi/id) to override the chassis that is reported by
hostnamed.
* Two new hwdb files have been started to lists "handhelds" (PDAs, * hostnamed's D-Bus interface gained a new method GetHardwareSerial()
calculators, etc.) and AV devices (DJ tables, keypads, etc.) that for reading the hardware serial, as reportd by DMI.
should accessible to the seat owner by default.
* A new unit systemd-networkd-wait-online@<interface>.service can be * Two new hwdb files have been added. One lists "handhelds" (PDAs,
used to wait for a specific interface to be up. calculators, etc.), the other AV production devices (DJ tables,
keypads, etc.) that should accessible to the seat owner user by
default.
* A new unit systemd-networkd-wait-online@<interface>.service has been
added that can be used to wait for a specific network interface to be
up.
* systemd-resolved is started earlier (in sysinit.target), so it * systemd-resolved is started earlier (in sysinit.target), so it
available earlier and will also be started in the initrd if installed available earlier and will also be started in the initrd if installed
there. there.
* udevadm trigger gained a new --prioritized-subsystem option to * udevadm trigger gained a new --prioritized-subsystem= option to
process certain subsystems (and all parent devices) earlier. process certain subsystems (and all their parent devices) earlier.
systemd-udev-trigger.service now uses this new option to trigger systemd-udev-trigger.service now uses this new option to trigger
block and TPM devices first, hopefully making the boot a bit faster. block and TPM devices first, hopefully making the boot a bit faster.
@ -208,35 +219,52 @@ CHANGES WITH 251 in spe:
initialized yet, respectively. initialized yet, respectively.
* systemd-cryptenroll can now control whether to require the user to * systemd-cryptenroll can now control whether to require the user to
enter a PIN when unlocking a volume via the new --tpm2-with-pin= enter a PIN when using TPM-based unlocking of a volume via the new
option. --tpm2-with-pin= option.
Option tpm2-pin= can be used in /etc/crypttab. Option tpm2-pin= can be used in /etc/crypttab.
* The user.delegate and user.invocation_id attributes on cgroups are * When unlocking devices via TPM, TPM2 parameter encryption is now
used in addition to trusted.delegate and trusted.invocation_id. The used, to ensure that communication between CPU and discrete TPM chips
latter pair requires privileges to set, but the former doesn't and cannot be eavesdropped to acquire disk encryption keys.
can be also set by the unprivileged user manager.
* The user.delegate and user.invocation_id extended attributes on
cgroups are used in addition to trusted.delegate and
trusted.invocation_id. The latter pair requires privileges to set,
but the former doesn't and can be also set by the unprivileged user
manager.
(Only supported on kernels ≥5.6.) (Only supported on kernels ≥5.6.)
* New option sort-key= has been added to the Boot Loader Specification * New option sort-key= has been added to the Boot Loader Specification
to override the entry sorty order. It is read by sd-boot and bootctl, to override the sorting order of the entries in the boot menu. It is
and will be written by kernel-install, with the default value of read by sd-boot and bootctl, and will be written by kernel-install,
IMAGE_ID= or ID= fields from os-release. Together, this means that with the default value of IMAGE_ID= or ID= fields from
on multiboot installations, entries should be grouped and sorted os-release. Together, this means that on multiboot installations,
in a predictable way. entries should be grouped and sorted in a predictable way.
* sd-boot can now beep when the menu is shown and menu entries are * sd-boot can now optionally beep when the menu is shown and menu
selected, which can be useful on machines without a working display. entries are selected, which can be useful on machines without a
working display. (Controllable via a loader.conf setting.)
* %y/%Y specifiers can be used in unit files to refer to unit file * In unit files the new %y/%Y specifiers can be used to refer to
path, which is particularly useful for linked unit files. normalized unit file path, which is particularly useful for symlinked
unit files.
%R specifier resolves to the pretty hostname. The new %R specifier resolves to the pretty hostname
(i.e. PRETTY_HOSTNAME= from /etc/machine-info).
%d specifier resolves to the credentials directory (same as The new %d specifier resolves to the credentials directory of a
$CREDENTIALS_DIRECTORY). service (same as $CREDENTIALS_DIRECTORY).
* The RootDirectory=, MountAPIVFS=, ExtensionDirectories=,
*Capabilities*=, ProtectHome=, *Directory=, TemporaryFileSystem=,
PrivateTmp=, PrivateDevices=, PrivateNetwork=, NetworkNamespacePath=,
PrivateIPC=, IPCNamespacePath=, PrivateUsers=, ProtectClock=,
ProtectKernelTunables=, ProtectKernelModules=, ProtectKernelLogs=,
MountFlags= service settings now also work in unprivileged user
services, i.e. those run by the user's --user service manager, as long
as user namespaces are enabled on the system.
* The --make-machine-id-directory= switch to bootctl has been replaced * The --make-machine-id-directory= switch to bootctl has been replaced
by --make-entry-directory=, given that the entry directory is not by --make-entry-directory=, given that the entry directory is not
@ -244,15 +272,26 @@ CHANGES WITH 251 in spe:
ID as selected via --entry-token= described above. The old name of ID as selected via --entry-token= described above. The old name of
the option is still understood to maximize compatibility. the option is still understood to maximize compatibility.
* Services with Restart=always and a failing ExecCondition= will no longer * 'bootctl list' gained support for a new --json= switch to output boot
be restarted, to bring ExecCondition= in line with Condition*= settings. menu entries in a simply JSON format.
* Services with Restart=always and a failing ExecCondition= will no
longer be restarted, to bring ExecCondition= behaviour in line with
Condition*= settings.
* LoadCredential= now accepts a directory as the argument; all files * LoadCredential= now accepts a directory as the argument; all files
from the directory will be loaded. from the directory will be loaded as credentials.
* A new D-Bus property ControlGroupId is now exposed on service units,
that encapsulates the service's numeric cgroup ID that newer kernels
maintain for each cgroup.
* systemd-networkd gained a new [Bridge] Isolated=true|false setting * systemd-networkd gained a new [Bridge] Isolated=true|false setting
that configures the eponymous kernel attribute on the bridge. that configures the eponymous kernel attribute on the bridge.
* .netdev files now can be used to create virtual WLAN devices, and
configure various settings on them, via the [VirtualWLAN] section.
* .link files gained support for [Match] Firmware= setting to match on * .link files gained support for [Match] Firmware= setting to match on
the device firmware description string. By mistake, it was previously the device firmware description string. By mistake, it was previously
only supported in .network files. only supported in .network files.
@ -262,6 +301,8 @@ CHANGES WITH 251 in spe:
This value is also shown by 'networkctl status'. This value is also shown by 'networkctl status'.
* .link files gained support for setting MDI/MID-X on a link.
* The Local= setting for various virtual network devices gained support * The Local= setting for various virtual network devices gained support
for specifying, in addition to the network address, the name of a for specifying, in addition to the network address, the name of a
local interface which must have the specified address. local interface which must have the specified address.
@ -274,7 +315,28 @@ CHANGES WITH 251 in spe:
* journalctl --list-boots now supports JSON output and the --reverse option. * journalctl --list-boots now supports JSON output and the --reverse option.
* Under docs/: JOURNAL_EXPORT_FORMATS was imported from the wiki and * Under docs/: JOURNAL_EXPORT_FORMATS was imported from the wiki and
updated, BUILDING_IMAGES is new. updated, BUILDING_IMAGES is new:
https://systemd.io/JOURNAL_EXPORT_FORMATS
https://systemd.io/BUILDING_IMAGES
* The sd-id128 API gained a new call sd_id128_to_uuid_string() that is
similar to sd_id128_to_string() but formats the ID in RFC 4122 UUID
format instead of simple series of hex characters.
* The userdbctl tool will now show UID range information as part of the
list of known users.
* systemctl's --timestamp= option gained a new choice "unix", to show
timestamp as unix times, i.e. seconds since 1970, Jan 1st.
* PID 1 gained support for configuring the "pre-timeout" of watchdog
devices and the associated governor, via the new
RuntimeWatchdogPreSec= and RuntimeWatchdogPreGovernor= configuration
options in /etc/systemd/system.conf.
* The kernel-install tool gained a new 'inspect' verb which shows the
paths and other settings used.
Experimental features: Experimental features:

View File

@ -142,7 +142,7 @@ int efi_get_variable(
return 0; return 0;
} }
int efi_get_variable_string(const char *variable, char **p) { int efi_get_variable_string(const char *variable, char **ret) {
_cleanup_free_ void *s = NULL; _cleanup_free_ void *s = NULL;
size_t ss = 0; size_t ss = 0;
int r; int r;
@ -156,7 +156,7 @@ int efi_get_variable_string(const char *variable, char **p) {
if (!x) if (!x)
return -ENOMEM; return -ENOMEM;
*p = x; *ret = x;
return 0; return 0;
} }
@ -310,9 +310,17 @@ static int read_flag(const char *variable) {
bool is_efi_secure_boot(void) { bool is_efi_secure_boot(void) {
static int cache = -1; static int cache = -1;
int r;
if (cache < 0) if (cache < 0) {
cache = read_flag(EFI_GLOBAL_VARIABLE(SecureBoot)); r = read_flag(EFI_GLOBAL_VARIABLE(SecureBoot));
if (r == -ENOENT)
cache = false;
else if (r < 0)
log_debug_errno(r, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m");
else
cache = r;
}
return cache > 0; return cache > 0;
} }
@ -326,7 +334,8 @@ SecureBootMode efi_get_secure_boot_mode(void) {
int secure = read_flag(EFI_GLOBAL_VARIABLE(SecureBoot)); int secure = read_flag(EFI_GLOBAL_VARIABLE(SecureBoot));
if (secure < 0) { if (secure < 0) {
if (secure != -ENOENT) if (secure != -ENOENT)
log_debug_errno(secure, "Error reading SecureBoot EFI variable: %m"); log_debug_errno(secure, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m");
return (cache = SECURE_BOOT_UNSUPPORTED); return (cache = SECURE_BOOT_UNSUPPORTED);
} }
@ -341,7 +350,7 @@ SecureBootMode efi_get_secure_boot_mode(void) {
return (cache = decode_secure_boot_mode(secure, audit > 0, deployed > 0, setup > 0)); return (cache = decode_secure_boot_mode(secure, audit > 0, deployed > 0, setup > 0));
} }
static int read_efi_options_variable(char **line) { static int read_efi_options_variable(char **ret) {
int r; int r;
/* In SecureBoot mode this is probably not what you want. As your cmdline is cryptographically signed /* In SecureBoot mode this is probably not what you want. As your cmdline is cryptographically signed
@ -361,7 +370,7 @@ static int read_efi_options_variable(char **line) {
return -EPERM; return -EPERM;
} }
r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(SystemdOptions), line); r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(SystemdOptions), ret);
if (r == -ENOENT) if (r == -ENOENT)
return -ENODATA; return -ENODATA;
return r; return r;
@ -379,13 +388,13 @@ int cache_efi_options_variable(void) {
WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755); WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755);
} }
int systemd_efi_options_variable(char **line) { int systemd_efi_options_variable(char **ret) {
const char *e; const char *e;
int r; int r;
/* Returns the contents of the variable for current boot from the cache. */ /* Returns the contents of the variable for current boot from the cache. */
assert(line); assert(ret);
/* For testing purposes it is sometimes useful to be able to override this */ /* For testing purposes it is sometimes useful to be able to override this */
e = secure_getenv("SYSTEMD_EFI_OPTIONS"); e = secure_getenv("SYSTEMD_EFI_OPTIONS");
@ -396,11 +405,11 @@ int systemd_efi_options_variable(char **line) {
if (!m) if (!m)
return -ENOMEM; return -ENOMEM;
*line = m; *ret = m;
return 0; return 0;
} }
r = read_one_line_file(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), line); r = read_one_line_file(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), ret);
if (r == -ENOENT) if (r == -ENOENT)
return -ENODATA; return -ENODATA;
return r; return r;
@ -410,7 +419,7 @@ static inline int compare_stat_mtime(const struct stat *a, const struct stat *b)
return CMP(timespec_load(&a->st_mtim), timespec_load(&b->st_mtim)); return CMP(timespec_load(&a->st_mtim), timespec_load(&b->st_mtim));
} }
int systemd_efi_options_efivarfs_if_newer(char **line) { int systemd_efi_options_efivarfs_if_newer(char **ret) {
struct stat a = {}, b; struct stat a = {}, b;
int r; int r;
@ -424,15 +433,14 @@ int systemd_efi_options_efivarfs_if_newer(char **line) {
log_debug("Variable SystemdOptions in evifarfs is newer than in cache."); log_debug("Variable SystemdOptions in evifarfs is newer than in cache.");
else { else {
log_debug("Variable SystemdOptions in cache is up to date."); log_debug("Variable SystemdOptions in cache is up to date.");
*line = NULL; *ret = NULL;
return 0; return 0;
} }
r = read_efi_options_variable(line); r = read_efi_options_variable(ret);
if (r < 0) if (r < 0)
log_warning_errno(r, "Failed to read SystemdOptions EFI variable: %m"); return log_debug_errno(r, "Failed to read SystemdOptions EFI variable: %m");
if (r == -ENOENT)
return -ENODATA; return 0;
return r;
} }
#endif #endif

View File

@ -19,9 +19,10 @@
#define EFI_VENDOR_GLOBAL_STR SD_ID128_MAKE_UUID_STR(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c) #define EFI_VENDOR_GLOBAL_STR SD_ID128_MAKE_UUID_STR(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c)
#define EFI_VENDOR_SYSTEMD SD_ID128_MAKE(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67) #define EFI_VENDOR_SYSTEMD SD_ID128_MAKE(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67)
#define EFI_VENDOR_SYSTEMD_STR SD_ID128_MAKE_UUID_STR(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67) #define EFI_VENDOR_SYSTEMD_STR SD_ID128_MAKE_UUID_STR(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67)
#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002 #define EFI_VARIABLE_NON_VOLATILE UINT32_C(0x00000001)
#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004 #define EFI_VARIABLE_BOOTSERVICE_ACCESS UINT32_C(0x00000002)
#define EFI_VARIABLE_RUNTIME_ACCESS UINT32_C(0x00000004)
/* Note that the <lowercaseuuid>-<varname> naming scheme is an efivarfs convention, i.e. part of the Linux /* Note that the <lowercaseuuid>-<varname> naming scheme is an efivarfs convention, i.e. part of the Linux
* API file system implementation for EFI. EFI itself processes UIDS in binary form. * API file system implementation for EFI. EFI itself processes UIDS in binary form.
@ -42,8 +43,8 @@
#if ENABLE_EFI #if ENABLE_EFI
int efi_get_variable(const char *variable, uint32_t *attribute, void **value, size_t *size); int efi_get_variable(const char *variable, uint32_t *attribute, void **ret_value, size_t *ret_size);
int efi_get_variable_string(const char *variable, char **p); int efi_get_variable_string(const char *variable, char **ret);
int efi_set_variable(const char *variable, const void *value, size_t size); int efi_set_variable(const char *variable, const void *value, size_t size);
int efi_set_variable_string(const char *variable, const char *p); int efi_set_variable_string(const char *variable, const char *p);
@ -52,8 +53,8 @@ bool is_efi_secure_boot(void);
SecureBootMode efi_get_secure_boot_mode(void); SecureBootMode efi_get_secure_boot_mode(void);
int cache_efi_options_variable(void); int cache_efi_options_variable(void);
int systemd_efi_options_variable(char **line); int systemd_efi_options_variable(char **ret);
int systemd_efi_options_efivarfs_if_newer(char **line); int systemd_efi_options_efivarfs_if_newer(char **ret);
#else #else
@ -61,7 +62,7 @@ static inline int efi_get_variable(const char *variable, uint32_t *attribute, vo
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static inline int efi_get_variable_string(const char *variable, char **p) { static inline int efi_get_variable_string(const char *variable, char **ret) {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }

View File

@ -138,10 +138,18 @@ static inline bool ERRNO_IS_PRIVILEGE(int r) {
EPERM); EPERM);
} }
/* Three difference errors for "not enough disk space" */ /* Three different errors for "not enough disk space" */
static inline bool ERRNO_IS_DISK_SPACE(int r) { static inline bool ERRNO_IS_DISK_SPACE(int r) {
return IN_SET(abs(r), return IN_SET(abs(r),
ENOSPC, ENOSPC,
EDQUOT, EDQUOT,
EFBIG); EFBIG);
} }
/* Three different errors for "this device does not quite exist" */
static inline bool ERRNO_IS_DEVICE_ABSENT(int r) {
return IN_SET(abs(r),
ENODEV,
ENXIO,
ENOENT);
}

View File

@ -5,6 +5,7 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "bootspec.h" #include "bootspec.h"
#include "efi-api.h"
#include "efi-loader.h" #include "efi-loader.h"
#include "efivars.h" #include "efivars.h"
#include "fd-util.h" #include "fd-util.h"

View File

@ -17,6 +17,7 @@
#include "bootspec.h" #include "bootspec.h"
#include "copy.h" #include "copy.h"
#include "dirent-util.h" #include "dirent-util.h"
#include "efi-api.h"
#include "efi-loader.h" #include "efi-loader.h"
#include "efivars.h" #include "efivars.h"
#include "env-file.h" #include "env-file.h"

View File

@ -17,7 +17,7 @@
#include "cryptsetup-tpm2.h" #include "cryptsetup-tpm2.h"
#include "cryptsetup-util.h" #include "cryptsetup-util.h"
#include "device-util.h" #include "device-util.h"
#include "efi-loader.h" #include "efi-api.h"
#include "env-util.h" #include "env-util.h"
#include "escape.h" #include "escape.h"
#include "fileio.h" #include "fileio.h"

View File

@ -495,7 +495,7 @@ static int acquire_open_luks_device(
return r; return r;
r = sym_crypt_init_by_name(&cd, setup->dm_name); r = sym_crypt_init_by_name(&cd, setup->dm_name);
if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT) && graceful) if ((ERRNO_IS_DEVICE_ABSENT(r) || r == -EINVAL) && graceful)
return 0; return 0;
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", setup->dm_name); return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", setup->dm_name);
@ -1631,7 +1631,7 @@ int home_deactivate_luks(UserRecord *h, HomeSetup *setup) {
cryptsetup_enable_logging(setup->crypt_device); cryptsetup_enable_logging(setup->crypt_device);
r = sym_crypt_deactivate_by_name(setup->crypt_device, setup->dm_name, 0); r = sym_crypt_deactivate_by_name(setup->crypt_device, setup->dm_name, 0);
if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT)) { if (ERRNO_IS_DEVICE_ABSENT(r) || r == -EINVAL) {
log_debug_errno(r, "LUKS device %s is already detached.", setup->dm_node); log_debug_errno(r, "LUKS device %s is already detached.", setup->dm_node);
we_detached = false; we_detached = false;
} else if (r < 0) } else if (r < 0)

View File

@ -20,6 +20,7 @@
#include "cgroup-util.h" #include "cgroup-util.h"
#include "device-util.h" #include "device-util.h"
#include "dirent-util.h" #include "dirent-util.h"
#include "efi-api.h"
#include "efi-loader.h" #include "efi-loader.h"
#include "efivars.h" #include "efivars.h"
#include "env-file.h" #include "env-file.h"

View File

@ -80,7 +80,7 @@ static int find_device(
r = sd_device_new_from_subsystem_sysname(&device, "rfkill", sysname); r = sd_device_new_from_subsystem_sysname(&device, "rfkill", sysname);
if (r < 0) if (r < 0)
return log_full_errno(IN_SET(r, -ENOENT, -ENXIO, -ENODEV) ? LOG_DEBUG : LOG_ERR, r, return log_full_errno(ERRNO_IS_DEVICE_ABSENT(r) ? LOG_DEBUG : LOG_ERR, r,
"Failed to open device '%s': %m", sysname); "Failed to open device '%s': %m", sysname);
r = sd_device_get_sysattr_value(device, "name", &name); r = sd_device_get_sysattr_value(device, "name", &name);

View File

@ -22,7 +22,7 @@
#include "cgroup-util.h" #include "cgroup-util.h"
#include "condition.h" #include "condition.h"
#include "cpu-set-util.h" #include "cpu-set-util.h"
#include "efi-loader.h" #include "efi-api.h"
#include "env-file.h" #include "env-file.h"
#include "env-util.h" #include "env-util.h"
#include "extract-word.h" #include "extract-word.h"

556
src/shared/efi-api.c Normal file
View File

@ -0,0 +1,556 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <unistd.h>
#include "alloc-util.h"
#include "dirent-util.h"
#include "efi-api.h"
#include "efivars.h"
#include "fd-util.h"
#include "sort-util.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "utf8.h"
#if ENABLE_EFI
#define LOAD_OPTION_ACTIVE 0x00000001
#define MEDIA_DEVICE_PATH 0x04
#define MEDIA_HARDDRIVE_DP 0x01
#define MEDIA_FILEPATH_DP 0x04
#define SIGNATURE_TYPE_GUID 0x02
#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02
#define END_DEVICE_PATH_TYPE 0x7f
#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff
#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI UINT64_C(0x0000000000000001)
#define boot_option__contents \
{ \
uint32_t attr; \
uint16_t path_len; \
uint16_t title[]; \
}
struct boot_option boot_option__contents;
struct boot_option__packed boot_option__contents _packed_;
assert_cc(offsetof(struct boot_option, title) == offsetof(struct boot_option__packed, title));
/* sizeof(struct boot_option) != sizeof(struct boot_option__packed), so
* the *size* of the structure should not be used anywhere below. */
struct drive_path {
uint32_t part_nr;
uint64_t part_start;
uint64_t part_size;
char signature[16];
uint8_t mbr_type;
uint8_t signature_type;
} _packed_;
#define device_path__contents \
{ \
uint8_t type; \
uint8_t sub_type; \
uint16_t length; \
union { \
uint16_t path[0]; \
struct drive_path drive; \
}; \
}
struct device_path device_path__contents;
struct device_path__packed device_path__contents _packed_;
assert_cc(sizeof(struct device_path) == sizeof(struct device_path__packed));
int efi_reboot_to_firmware_supported(void) {
_cleanup_free_ void *v = NULL;
static int cache = -1;
uint64_t b;
size_t s;
int r;
if (cache > 0)
return 0;
if (cache == 0)
return -EOPNOTSUPP;
if (!is_efi_boot())
goto not_supported;
r = efi_get_variable(EFI_GLOBAL_VARIABLE(OsIndicationsSupported), NULL, &v, &s);
if (r == -ENOENT)
goto not_supported; /* variable doesn't exist? it's not supported then */
if (r < 0)
return r;
if (s != sizeof(uint64_t))
return -EINVAL;
b = *(uint64_t*) v;
if (!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI))
goto not_supported; /* bit unset? it's not supported then */
cache = 1;
return 0;
not_supported:
cache = 0;
return -EOPNOTSUPP;
}
static int get_os_indications(uint64_t *ret) {
static struct stat cache_stat = {};
_cleanup_free_ void *v = NULL;
static uint64_t cache;
struct stat new_stat;
size_t s;
int r;
assert(ret);
/* Let's verify general support first */
r = efi_reboot_to_firmware_supported();
if (r < 0)
return r;
/* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
if (stat(EFIVAR_PATH(EFI_GLOBAL_VARIABLE(OsIndications)), &new_stat) < 0) {
if (errno != ENOENT)
return -errno;
/* Doesn't exist? Then we can exit early (also see below) */
*ret = 0;
return 0;
} else if (stat_inode_unmodified(&new_stat, &cache_stat)) {
/* inode didn't change, we can return the cached value */
*ret = cache;
return 0;
}
r = efi_get_variable(EFI_GLOBAL_VARIABLE(OsIndications), NULL, &v, &s);
if (r == -ENOENT) {
/* Some firmware implementations that do support OsIndications and report that with
* OsIndicationsSupported will remove the OsIndications variable when it is unset. Let's
* pretend it's 0 then, to hide this implementation detail. Note that this call will return
* -ENOENT then only if the support for OsIndications is missing entirely, as determined by
* efi_reboot_to_firmware_supported() above. */
*ret = 0;
return 0;
}
if (r < 0)
return r;
if (s != sizeof(uint64_t))
return -EINVAL;
cache_stat = new_stat;
*ret = cache = *(uint64_t *)v;
return 0;
}
int efi_get_reboot_to_firmware(void) {
int r;
uint64_t b;
r = get_os_indications(&b);
if (r < 0)
return r;
return !!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI);
}
int efi_set_reboot_to_firmware(bool value) {
int r;
uint64_t b, b_new;
r = get_os_indications(&b);
if (r < 0)
return r;
b_new = UPDATE_FLAG(b, EFI_OS_INDICATIONS_BOOT_TO_FW_UI, value);
/* Avoid writing to efi vars store if we can due to firmware bugs. */
if (b != b_new)
return efi_set_variable(EFI_GLOBAL_VARIABLE(OsIndications), &b_new, sizeof(uint64_t));
return 0;
}
static ssize_t utf16_size(const uint16_t *s, size_t buf_len_bytes) {
size_t l = 0;
/* Returns the size of the string in bytes without the terminating two zero bytes */
if (buf_len_bytes % sizeof(uint16_t) != 0)
return -EINVAL;
while (l < buf_len_bytes / sizeof(uint16_t)) {
if (s[l] == 0)
return (l + 1) * sizeof(uint16_t);
l++;
}
return -EINVAL; /* The terminator was not found */
}
struct guid {
uint32_t u1;
uint16_t u2;
uint16_t u3;
uint8_t u4[8];
} _packed_;
static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) {
uint32_t u1;
uint16_t u2, u3;
const struct guid *uuid = guid;
memcpy(&u1, &uuid->u1, sizeof(uint32_t));
id128->bytes[0] = (u1 >> 24) & 0xff;
id128->bytes[1] = (u1 >> 16) & 0xff;
id128->bytes[2] = (u1 >> 8) & 0xff;
id128->bytes[3] = u1 & 0xff;
memcpy(&u2, &uuid->u2, sizeof(uint16_t));
id128->bytes[4] = (u2 >> 8) & 0xff;
id128->bytes[5] = u2 & 0xff;
memcpy(&u3, &uuid->u3, sizeof(uint16_t));
id128->bytes[6] = (u3 >> 8) & 0xff;
id128->bytes[7] = u3 & 0xff;
memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4));
}
int efi_get_boot_option(
uint16_t id,
char **ret_title,
sd_id128_t *ret_part_uuid,
char **ret_path,
bool *ret_active) {
char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1];
_cleanup_free_ uint8_t *buf = NULL;
size_t l;
struct boot_option *header;
ssize_t title_size;
_cleanup_free_ char *s = NULL, *p = NULL;
sd_id128_t p_uuid = SD_ID128_NULL;
int r;
if (!is_efi_boot())
return -EOPNOTSUPP;
xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id);
r = efi_get_variable(variable, NULL, (void **)&buf, &l);
if (r < 0)
return r;
if (l < offsetof(struct boot_option, title))
return -ENOENT;
header = (struct boot_option *)buf;
title_size = utf16_size(header->title, l - offsetof(struct boot_option, title));
if (title_size < 0)
return title_size;
if (ret_title) {
s = utf16_to_utf8(header->title, title_size);
if (!s)
return -ENOMEM;
}
if (header->path_len > 0) {
uint8_t *dbuf;
size_t dnext, doff;
doff = offsetof(struct boot_option, title) + title_size;
dbuf = buf + doff;
if (header->path_len > l - doff)
return -EINVAL;
dnext = 0;
while (dnext < header->path_len) {
struct device_path *dpath;
dpath = (struct device_path *)(dbuf + dnext);
if (dpath->length < 4)
break;
/* Type 0x7F End of Hardware Device Path, Sub-Type 0xFF End Entire Device Path */
if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE)
break;
dnext += dpath->length;
/* Type 0x04 Media Device Path */
if (dpath->type != MEDIA_DEVICE_PATH)
continue;
/* Sub-Type 1 Hard Drive */
if (dpath->sub_type == MEDIA_HARDDRIVE_DP) {
/* 0x02 GUID Partition Table */
if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER)
continue;
/* 0x02 GUID signature */
if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID)
continue;
if (ret_part_uuid)
efi_guid_to_id128(dpath->drive.signature, &p_uuid);
continue;
}
/* Sub-Type 4 File Path */
if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && ret_path) {
p = utf16_to_utf8(dpath->path, dpath->length-4);
if (!p)
return -ENOMEM;
efi_tilt_backslashes(p);
continue;
}
}
}
if (ret_title)
*ret_title = TAKE_PTR(s);
if (ret_part_uuid)
*ret_part_uuid = p_uuid;
if (ret_path)
*ret_path = TAKE_PTR(p);
if (ret_active)
*ret_active = header->attr & LOAD_OPTION_ACTIVE;
return 0;
}
static void to_utf16(uint16_t *dest, const char *src) {
int i;
for (i = 0; src[i] != '\0'; i++)
dest[i] = src[i];
dest[i] = '\0';
}
static void id128_to_efi_guid(sd_id128_t id, void *guid) {
struct guid uuid = {
.u1 = id.bytes[0] << 24 | id.bytes[1] << 16 | id.bytes[2] << 8 | id.bytes[3],
.u2 = id.bytes[4] << 8 | id.bytes[5],
.u3 = id.bytes[6] << 8 | id.bytes[7],
};
memcpy(uuid.u4, id.bytes+8, sizeof(uuid.u4));
memcpy(guid, &uuid, sizeof(uuid));
}
static uint16_t *tilt_slashes(uint16_t *s) {
for (uint16_t *p = s; *p; p++)
if (*p == '/')
*p = '\\';
return s;
}
int efi_add_boot_option(
uint16_t id,
const char *title,
uint32_t part,
uint64_t pstart,
uint64_t psize,
sd_id128_t part_uuid,
const char *path) {
size_t size, title_len, path_len;
_cleanup_free_ char *buf = NULL;
struct boot_option *option;
struct device_path *devicep;
char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1];
if (!is_efi_boot())
return -EOPNOTSUPP;
title_len = (strlen(title)+1) * 2;
path_len = (strlen(path)+1) * 2;
buf = malloc0(offsetof(struct boot_option, title) + title_len +
sizeof(struct drive_path) +
sizeof(struct device_path) + path_len);
if (!buf)
return -ENOMEM;
/* header */
option = (struct boot_option *)buf;
option->attr = LOAD_OPTION_ACTIVE;
option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) +
offsetof(struct device_path, path) + path_len +
offsetof(struct device_path, path);
to_utf16(option->title, title);
size = offsetof(struct boot_option, title) + title_len;
/* partition info */
devicep = (struct device_path *)(buf + size);
devicep->type = MEDIA_DEVICE_PATH;
devicep->sub_type = MEDIA_HARDDRIVE_DP;
devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path);
memcpy(&devicep->drive.part_nr, &part, sizeof(uint32_t));
memcpy(&devicep->drive.part_start, &pstart, sizeof(uint64_t));
memcpy(&devicep->drive.part_size, &psize, sizeof(uint64_t));
id128_to_efi_guid(part_uuid, devicep->drive.signature);
devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
devicep->drive.signature_type = SIGNATURE_TYPE_GUID;
size += devicep->length;
/* path to loader */
devicep = (struct device_path *)(buf + size);
devicep->type = MEDIA_DEVICE_PATH;
devicep->sub_type = MEDIA_FILEPATH_DP;
devicep->length = offsetof(struct device_path, path) + path_len;
to_utf16(devicep->path, path);
tilt_slashes(devicep->path);
size += devicep->length;
/* end of path */
devicep = (struct device_path *)(buf + size);
devicep->type = END_DEVICE_PATH_TYPE;
devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE;
devicep->length = offsetof(struct device_path, path);
size += devicep->length;
xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id);
return efi_set_variable(variable, buf, size);
}
int efi_remove_boot_option(uint16_t id) {
char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1];
if (!is_efi_boot())
return -EOPNOTSUPP;
xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id);
return efi_set_variable(variable, NULL, 0);
}
int efi_get_boot_order(uint16_t **ret_order) {
_cleanup_free_ void *buf = NULL;
size_t l;
int r;
assert(ret_order);
if (!is_efi_boot())
return -EOPNOTSUPP;
r = efi_get_variable(EFI_GLOBAL_VARIABLE(BootOrder), NULL, &buf, &l);
if (r < 0)
return r;
if (l <= 0)
return -ENOENT;
if (l % sizeof(uint16_t) > 0 ||
l / sizeof(uint16_t) > INT_MAX)
return -EINVAL;
*ret_order = TAKE_PTR(buf);
return (int) (l / sizeof(uint16_t));
}
int efi_set_boot_order(const uint16_t *order, size_t n) {
if (!is_efi_boot())
return -EOPNOTSUPP;
return efi_set_variable(EFI_GLOBAL_VARIABLE(BootOrder), order, n * sizeof(uint16_t));
}
static int boot_id_hex(const char s[static 4]) {
int id = 0;
assert(s);
for (int i = 0; i < 4; i++)
if (s[i] >= '0' && s[i] <= '9')
id |= (s[i] - '0') << (3 - i) * 4;
else if (s[i] >= 'A' && s[i] <= 'F')
id |= (s[i] - 'A' + 10) << (3 - i) * 4;
else
return -EINVAL;
return id;
}
static int cmp_uint16(const uint16_t *a, const uint16_t *b) {
return CMP(*a, *b);
}
int efi_get_boot_options(uint16_t **ret_options) {
_cleanup_closedir_ DIR *dir = NULL;
_cleanup_free_ uint16_t *list = NULL;
int count = 0;
assert(ret_options);
if (!is_efi_boot())
return -EOPNOTSUPP;
dir = opendir(EFIVAR_PATH("."));
if (!dir)
return -errno;
FOREACH_DIRENT(de, dir, return -errno) {
int id;
if (strncmp(de->d_name, "Boot", 4) != 0)
continue;
if (strlen(de->d_name) != 45)
continue;
if (strcmp(de->d_name + 8, EFI_GLOBAL_VARIABLE_STR("")) != 0) /* generate variable suffix using macro */
continue;
id = boot_id_hex(de->d_name + 4);
if (id < 0)
continue;
if (!GREEDY_REALLOC(list, count + 1))
return -ENOMEM;
list[count++] = id;
}
typesafe_qsort(list, count, cmp_uint16);
*ret_options = TAKE_PTR(list);
return count;
}
bool efi_has_tpm2(void) {
static int cache = -1;
/* Returns whether the system has a TPM2 chip which is known to the EFI firmware. */
if (cache < 0) {
/* First, check if we are on an EFI boot at all. */
if (!is_efi_boot())
cache = false;
else {
/* Then, check if the ACPI table "TPM2" exists, which is the TPM2 event log table, see:
* https://trustedcomputinggroup.org/wp-content/uploads/TCG_ACPIGeneralSpecification_v1.20_r8.pdf
* This table exists whenever the firmware is hooked up to TPM2. */
cache = access("/sys/firmware/acpi/tables/TPM2", F_OK) >= 0;
if (!cache && errno != ENOENT)
log_debug_errno(errno, "Unable to test whether /sys/firmware/acpi/tables/TPM2 exists, assuming it doesn't: %m");
}
}
return cache;
}
#endif
char *efi_tilt_backslashes(char *s) {
for (char *p = s; *p; p++)
if (*p == '\\')
*p = '/';
return s;
}

68
src/shared/efi-api.h Normal file
View File

@ -0,0 +1,68 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "efivars-fundamental.h"
#include "efivars.h"
/* Various calls for interfacing with EFI variables from the official UEFI specs. */
#if ENABLE_EFI
int efi_reboot_to_firmware_supported(void);
int efi_get_reboot_to_firmware(void);
int efi_set_reboot_to_firmware(bool value);
int efi_get_boot_option(uint16_t nr, char **ret_title, sd_id128_t *ret_part_uuid, char **ret_path, bool *ret_active);
int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path);
int efi_remove_boot_option(uint16_t id);
int efi_get_boot_order(uint16_t **ret_order);
int efi_set_boot_order(const uint16_t *order, size_t n);
int efi_get_boot_options(uint16_t **ret_options);
bool efi_has_tpm2(void);
#else
static inline int efi_reboot_to_firmware_supported(void) {
return -EOPNOTSUPP;
}
static inline int efi_get_reboot_to_firmware(void) {
return -EOPNOTSUPP;
}
static inline int efi_set_reboot_to_firmware(bool value) {
return -EOPNOTSUPP;
}
static inline int efi_get_boot_option(uint16_t nr, char **ret_title, sd_id128_t *ret_part_uuid, char **ret_path, bool *ret_active) {
return -EOPNOTSUPP;
}
static inline int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path) {
return -EOPNOTSUPP;
}
static inline int efi_remove_boot_option(uint16_t id) {
return -EOPNOTSUPP;
}
static inline int efi_get_boot_order(uint16_t **ret_order) {
return -EOPNOTSUPP;
}
static inline int efi_set_boot_order(const uint16_t *order, size_t n) {
return -EOPNOTSUPP;
}
static inline int efi_get_boot_options(uint16_t **ret_options) {
return -EOPNOTSUPP;
}
static inline bool efi_has_tpm2(void) {
return false;
}
#endif
char *efi_tilt_backslashes(char *s);

View File

@ -1,537 +1,22 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include "alloc-util.h" #include "alloc-util.h"
#include "dirent-util.h"
#include "efi-loader.h" #include "efi-loader.h"
#include "efivars.h"
#include "fd-util.h"
#include "io-util.h"
#include "parse-util.h" #include "parse-util.h"
#include "sort-util.h" #include "path-util.h"
#include "stat-util.h" #include "stat-util.h"
#include "stdio-util.h" #include "strv.h"
#include "string-util.h"
#include "utf8.h" #include "utf8.h"
#include "virt.h"
#if ENABLE_EFI #if ENABLE_EFI
#define LOAD_OPTION_ACTIVE 0x00000001 static int read_usec(const char *variable, usec_t *ret) {
#define MEDIA_DEVICE_PATH 0x04
#define MEDIA_HARDDRIVE_DP 0x01
#define MEDIA_FILEPATH_DP 0x04
#define SIGNATURE_TYPE_GUID 0x02
#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02
#define END_DEVICE_PATH_TYPE 0x7f
#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff
#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001
#define boot_option__contents \
{ \
uint32_t attr; \
uint16_t path_len; \
uint16_t title[]; \
}
struct boot_option boot_option__contents;
struct boot_option__packed boot_option__contents _packed_;
assert_cc(offsetof(struct boot_option, title) == offsetof(struct boot_option__packed, title));
/* sizeof(struct boot_option) != sizeof(struct boot_option__packed), so
* the *size* of the structure should not be used anywhere below. */
struct drive_path {
uint32_t part_nr;
uint64_t part_start;
uint64_t part_size;
char signature[16];
uint8_t mbr_type;
uint8_t signature_type;
} _packed_;
#define device_path__contents \
{ \
uint8_t type; \
uint8_t sub_type; \
uint16_t length; \
union { \
uint16_t path[0]; \
struct drive_path drive; \
}; \
}
struct device_path device_path__contents;
struct device_path__packed device_path__contents _packed_;
assert_cc(sizeof(struct device_path) == sizeof(struct device_path__packed));
int efi_reboot_to_firmware_supported(void) {
_cleanup_free_ void *v = NULL;
static int cache = -1;
uint64_t b;
size_t s;
int r;
if (cache > 0)
return 0;
if (cache == 0)
return -EOPNOTSUPP;
if (!is_efi_boot())
goto not_supported;
r = efi_get_variable(EFI_GLOBAL_VARIABLE(OsIndicationsSupported), NULL, &v, &s);
if (r == -ENOENT)
goto not_supported; /* variable doesn't exist? it's not supported then */
if (r < 0)
return r;
if (s != sizeof(uint64_t))
return -EINVAL;
b = *(uint64_t*) v;
if (!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI))
goto not_supported; /* bit unset? it's not supported then */
cache = 1;
return 0;
not_supported:
cache = 0;
return -EOPNOTSUPP;
}
static int get_os_indications(uint64_t *ret) {
static struct stat cache_stat = {};
_cleanup_free_ void *v = NULL;
static uint64_t cache;
struct stat new_stat;
size_t s;
int r;
assert(ret);
/* Let's verify general support first */
r = efi_reboot_to_firmware_supported();
if (r < 0)
return r;
/* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
if (stat(EFIVAR_PATH(EFI_GLOBAL_VARIABLE(OsIndications)), &new_stat) < 0) {
if (errno != ENOENT)
return -errno;
/* Doesn't exist? Then we can exit early (also see below) */
*ret = 0;
return 0;
} else if (stat_inode_unmodified(&new_stat, &cache_stat)) {
/* inode didn't change, we can return the cached value */
*ret = cache;
return 0;
}
r = efi_get_variable(EFI_GLOBAL_VARIABLE(OsIndications), NULL, &v, &s);
if (r == -ENOENT) {
/* Some firmware implementations that do support OsIndications and report that with
* OsIndicationsSupported will remove the OsIndications variable when it is unset. Let's
* pretend it's 0 then, to hide this implementation detail. Note that this call will return
* -ENOENT then only if the support for OsIndications is missing entirely, as determined by
* efi_reboot_to_firmware_supported() above. */
*ret = 0;
return 0;
}
if (r < 0)
return r;
if (s != sizeof(uint64_t))
return -EINVAL;
cache_stat = new_stat;
*ret = cache = *(uint64_t *)v;
return 0;
}
int efi_get_reboot_to_firmware(void) {
int r;
uint64_t b;
r = get_os_indications(&b);
if (r < 0)
return r;
return !!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI);
}
int efi_set_reboot_to_firmware(bool value) {
int r;
uint64_t b, b_new;
r = get_os_indications(&b);
if (r < 0)
return r;
b_new = UPDATE_FLAG(b, EFI_OS_INDICATIONS_BOOT_TO_FW_UI, value);
/* Avoid writing to efi vars store if we can due to firmware bugs. */
if (b != b_new)
return efi_set_variable(EFI_GLOBAL_VARIABLE(OsIndications), &b_new, sizeof(uint64_t));
return 0;
}
static ssize_t utf16_size(const uint16_t *s, size_t buf_len_bytes) {
size_t l = 0;
/* Returns the size of the string in bytes without the terminating two zero bytes */
if (buf_len_bytes % sizeof(uint16_t) != 0)
return -EINVAL;
while (l < buf_len_bytes / sizeof(uint16_t)) {
if (s[l] == 0)
return (l + 1) * sizeof(uint16_t);
l++;
}
return -EINVAL; /* The terminator was not found */
}
struct guid {
uint32_t u1;
uint16_t u2;
uint16_t u3;
uint8_t u4[8];
} _packed_;
static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) {
uint32_t u1;
uint16_t u2, u3;
const struct guid *uuid = guid;
memcpy(&u1, &uuid->u1, sizeof(uint32_t));
id128->bytes[0] = (u1 >> 24) & 0xff;
id128->bytes[1] = (u1 >> 16) & 0xff;
id128->bytes[2] = (u1 >> 8) & 0xff;
id128->bytes[3] = u1 & 0xff;
memcpy(&u2, &uuid->u2, sizeof(uint16_t));
id128->bytes[4] = (u2 >> 8) & 0xff;
id128->bytes[5] = u2 & 0xff;
memcpy(&u3, &uuid->u3, sizeof(uint16_t));
id128->bytes[6] = (u3 >> 8) & 0xff;
id128->bytes[7] = u3 & 0xff;
memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4));
}
int efi_get_boot_option(
uint16_t id,
char **title,
sd_id128_t *part_uuid,
char **path,
bool *active) {
char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1];
_cleanup_free_ uint8_t *buf = NULL;
size_t l;
struct boot_option *header;
ssize_t title_size;
_cleanup_free_ char *s = NULL, *p = NULL;
sd_id128_t p_uuid = SD_ID128_NULL;
int r;
if (!is_efi_boot())
return -EOPNOTSUPP;
xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id);
r = efi_get_variable(variable, NULL, (void **)&buf, &l);
if (r < 0)
return r;
if (l < offsetof(struct boot_option, title))
return -ENOENT;
header = (struct boot_option *)buf;
title_size = utf16_size(header->title, l - offsetof(struct boot_option, title));
if (title_size < 0)
return title_size;
if (title) {
s = utf16_to_utf8(header->title, title_size);
if (!s)
return -ENOMEM;
}
if (header->path_len > 0) {
uint8_t *dbuf;
size_t dnext, doff;
doff = offsetof(struct boot_option, title) + title_size;
dbuf = buf + doff;
if (header->path_len > l - doff)
return -EINVAL;
dnext = 0;
while (dnext < header->path_len) {
struct device_path *dpath;
dpath = (struct device_path *)(dbuf + dnext);
if (dpath->length < 4)
break;
/* Type 0x7F End of Hardware Device Path, Sub-Type 0xFF End Entire Device Path */
if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE)
break;
dnext += dpath->length;
/* Type 0x04 Media Device Path */
if (dpath->type != MEDIA_DEVICE_PATH)
continue;
/* Sub-Type 1 Hard Drive */
if (dpath->sub_type == MEDIA_HARDDRIVE_DP) {
/* 0x02 GUID Partition Table */
if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER)
continue;
/* 0x02 GUID signature */
if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID)
continue;
if (part_uuid)
efi_guid_to_id128(dpath->drive.signature, &p_uuid);
continue;
}
/* Sub-Type 4 File Path */
if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && path) {
p = utf16_to_utf8(dpath->path, dpath->length-4);
if (!p)
return -ENOMEM;
efi_tilt_backslashes(p);
continue;
}
}
}
if (title)
*title = TAKE_PTR(s);
if (part_uuid)
*part_uuid = p_uuid;
if (path)
*path = TAKE_PTR(p);
if (active)
*active = header->attr & LOAD_OPTION_ACTIVE;
return 0;
}
static void to_utf16(uint16_t *dest, const char *src) {
int i;
for (i = 0; src[i] != '\0'; i++)
dest[i] = src[i];
dest[i] = '\0';
}
static void id128_to_efi_guid(sd_id128_t id, void *guid) {
struct guid uuid = {
.u1 = id.bytes[0] << 24 | id.bytes[1] << 16 | id.bytes[2] << 8 | id.bytes[3],
.u2 = id.bytes[4] << 8 | id.bytes[5],
.u3 = id.bytes[6] << 8 | id.bytes[7],
};
memcpy(uuid.u4, id.bytes+8, sizeof(uuid.u4));
memcpy(guid, &uuid, sizeof(uuid));
}
static uint16_t *tilt_slashes(uint16_t *s) {
for (uint16_t *p = s; *p; p++)
if (*p == '/')
*p = '\\';
return s;
}
int efi_add_boot_option(
uint16_t id,
const char *title,
uint32_t part,
uint64_t pstart,
uint64_t psize,
sd_id128_t part_uuid,
const char *path) {
size_t size, title_len, path_len;
_cleanup_free_ char *buf = NULL;
struct boot_option *option;
struct device_path *devicep;
char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1];
if (!is_efi_boot())
return -EOPNOTSUPP;
title_len = (strlen(title)+1) * 2;
path_len = (strlen(path)+1) * 2;
buf = malloc0(offsetof(struct boot_option, title) + title_len +
sizeof(struct drive_path) +
sizeof(struct device_path) + path_len);
if (!buf)
return -ENOMEM;
/* header */
option = (struct boot_option *)buf;
option->attr = LOAD_OPTION_ACTIVE;
option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) +
offsetof(struct device_path, path) + path_len +
offsetof(struct device_path, path);
to_utf16(option->title, title);
size = offsetof(struct boot_option, title) + title_len;
/* partition info */
devicep = (struct device_path *)(buf + size);
devicep->type = MEDIA_DEVICE_PATH;
devicep->sub_type = MEDIA_HARDDRIVE_DP;
devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path);
memcpy(&devicep->drive.part_nr, &part, sizeof(uint32_t));
memcpy(&devicep->drive.part_start, &pstart, sizeof(uint64_t));
memcpy(&devicep->drive.part_size, &psize, sizeof(uint64_t));
id128_to_efi_guid(part_uuid, devicep->drive.signature);
devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
devicep->drive.signature_type = SIGNATURE_TYPE_GUID;
size += devicep->length;
/* path to loader */
devicep = (struct device_path *)(buf + size);
devicep->type = MEDIA_DEVICE_PATH;
devicep->sub_type = MEDIA_FILEPATH_DP;
devicep->length = offsetof(struct device_path, path) + path_len;
to_utf16(devicep->path, path);
tilt_slashes(devicep->path);
size += devicep->length;
/* end of path */
devicep = (struct device_path *)(buf + size);
devicep->type = END_DEVICE_PATH_TYPE;
devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE;
devicep->length = offsetof(struct device_path, path);
size += devicep->length;
xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id);
return efi_set_variable(variable, buf, size);
}
int efi_remove_boot_option(uint16_t id) {
char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1];
if (!is_efi_boot())
return -EOPNOTSUPP;
xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id);
return efi_set_variable(variable, NULL, 0);
}
int efi_get_boot_order(uint16_t **order) {
_cleanup_free_ void *buf = NULL;
size_t l;
int r;
if (!is_efi_boot())
return -EOPNOTSUPP;
r = efi_get_variable(EFI_GLOBAL_VARIABLE(BootOrder), NULL, &buf, &l);
if (r < 0)
return r;
if (l <= 0)
return -ENOENT;
if (l % sizeof(uint16_t) > 0 ||
l / sizeof(uint16_t) > INT_MAX)
return -EINVAL;
*order = TAKE_PTR(buf);
return (int) (l / sizeof(uint16_t));
}
int efi_set_boot_order(uint16_t *order, size_t n) {
if (!is_efi_boot())
return -EOPNOTSUPP;
return efi_set_variable(EFI_GLOBAL_VARIABLE(BootOrder), order, n * sizeof(uint16_t));
}
static int boot_id_hex(const char s[static 4]) {
int id = 0;
assert(s);
for (int i = 0; i < 4; i++)
if (s[i] >= '0' && s[i] <= '9')
id |= (s[i] - '0') << (3 - i) * 4;
else if (s[i] >= 'A' && s[i] <= 'F')
id |= (s[i] - 'A' + 10) << (3 - i) * 4;
else
return -EINVAL;
return id;
}
static int cmp_uint16(const uint16_t *a, const uint16_t *b) {
return CMP(*a, *b);
}
int efi_get_boot_options(uint16_t **options) {
_cleanup_closedir_ DIR *dir = NULL;
_cleanup_free_ uint16_t *list = NULL;
int count = 0;
assert(options);
if (!is_efi_boot())
return -EOPNOTSUPP;
dir = opendir(EFIVAR_PATH("."));
if (!dir)
return -errno;
FOREACH_DIRENT(de, dir, return -errno) {
int id;
if (strncmp(de->d_name, "Boot", 4) != 0)
continue;
if (strlen(de->d_name) != 45)
continue;
if (strcmp(de->d_name + 8, EFI_GLOBAL_VARIABLE_STR("")) != 0) /* generate variable suffix using macro */
continue;
id = boot_id_hex(de->d_name + 4);
if (id < 0)
continue;
if (!GREEDY_REALLOC(list, count + 1))
return -ENOMEM;
list[count++] = id;
}
typesafe_qsort(list, count, cmp_uint16);
*options = TAKE_PTR(list);
return count;
}
static int read_usec(const char *variable, usec_t *u) {
_cleanup_free_ char *j = NULL; _cleanup_free_ char *j = NULL;
int r;
uint64_t x = 0; uint64_t x = 0;
int r;
assert(variable); assert(variable);
assert(u); assert(ret);
r = efi_get_variable_string(variable, &j); r = efi_get_variable_string(variable, &j);
if (r < 0) if (r < 0)
@ -541,16 +26,16 @@ static int read_usec(const char *variable, usec_t *u) {
if (r < 0) if (r < 0)
return r; return r;
*u = x; *ret = x;
return 0; return 0;
} }
int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) { int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader) {
uint64_t x, y; uint64_t x, y;
int r; int r;
assert(firmware); assert(ret_firmware);
assert(loader); assert(ret_loader);
if (!is_efi_boot()) if (!is_efi_boot())
return -EOPNOTSUPP; return -EOPNOTSUPP;
@ -568,16 +53,17 @@ int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) {
"Bad LoaderTimeInitUSec=%"PRIu64", LoaderTimeExecUSec=%" PRIu64"; refusing.", "Bad LoaderTimeInitUSec=%"PRIu64", LoaderTimeExecUSec=%" PRIu64"; refusing.",
x, y); x, y);
*firmware = x; *ret_firmware = x;
*loader = y; *ret_loader = y;
return 0; return 0;
} }
int efi_loader_get_device_part_uuid(sd_id128_t *u) { int efi_loader_get_device_part_uuid(sd_id128_t *ret) {
_cleanup_free_ char *p = NULL; _cleanup_free_ char *p = NULL;
int r, parsed[16]; int r, parsed[16];
assert(ret);
if (!is_efi_boot()) if (!is_efi_boot())
return -EOPNOTSUPP; return -EOPNOTSUPP;
@ -592,9 +78,9 @@ int efi_loader_get_device_part_uuid(sd_id128_t *u) {
&parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16) &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16)
return -EIO; return -EIO;
if (u) if (ret)
for (unsigned i = 0; i < ELEMENTSOF(parsed); i++) for (unsigned i = 0; i < ELEMENTSOF(parsed); i++)
u->bytes[i] = parsed[i]; ret->bytes[i] = parsed[i];
return 0; return 0;
} }
@ -657,6 +143,8 @@ int efi_loader_get_features(uint64_t *ret) {
size_t s; size_t s;
int r; int r;
assert(ret);
if (!is_efi_boot()) { if (!is_efi_boot()) {
*ret = 0; *ret = 0;
return 0; return 0;
@ -763,29 +251,6 @@ int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat
return 0; return 0;
} }
bool efi_has_tpm2(void) {
static int cache = -1;
/* Returns whether the system has a TPM2 chip which is known to the EFI firmware. */
if (cache < 0) {
/* First, check if we are on an EFI boot at all. */
if (!is_efi_boot())
cache = false;
else {
/* Then, check if the ACPI table "TPM2" exists, which is the TPM2 event log table, see:
* https://trustedcomputinggroup.org/wp-content/uploads/TCG_ACPIGeneralSpecification_v1.20_r8.pdf
* This table exists whenever the firmware is hooked up to TPM2. */
cache = access("/sys/firmware/acpi/tables/TPM2", F_OK) >= 0;
if (!cache && errno != ENOENT)
log_debug_errno(errno, "Unable to test whether /sys/firmware/acpi/tables/TPM2 exists, assuming it doesn't: %m");
}
}
return cache;
}
#endif #endif
bool efi_loader_entry_name_valid(const char *s) { bool efi_loader_entry_name_valid(const char *s) {
@ -794,11 +259,3 @@ bool efi_loader_entry_name_valid(const char *s) {
return in_charset(s, ALPHANUMERICAL "+-_."); return in_charset(s, ALPHANUMERICAL "+-_.");
} }
char *efi_tilt_backslashes(char *s) {
for (char *p = s; *p; p++)
if (*p == '\\')
*p = '/';
return s;
}

View File

@ -6,21 +6,12 @@
#include "efivars-fundamental.h" #include "efivars-fundamental.h"
#include "efivars.h" #include "efivars.h"
/* Various calls that interface with EFI variables implementing https://systemd.io/BOOT_LOADER_INTERFACE */
#if ENABLE_EFI #if ENABLE_EFI
int efi_reboot_to_firmware_supported(void); int efi_loader_get_device_part_uuid(sd_id128_t *ret);
int efi_get_reboot_to_firmware(void); int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader);
int efi_set_reboot_to_firmware(bool value);
int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active);
int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path);
int efi_remove_boot_option(uint16_t id);
int efi_get_boot_order(uint16_t **order);
int efi_set_boot_order(uint16_t *order, size_t n);
int efi_get_boot_options(uint16_t **options);
int efi_loader_get_device_part_uuid(sd_id128_t *u);
int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader);
int efi_loader_get_entries(char ***ret); int efi_loader_get_entries(char ***ret);
@ -29,46 +20,8 @@ int efi_loader_get_features(uint64_t *ret);
int efi_loader_get_config_timeout_one_shot(usec_t *ret); int efi_loader_get_config_timeout_one_shot(usec_t *ret);
int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat); int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat);
bool efi_has_tpm2(void);
#else #else
static inline int efi_reboot_to_firmware_supported(void) {
return -EOPNOTSUPP;
}
static inline int efi_get_reboot_to_firmware(void) {
return -EOPNOTSUPP;
}
static inline int efi_set_reboot_to_firmware(bool value) {
return -EOPNOTSUPP;
}
static inline int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active) {
return -EOPNOTSUPP;
}
static inline int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path) {
return -EOPNOTSUPP;
}
static inline int efi_remove_boot_option(uint16_t id) {
return -EOPNOTSUPP;
}
static inline int efi_get_boot_order(uint16_t **order) {
return -EOPNOTSUPP;
}
static inline int efi_set_boot_order(uint16_t *order, size_t n) {
return -EOPNOTSUPP;
}
static inline int efi_get_boot_options(uint16_t **options) {
return -EOPNOTSUPP;
}
static inline int efi_loader_get_device_part_uuid(sd_id128_t *u) { static inline int efi_loader_get_device_part_uuid(sd_id128_t *u) {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
@ -93,12 +46,6 @@ static inline int efi_loader_update_entry_one_shot_cache(char **cache, struct st
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static inline bool efi_has_tpm2(void) {
return false;
}
#endif #endif
bool efi_loader_entry_name_valid(const char *s); bool efi_loader_entry_name_valid(const char *s);
char *efi_tilt_backslashes(char *s);

View File

@ -105,6 +105,8 @@ shared_sources = files(
'dns-domain.h', 'dns-domain.h',
'dropin.c', 'dropin.c',
'dropin.h', 'dropin.h',
'efi-api.c',
'efi-api.h',
'efi-loader.c', 'efi-loader.c',
'efi-loader.h', 'efi-loader.h',
'elf-util.c', 'elf-util.c',

View File

@ -6,6 +6,7 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include "device-util.h" #include "device-util.h"
#include "errno-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "string-util.h" #include "string-util.h"
#include "strxcpyx.h" #include "strxcpyx.h"
@ -22,7 +23,7 @@ static int builtin_btrfs(sd_device *dev, sd_netlink **rtnl, int argc, char *argv
fd = open("/dev/btrfs-control", O_RDWR|O_CLOEXEC); fd = open("/dev/btrfs-control", O_RDWR|O_CLOEXEC);
if (fd < 0) { if (fd < 0) {
if (IN_SET(errno, ENOENT, ENXIO, ENODEV)) { if (ERRNO_IS_DEVICE_ABSENT(errno)) {
/* Driver not installed? Then we aren't ready. This is useful in initrds that lack /* Driver not installed? Then we aren't ready. This is useful in initrds that lack
* btrfs.ko. After the host transition (where btrfs.ko will hopefully become * btrfs.ko. After the host transition (where btrfs.ko will hopefully become
* available) the device can be retriggered and will then be considered ready. */ * available) the device can be retriggered and will then be considered ready. */

View File

@ -101,7 +101,7 @@ static void udev_ctrl_disconnect(UdevCtrl *uctrl) {
if (!uctrl) if (!uctrl)
return; return;
uctrl->event_source_connect = sd_event_source_unref(uctrl->event_source_connect); uctrl->event_source_connect = sd_event_source_disable_unref(uctrl->event_source_connect);
uctrl->sock_connect = safe_close(uctrl->sock_connect); uctrl->sock_connect = safe_close(uctrl->sock_connect);
} }
@ -110,7 +110,7 @@ static UdevCtrl *udev_ctrl_free(UdevCtrl *uctrl) {
udev_ctrl_disconnect(uctrl); udev_ctrl_disconnect(uctrl);
sd_event_source_unref(uctrl->event_source); sd_event_source_disable_unref(uctrl->event_source);
safe_close(uctrl->sock); safe_close(uctrl->sock);
sd_event_unref(uctrl->event); sd_event_unref(uctrl->event);
@ -323,7 +323,7 @@ int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, const void *data)
} }
int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout) { int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout) {
_cleanup_(sd_event_source_unrefp) sd_event_source *source_io = NULL, *source_timeout = NULL; _cleanup_(sd_event_source_disable_unrefp) sd_event_source *source_io = NULL, *source_timeout = NULL;
int r; int r;
assert(uctrl); assert(uctrl);

View File

@ -695,9 +695,9 @@ static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userd
static int spawn_wait(Spawn *spawn) { static int spawn_wait(Spawn *spawn) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL; _cleanup_(sd_event_unrefp) sd_event *e = NULL;
_cleanup_(sd_event_source_unrefp) sd_event_source *sigchld_source = NULL; _cleanup_(sd_event_source_disable_unrefp) sd_event_source *sigchld_source = NULL;
_cleanup_(sd_event_source_unrefp) sd_event_source *stdout_source = NULL; _cleanup_(sd_event_source_disable_unrefp) sd_event_source *stdout_source = NULL;
_cleanup_(sd_event_source_unrefp) sd_event_source *stderr_source = NULL; _cleanup_(sd_event_source_disable_unrefp) sd_event_source *stderr_source = NULL;
int r; int r;
assert(spawn); assert(spawn);

View File

@ -165,18 +165,12 @@ static Event *event_free(Event *event) {
sd_device_unref(event->dev); sd_device_unref(event->dev);
sd_device_unref(event->dev_kernel); sd_device_unref(event->dev_kernel);
sd_event_source_unref(event->timeout_warning_event); sd_event_source_disable_unref(event->timeout_warning_event);
sd_event_source_unref(event->timeout_event); sd_event_source_disable_unref(event->timeout_event);
if (event->worker) if (event->worker)
event->worker->event = NULL; event->worker->event = NULL;
/* only clean up the queue from the process that created it */
if (LIST_IS_EMPTY(event->manager->events) &&
event->manager->pid == getpid_cached())
if (unlink("/run/udev/queue") < 0 && errno != ENOENT)
log_warning_errno(errno, "Failed to unlink /run/udev/queue, ignoring: %m");
return mfree(event); return mfree(event);
} }
@ -208,8 +202,8 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(worker_hash_op, void, trivial_hash
static void manager_clear_for_worker(Manager *manager) { static void manager_clear_for_worker(Manager *manager) {
assert(manager); assert(manager);
manager->inotify_event = sd_event_source_unref(manager->inotify_event); manager->inotify_event = sd_event_source_disable_unref(manager->inotify_event);
manager->kill_workers_event = sd_event_source_unref(manager->kill_workers_event); manager->kill_workers_event = sd_event_source_disable_unref(manager->kill_workers_event);
manager->event = sd_event_unref(manager->event); manager->event = sd_event_unref(manager->event);
@ -306,7 +300,7 @@ static void manager_exit(Manager *manager) {
/* close sources of new events and discard buffered events */ /* close sources of new events and discard buffered events */
manager->ctrl = udev_ctrl_unref(manager->ctrl); manager->ctrl = udev_ctrl_unref(manager->ctrl);
manager->inotify_event = sd_event_source_unref(manager->inotify_event); manager->inotify_event = sd_event_source_disable_unref(manager->inotify_event);
manager->inotify_fd = safe_close(manager->inotify_fd); manager->inotify_fd = safe_close(manager->inotify_fd);
manager->monitor = sd_device_monitor_unref(manager->monitor); manager->monitor = sd_device_monitor_unref(manager->monitor);
@ -405,8 +399,10 @@ static int worker_lock_block_device(sd_device *dev, int *ret_fd) {
fd = open(val, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); fd = open(val, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
if (fd < 0) { if (fd < 0) {
log_device_debug_errno(dev, errno, "Failed to open '%s', ignoring: %m", val); bool ignore = ERRNO_IS_DEVICE_ABSENT(errno);
return 0;
log_device_debug_errno(dev, errno, "Failed to open '%s'%s: %m", val, ignore ? ", ignoring" : "");
return ignore ? 0 : -errno;
} }
if (flock(fd, LOCK_SH|LOCK_NB) < 0) if (flock(fd, LOCK_SH|LOCK_NB) < 0)
@ -764,10 +760,6 @@ static int event_run(Event *event) {
/* Re-enable the debug message for the next batch of events */ /* Re-enable the debug message for the next batch of events */
log_children_max_reached = true; log_children_max_reached = true;
/* fork with up-to-date SELinux label database, so the child inherits the up-to-date db
* and, until the next SELinux policy changes, we safe further reloads in future children */
mac_selinux_maybe_reload();
/* start new worker and pass initial device */ /* start new worker and pass initial device */
r = worker_spawn(manager, event); r = worker_spawn(manager, event);
if (r < 0) if (r < 0)
@ -949,30 +941,31 @@ static int event_queue_start(Manager *manager) {
return log_warning_errno(r, "Failed to read udev rules: %m"); return log_warning_errno(r, "Failed to read udev rules: %m");
} }
/* fork with up-to-date SELinux label database, so the child inherits the up-to-date db
* and, until the next SELinux policy changes, we safe further reloads in future children */
mac_selinux_maybe_reload();
LIST_FOREACH(event, event, manager->events) { LIST_FOREACH(event, event, manager->events) {
if (event->state != EVENT_QUEUED) if (event->state != EVENT_QUEUED)
continue; continue;
/* do not start event if parent or child event is still running or queued */ /* do not start event if parent or child event is still running or queued */
r = event_is_blocked(event); r = event_is_blocked(event);
if (r > 0)
continue;
if (r < 0) { if (r < 0) {
sd_device_action_t a = _SD_DEVICE_ACTION_INVALID; sd_device_action_t a = _SD_DEVICE_ACTION_INVALID;
(void) sd_device_get_action(event->dev, &a); (void) sd_device_get_action(event->dev, &a);
log_device_warning_errno(event->dev, r, log_device_warning_errno(event->dev, r,
"Failed to check event dependency, " "Failed to check dependencies for event (SEQNUM=%"PRIu64", ACTION=%s), "
"skipping event (SEQNUM=%"PRIu64", ACTION=%s)", "assuming there is no blocking event, ignoring: %m",
event->seqnum, event->seqnum,
strna(device_action_to_string(a))); strna(device_action_to_string(a)));
event_free(event);
return r;
} }
if (r > 0)
continue;
r = event_run(event); r = event_run(event);
if (r <= 0) if (r <= 0) /* 0 means there are no idle workers. Let's escape from the loop. */
return r; return r;
} }
@ -1448,10 +1441,11 @@ static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, voi
device_tag_index(worker->event->dev, NULL, false); device_tag_index(worker->event->dev, NULL, false);
if (manager->monitor) { if (manager->monitor) {
/* forward kernel event without amending it */ /* Forward kernel event unchanged */
r = device_monitor_send_device(manager->monitor, NULL, worker->event->dev_kernel); r = device_monitor_send_device(manager->monitor, NULL, worker->event->dev_kernel);
if (r < 0) if (r < 0)
log_device_error_errno(worker->event->dev_kernel, r, "Failed to send back device to kernel: %m"); log_device_warning_errno(worker->event->dev_kernel, r,
"Failed to broadcast failed event to libudev listeners, ignoring: %m");
} }
} }
@ -1479,7 +1473,13 @@ static int on_post(sd_event_source *s, void *userdata) {
if (!LIST_IS_EMPTY(manager->events)) if (!LIST_IS_EMPTY(manager->events))
return 1; return 1;
/* There are no pending events. Let's cleanup idle process. */ /* There are no queued events. Let's remove /run/udev/queue and clean up the idle processes. */
if (unlink("/run/udev/queue") < 0) {
if (errno != ENOENT)
log_warning_errno(errno, "Failed to unlink /run/udev/queue, ignoring: %m");
} else
log_debug("No events are queued, removing /run/udev/queue.");
if (!hashmap_isempty(manager->workers)) { if (!hashmap_isempty(manager->workers)) {
/* There are idle workers */ /* There are idle workers */

View File

@ -25,10 +25,8 @@ wait_on_state_or_fail () {
systemd-analyze log-level debug systemd-analyze log-level debug
# Trigger testservice-failure-exit-handler-68.service
cat >/run/systemd/system/testservice-failure-68.service <<EOF cat >/run/systemd/system/testservice-failure-68.service <<EOF
[Unit] [Unit]
Description=TEST-68-PROPAGATE-EXIT-STATUS with OnFailure= trigger
OnFailure=testservice-failure-exit-handler-68.service OnFailure=testservice-failure-exit-handler-68.service
[Service] [Service]
@ -37,17 +35,14 @@ EOF
cat >/run/systemd/system/testservice-failure-68-template.service <<EOF cat >/run/systemd/system/testservice-failure-68-template.service <<EOF
[Unit] [Unit]
Description=TEST-68-PROPAGATE-EXIT-STATUS with OnFailure= trigger (template)
OnFailure=testservice-failure-exit-handler-68-template@%n.service OnFailure=testservice-failure-exit-handler-68-template@%n.service
[Service] [Service]
ExecStart=sh -c "exit 1" ExecStart=sh -c "exit 1"
EOF EOF
# Trigger testservice-success-exit-handler-68.service
cat >/run/systemd/system/testservice-success-68.service <<EOF cat >/run/systemd/system/testservice-success-68.service <<EOF
[Unit] [Unit]
Description=TEST-68-PROPAGATE-EXIT-STATUS with OnSuccess= trigger
OnSuccess=testservice-success-exit-handler-68.service OnSuccess=testservice-success-exit-handler-68.service
[Service] [Service]
@ -56,7 +51,6 @@ EOF
cat >/run/systemd/system/testservice-success-68-template.service <<EOF cat >/run/systemd/system/testservice-success-68-template.service <<EOF
[Unit] [Unit]
Description=TEST-68-PROPAGATE-EXIT-STATUS with OnSuccess= trigger (template)
OnSuccess=testservice-success-exit-handler-68-template@%n.service OnSuccess=testservice-success-exit-handler-68-template@%n.service
[Service] [Service]
@ -102,21 +96,17 @@ exit 0
EOF EOF
chmod +x /tmp/check_on_success.sh chmod +x /tmp/check_on_success.sh
# Handle testservice-failure-exit-handler-68.service exiting with success.
cat >/run/systemd/system/testservice-success-exit-handler-68.service <<EOF cat >/run/systemd/system/testservice-success-exit-handler-68.service <<EOF
[Unit]
Description=TEST-68-PROPAGATE-EXIT-STATUS handle service exiting in success
[Service] [Service]
ExecStartPre=/tmp/check_on_success.sh ExecStartPre=/tmp/check_on_success.sh
ExecStart=/tmp/check_on_success.sh ExecStart=/tmp/check_on_success.sh
EOF EOF
cp /run/systemd/system/testservice-success-exit-handler-68.service \
/run/systemd/system/testservice-transient-success-exit-handler-68.service
# Template version. # Template version.
cat >/run/systemd/system/testservice-success-exit-handler-68-template@.service <<EOF cat >/run/systemd/system/testservice-success-exit-handler-68-template@.service <<EOF
[Unit]
Description=TEST-68-PROPAGATE-EXIT-STATUS handle service exiting in success (template)
[Service] [Service]
ExecStartPre=echo "triggered by %i" ExecStartPre=echo "triggered by %i"
ExecStartPre=/tmp/check_on_success.sh ExecStartPre=/tmp/check_on_success.sh
@ -163,11 +153,7 @@ EOF
chmod +x /tmp/check_on_failure.sh chmod +x /tmp/check_on_failure.sh
# Handle testservice-failure-exit-handler-68.service exiting with failure.
cat >/run/systemd/system/testservice-failure-exit-handler-68.service <<EOF cat >/run/systemd/system/testservice-failure-exit-handler-68.service <<EOF
[Unit]
Description=TEST-68-PROPAGATE-EXIT-STATUS handle service exiting in failure
[Service] [Service]
# repeat the check to make sure that values are set correctly on repeated invocations # repeat the check to make sure that values are set correctly on repeated invocations
Type=oneshot Type=oneshot
@ -178,11 +164,11 @@ ExecStart=/tmp/check_on_failure.sh
ExecStartPost=test -z '$MONITOR_SERVICE_RESULT' ExecStartPost=test -z '$MONITOR_SERVICE_RESULT'
EOF EOF
cp /run/systemd/system/testservice-failure-exit-handler-68.service \
/run/systemd/system/testservice-transient-failure-exit-handler-68.service
# Template version. # Template version.
cat >/run/systemd/system/testservice-failure-exit-handler-68-template@.service <<EOF cat >/run/systemd/system/testservice-failure-exit-handler-68-template@.service <<EOF
[Unit]
Description=TEST-68-PROPAGATE-EXIT-STATUS handle service exiting in failure (template)
[Service] [Service]
Type=oneshot Type=oneshot
ExecStartPre=echo "triggered by %i" ExecStartPre=echo "triggered by %i"
@ -203,23 +189,19 @@ wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "
systemctl start testservice-success-68.service systemctl start testservice-success-68.service
wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10" wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
# Let's get rid of the failed units so the tests below don't fail, and daemon-reload
# to force garbace collection of the units.
systemctl reset-failed
systemctl daemon-reload
# Test some transient units since these exit very quickly. # Test some transient units since these exit very quickly.
: "-------III--------------------------------------------------" : "-------III--------------------------------------------------"
systemd-run --unit=testservice-transient-success-68 --property=OnSuccess=testservice-success-exit-handler-68.service sh -c "exit 0" systemd-run --unit=testservice-transient-success-68 \
--property=OnSuccess=testservice-transient-success-exit-handler-68.service \
sh -c "exit 0"
wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10" wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
: "-------IIII-------------------------------------------------" : "-------IIII-------------------------------------------------"
systemd-run --unit=testservice-transient-failure-68 --property=OnFailure=testservice-failure-exit-handler-68.service sh -c "exit 1" systemd-run --unit=testservice-transient-failure-68 \
--property=OnFailure=testservice-transient-failure-exit-handler-68.service \
sh -c "exit 1"
wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10" wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
systemctl reset-failed
systemctl daemon-reload
# Test template handlers too # Test template handlers too
: "-------V---------------------------------------------------" : "-------V---------------------------------------------------"
systemctl start testservice-success-68-template.service systemctl start testservice-success-68-template.service