Compare commits

..

1 Commits

Author SHA1 Message Date
Philip Meulengracht 1f8f97c438
Merge 684f4d25c8 into 7b9dc72c3c 2024-09-13 12:36:59 +02:00
82 changed files with 895 additions and 1694 deletions

13
NEWS
View File

@ -2,15 +2,6 @@ systemd System and Service Manager
CHANGES WITH 257 in spe:
Incompatible changes:
* The --purge switch of systemd-tmpfiles (which was added in v256) has
been reworked: it will now only apply to tmpfiles.d/ lines marked
with the new "$" flag. This is an incompatible change, and means any
tmpfiles.d/ files which shall be used together with --purge need to
be updated accordingly. This change has been made to make it harder
to accidentally delete too many files when using --purge incorrectly.
Announcements of Future Feature Removals and Incompatible Changes:
* Support for automatic flushing of the nscd user/group database caches
@ -94,7 +85,7 @@ CHANGES WITH 257 in spe:
/usr/lib/clock-epoch, and /var/lib/systemd/timesync/clock. See
systemd(1) for an detailed updated description.
* Ctrl-Alt-Delete is re-enabled during late shutdown, so that the user
* Ctrl-Alt-Delete is reenabled during late shutdown, so that the user
can still initiate a reboot if the system freezes.
* Unit option PrivateUsers=identity can be used to request a user
@ -211,7 +202,7 @@ CHANGES WITH 257 in spe:
versions.
* systemd-sysupdate gained a new --transfer-source= option to set the
directory to which transfer sources configured with
directory to which transfer sources cofigured with
PathRelativeTo=explicit will be interpreted.
Miscellaneous:

12
TODO
View File

@ -130,10 +130,6 @@ Deprecations and removals:
Features:
* find a nice way to opt-in into auto-masking SIGCHLD on first
sd_event_add_child(), and then get rid of many more explicit sigprocmask()
calls.
* maybe set shell.prompt.prefix credential in run0 to some warning emoji,
i.e. ⚠️ or ☢️ or ⚡ or 👊 or 🧑‍🔧 or so.
@ -999,6 +995,12 @@ Features:
* in the initrd, once the rootfs encryption key has been measured to PCR 15,
derive default machine ID to use from it, and pass it to host PID 1.
* tree-wide: convert as much as possible over to use sd_event_set_signal_exit(), instead
of manually hooking into SIGINT/SIGTERM
* tree-wide: convert as much as possible over to SD_EVENT_SIGNAL_PROCMASK
instead of manual blocking.
* sd-boot: for each installed OS, grey out older entries (i.e. all but the
newest), to indicate they are obsolete
@ -1499,8 +1501,6 @@ Features:
* systemd-analyze netif that explains predictable interface (or networkctl)
* systemd-analyze inspect-elf should show other notes too, at least build-id.
* Figure out naming of verbs in systemd-analyze: we have (singular) capability,
exit-status, but (plural) filesystems, architectures.

View File

@ -87,90 +87,3 @@ of the libraries they specify in order to be enabled.
| required | Core functionality needs the dependency, the binary will not work if it cannot be found |
| recommended | Important functionality needs the dependency, the binary will work but in most cases the dependency should be provided |
| suggested | Secondary functionality needs the dependency, the binary will work and the dependency is only needed for full-featured installations |
### Displaying `dlopen()` notes
The raw ELF section can be extracted using `objdump`:
```console
$ objdump -j .note.dlopen -s /usr/lib64/systemd/libsystemd-shared-257.so
/usr/lib64/systemd/libsystemd-shared-257.so: file format elf64-x86-64
Contents of section .note.dlopen:
0334 04000000 8e000000 0a0c7c40 46444f00 ..........|@FDO.
0344 5b7b2266 65617475 7265223a 22627066 [{"feature":"bpf
0354 222c2264 65736372 69707469 6f6e223a ","description":
0364 22537570 706f7274 20666972 6577616c "Support firewal
0374 6c696e67 20616e64 2073616e 64626f78 ling and sandbox
0384 696e6720 77697468 20425046 222c2270 ing with BPF","p
0394 72696f72 69747922 3a227375 67676573 riority":"sugges
03a4 74656422 2c22736f 6e616d65 223a5b22 ted","soname":["
03b4 6c696262 70662e73 6f2e3122 2c226c69 libbpf.so.1","li
03c4 62627066 2e736f2e 30225d7d 5d000000 bbpf.so.0"]}]...
03d4 04000000 9e000000 0a0c7c40 46444f00 ..........|@FDO.
...
```
It is more convenient to use a higher level tool:
```console
$ dlopen-notes /usr/lib64/systemd/libsystemd-shared-257.so
# /usr/lib64/systemd/libsystemd-shared-257.so
[
{
"feature": "archive",
"description": "Support for decompressing archive files",
"priority": "suggested",
"soname": [
"libarchive.so.13"
]
},
{
"feature": "bpf",
"description": "Support firewalling and sandboxing with BPF",
"priority": "suggested",
"soname": [
"libbpf.so.1",
"libbpf.so.0"
]
},
...
```
`dlopen-notes` can display the notes grouped in a few different ways.
One option is to filter the libraries by "feature". This answers the
question "what libraries are needed to provide specified features":
```console
$ dlopen-notes.py -f archive,bpf /usr/lib64/systemd/libsystemd-shared-257.so
# grouped by feature
{
"bpf": {
"description": "Support firewalling and sandboxing with BPF",
"sonames": {
"libbpf.so.1": "suggested",
"libbpf.so.0": "suggested"
}
},
"archive": {
"description": "Support for decompressing archive files",
"sonames": {
"libarchive.so.13": "suggested"
}
}
}
The format that is used when building `deb` packages:
```console
$ dlopen-notes -s /usr/lib64/systemd/libsystemd-shared-257.so
libarchive.so.13 suggested
libbpf.so.0 suggested
libbpf.so.1 suggested
...
```
The format that can be useful when building `rpm` packages:
```console
$ dlopen-notes --rpm-requires archive --rpm-recommends bpf /usr/lib64/systemd/libsystemd-shared-257.so
Requires: libarchive.so.13()(64bit)
Recommends: libbpf.so.1()(64bit)
```

View File

@ -103,97 +103,3 @@ A set of well-known keys is defined here, and hopefully shared among all vendors
| architecture | The binary package architecture | arm32 |
| osCpe | A CPE name for the OS, typically corresponding to CPE_NAME in os-release | cpe:/o:fedoraproject:fedora:33 |
| debugInfoUrl | The debuginfod server url, if available | https://debuginfod.fedoraproject.org/ |
### Displaying package notes
The raw ELF section can be extracted using `objdump`:
```console
$ objdump -j .note.package -s /usr/bin/ls
/usr/bin/ls: file format elf64-x86-64
Contents of section .note.package:
03cc 04000000 7c000000 7e1afeca 46444f00 ....|...~...FDO.
03dc 7b227479 7065223a 2272706d 222c226e {"type":"rpm","n
03ec 616d6522 3a22636f 72657574 696c7322 ame":"coreutils"
03fc 2c227665 7273696f 6e223a22 392e342d ,"version":"9.4-
040c 372e6663 3430222c 22617263 68697465 7.fc40","archite
041c 63747572 65223a22 7838365f 3634222c cture":"x86_64",
042c 226f7343 7065223a 22637065 3a2f6f3a "osCpe":"cpe:/o:
043c 6665646f 72617072 6f6a6563 743a6665 fedoraproject:fe
044c 646f7261 3a343022 7d000000 dora:40"}...
```
It is more convenient to use a higher level tool:
```console
$ readelf --notes /usr/bin/ls
...
Displaying notes found in: .note.gnu.build-id
Owner Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: 40e5a1570a9d97fc48f5c61cfb7690fec0f872b2
Displaying notes found in: .note.ABI-tag
Owner Data size Description
GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 3.2.0
Displaying notes found in: .note.package
Owner Data size Description
FDO 0x0000007c FDO_PACKAGING_METADATA
Packaging Metadata: {"type":"rpm","name":"coreutils","version":"9.4-7.fc40","architecture":"x86_64","osCpe":"cpe:/o:fedoraproject:fedora:40"}
...
$ systemd-analyze inspect-elf /usr/bin/ls
path: /usr/bin/ls
elfType: executable
elfArchitecture: AMD x86-64
type: rpm
name: coreutils
version: 9.4-7.fc40
architecture: x86_64
osCpe: cpe:/o:fedoraproject:fedora:40
buildId: 40e5a1570a9d97fc48f5c61cfb7690fec0f872b2
```
If the binary crashes, `systemd-coredump` will display the combined information
from the crashing binary and any shared libraries it links to:
```console
$ coredumpctl info
PID: 3987823 (ls)
Signal: 11 (SEGV)
Command Line: ls --color=tty -lR /
Executable: /usr/bin/ls
...
Storage: /var/lib/systemd/coredump/core.ls.1000.88dea1b9831c420dbb398f9d2ad9b41e.3987823.1726230641000000.zst (present)
Size on Disk: 194.4K
Package: coreutils/9.4-7.fc40
build-id: 40e5a1570a9d97fc48f5c61cfb7690fec0f872b2
Message: Process 3987823 (ls) of user 1000 dumped core.
Module /usr/bin/ls from rpm coreutils-9.4-7.fc40.x86_64
Module libz.so.1 from rpm zlib-ng-2.1.7-1.fc40.x86_64
Module libcrypto.so.3 from rpm openssl-3.2.2-3.fc40.x86_64
Module libmount.so.1 from rpm util-linux-2.40.1-1.fc40.x86_64
Module libcrypt.so.2 from rpm libxcrypt-4.4.36-5.fc40.x86_64
Module libblkid.so.1 from rpm util-linux-2.40.1-1.fc40.x86_64
Module libnss_sss.so.2 from rpm sssd-2.9.5-1.fc40.x86_64
Module libpcre2-8.so.0 from rpm pcre2-10.44-1.fc40.x86_64
Module libcap.so.2 from rpm libcap-2.69-8.fc40.x86_64
Module libselinux.so.1 from rpm libselinux-3.6-4.fc40.x86_64
Stack trace of thread 3987823:
#0 0x00007f19331c3f7e lgetxattr (libc.so.6 + 0x116f7e)
#1 0x00007f19332be4c0 lgetfilecon_raw (libselinux.so.1 + 0x134c0)
#2 0x00007f19332c3bd9 lgetfilecon (libselinux.so.1 + 0x18bd9)
#3 0x000056038273ad55 gobble_file.constprop.0 (/usr/bin/ls + 0x17d55)
#4 0x0000560382733c55 print_dir (/usr/bin/ls + 0x10c55)
#5 0x0000560382727c35 main (/usr/bin/ls + 0x4c35)
#6 0x00007f19330d7088 __libc_start_call_main (libc.so.6 + 0x2a088)
#7 0x00007f19330d714b __libc_start_main@@GLIBC_2.34 (libc.so.6 + 0x2a14b)
#8 0x0000560382728f15 _start (/usr/bin/ls + 0x5f15)
ELF object binary architecture: AMD x86-64
```
(This is just a simulation. `ls` is not prone to crashing with a segmentation violation.)

View File

@ -299,10 +299,6 @@ sensor:modalias:acpi:KIOX000A*:dmi:*:svnCHUWIInnovationAndTechnology*:pnHi10X:*
sensor:modalias:acpi:MXC6655*:dmi:*:svnCHUWIINNOVATIONLIMITED:pnHi10Go:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0,-1, 0; 0, 0, 1
# Chuwi Hi10 Max
sensor:modalias:acpi:MXC6655*:dmi:*:svnCHUWIInnovationAndTechnology*:pnHi10Max:*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
# Chuwi Hi12
sensor:modalias:acpi:BOSC0200*:dmi:*:svnHampoo:pnP02BD6_HI-122LP:*
sensor:modalias:acpi:BOSC0200*:dmi:*:svnDefaultstring:pnDefaultstring:*
@ -607,15 +603,6 @@ sensor:modalias:i2c:bmc150_accel:dmi:*:svnHewlett-Packard:pnHPPavilionx2Detachab
sensor:modalias:i2c:bmc150_accel:dmi:*:svnHewlett-Packard:pnHPProTablet408:*:rn8048:*
ACCEL_MOUNT_MATRIX=0, 1, 0; -1, 0, 0; 0, 0, 1
#########################################
# HUAWEI
#########################################
# HUAWEI MateBook D 15 AMD
sensor:modalias:acpi:SMO8840*:dmi:*:svnHUAWEI:pnBOHK-WAX9X:*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
ACCEL_LOCATION=base
#########################################
# I.T.Works
#########################################

View File

@ -310,10 +310,6 @@ mouse:bluetooth:v047dp8019:name:Expert Wireless TB Mouse:*
ID_INPUT_TRACKBALL=1
MOUSE_DPI=400@125
# Kensington SlimBlade Pro trackball (via Bluetooth)
mouse:bluetooth:v047dp80d4:name:SlimBlade Pro:*
ID_INPUT_TRACKBALL=1
##########################################
# Lenovo
##########################################

View File

@ -267,8 +267,7 @@
<term><option>kernel-identify</option> <replaceable>kernel</replaceable></term>
<listitem><para>Takes a kernel image as argument. Checks what kind of kernel the image is. Returns
one of <literal>uki</literal>, <literal>addon</literal>, <literal>pe</literal>, and
<literal>unknown</literal>.
one of <literal>uki</literal>, <literal>pe</literal>, and <literal>unknown</literal>.
</para>
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
@ -361,24 +360,6 @@
<xi:include href="version-info.xml" xpointer="v242"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--print-loader-path</option></term>
<listitem><para>This option modifies the behaviour of <command>status</command>: it shows the
absolute path to the boot loader EFI binary used for the current boot if this information is
available. Note that no attempt is made to verify whether the binary still exists.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--print-stub-path</option></term>
<listitem><para>This option modifies the behaviour of <command>status</command>: it shows the
absolute path to the UKI/stub EFI binary used for the current boot if this information is
available. Note that no attempt is made to verify whether the binary still exists.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<varlistentry>
<term><option>-R</option></term>
<term><option>--print-root-device</option></term>

View File

@ -46,10 +46,11 @@
the root file system, which is then responsible for probing all remaining hardware, mounting all
necessary file systems and spawning all configured services.</para>
<para>On shutdown, the system manager stops all services, unmounts all non-busy file systems (detaching
the storage technologies backing them), and then (optionally) jumps into the exitrd, which is backed by
tmpfs, and unmounts/detaches the remaining file systems, including the real root. As a last step,
the system is powered down.</para>
<para>On shutdown, the system manager stops all services, unmounts
all file systems (detaching the storage technologies backing
them), and then (optionally) jumps back into the initrd code which
unmounts/detaches the root file system and the storage it resides
on. As a last step, the system is powered down.</para>
<para>Additional information about the system boot process may be
found in

View File

@ -115,7 +115,7 @@
result can be pre-calculated without too much effort. The <literal>.pcrsig</literal> section is not
included in this PCR measurement, since it is supposed to contain signatures for the output of the
measurement operation, and thus cannot also be input to it. If an UKI contains multiple profiles, only
the PE sections of the selected profile (and those of the base profile, except if overridden) are
the PE sections of the selected profile (and those of the base profile, except if overriden) are
measured.</para>
<para>If non-zero, the selected numeric profile is measured into PCR 12.</para>

View File

@ -152,11 +152,10 @@
<varlistentry>
<term><option>--purge</option></term>
<listitem><para>If this option is passed, all files and directories declared for
<emphasis>creation</emphasis> and marked with the <literal>$</literal> character by the
<filename>tmpfiles.d/</filename> files specified on the command line will be
<emphasis>deleted</emphasis>. Specifically, this acts on all files and directories marked with
<varname>f</varname>, <varname>F</varname>, <varname>d</varname>, <varname>D</varname>,
<listitem><para>If this option is passed, all files and directories marked for
<emphasis>creation</emphasis> by the <filename>tmpfiles.d/</filename> files specified on the command
line will be <emphasis>deleted</emphasis>. Specifically, this acts on all files and directories
marked with <varname>f</varname>, <varname>F</varname>, <varname>d</varname>, <varname>D</varname>,
<varname>v</varname>, <varname>q</varname>, <varname>Q</varname>, <varname>p</varname>,
<varname>L</varname>, <varname>c</varname>, <varname>b</varname>, <varname>C</varname>,
<varname>w</varname>, <varname>e</varname>. If this switch is used at least one

View File

@ -539,10 +539,6 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
service, the line is silently skipped. If <literal>^</literal> and <literal>~</literal> are combined
Base64 decoding is applied to the credential contents.</para>
<para>If the dollar sign (<literal>$</literal>) is used, the file becomes subject to removal when
<command>systemd-tmpfiles</command> is invoked with the <option>--purge</option> switch. Lines without
this character are unaffected by that switch.</para>
<para>Note that for all line types that result in creation of any kind of file node
(i.e. <varname>f</varname>,
<varname>d</varname>/<varname>D</varname>/<varname>v</varname>/<varname>q</varname>/<varname>Q</varname>,

View File

@ -141,12 +141,6 @@
For example, e"string\n" is parsed as 7 characters: 6 lowercase letters and a newline.
This can be useful for writing special characters when a kernel driver requires them.</para>
<para>The string can be prefixed with a lowercase i (i"string") to mark that the string or pattern
will match case-insensitively. For example, i"foo" will match
<literal>foo</literal>, <literal>FOO</literal>, <literal>FoO</literal> and so on. The prefix can be
used only for match (<literal>==</literal>) or unmatch (<literal>!=</literal>) rules, e.g.
<varname>ATTR{foo}==i"abcd"</varname>.</para>
<para>Please note that <constant>NUL</constant> is not allowed in either string variant.</para>
</refsect2>

View File

@ -58,6 +58,9 @@ OPTIONS=(
)
EOF
# Linting the PKGBUILD takes multiple seconds every build so avoid that by nuking all the linting functions.
rm /usr/share/makepkg/lint_pkgbuild/*
TS="${SOURCE_DATE_EPOCH:-$(date +%s)}"
sed --in-place "pkg/$PKG_SUBDIR/PKGBUILD" \
@ -65,11 +68,9 @@ sed --in-place "pkg/$PKG_SUBDIR/PKGBUILD" \
--expression "s/^pkgrel=.*/pkgrel=$(date "+%Y%m%d%H%M%S" --date "@$TS")/"
# We get around makepkg's root check by setting EUID to something else.
# Linting the PKGBUILD takes multiple seconds every build so avoid that by nuking all the linting functions.
# shellcheck disable=SC2046
env --chdir="pkg/$PKG_SUBDIR" \
EUID=123 \
MAKEPKG_LINT_PKGBUILD=0 \
makepkg \
--noextract \
--noprepare \

View File

@ -221,12 +221,6 @@ const char* const systemd_features =
" -BPF_FRAMEWORK"
#endif
#if HAVE_VMLINUX_H
" +BTF"
#else
" -BTF"
#endif
#if HAVE_XKBCOMMON
" +XKBCOMMON"
#else
@ -253,7 +247,7 @@ const char* const systemd_features =
;
static char* systemd_features_with_color(void) {
static char *systemd_features_with_color(void) {
const char *p = systemd_features;
_cleanup_free_ char *ret = NULL;
int r;

View File

@ -145,10 +145,8 @@ int efi_get_variable(
int efi_get_variable_string(const char *variable, char **ret) {
_cleanup_free_ void *s = NULL;
size_t ss = 0;
char *x;
int r;
assert(variable);
char *x;
r = efi_get_variable(variable, NULL, &s, &ss);
if (r < 0)
@ -158,27 +156,10 @@ int efi_get_variable_string(const char *variable, char **ret) {
if (!x)
return -ENOMEM;
if (ret)
*ret = x;
*ret = x;
return 0;
}
int efi_get_variable_path(const char *variable, char **ret) {
int r;
assert(variable);
r = efi_get_variable_string(variable, ret);
if (r < 0)
return r;
if (ret)
efi_tilt_backslashes(*ret);
return r;
}
static int efi_verify_variable(const char *variable, uint32_t attr, const void *value, size_t size) {
_cleanup_free_ void *buf = NULL;
size_t n;

View File

@ -11,7 +11,6 @@
#include "sd-id128.h"
#include "efivars-fundamental.h"
#include "string-util.h"
#include "time-util.h"
#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
@ -48,7 +47,6 @@
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 **ret);
int efi_get_variable_path(const char *variable, char **ret);
int efi_set_variable(const char *variable, const void *value, size_t size);
int efi_set_variable_string(const char *variable, const char *p);
@ -70,10 +68,6 @@ static inline int efi_get_variable_string(const char *variable, char **ret) {
return -EOPNOTSUPP;
}
static inline int efi_get_variable_path(const char *variable, char **ret) {
return -EOPNOTSUPP;
}
static inline int efi_set_variable(const char *variable, const void *value, size_t size) {
return -EOPNOTSUPP;
}
@ -106,7 +100,3 @@ static inline int systemd_efi_options_efivarfs_if_newer(char **line) {
return -ENODATA;
}
#endif
static inline char *efi_tilt_backslashes(char *s) {
return string_replace_char(s, '\\', '/');
}

View File

@ -28,7 +28,7 @@ int sigaction_many_internal(const struct sigaction *sa, ...);
int sigset_add_many_internal(sigset_t *ss, ...);
#define sigset_add_many(...) sigset_add_many_internal(__VA_ARGS__, -1)
int sigprocmask_many_internal(int how, sigset_t *ret_old_mask, ...);
int sigprocmask_many_internal(int how, sigset_t *old, ...);
#define sigprocmask_many(...) sigprocmask_many_internal(__VA_ARGS__, -1)
const char* signal_to_string(int i) _const_;

View File

@ -219,12 +219,14 @@ static int acquire_boot_count_path(
uint64_t left, done;
int r;
r = efi_get_variable_path(EFI_LOADER_VARIABLE(LoaderBootCountPath), &path);
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderBootCountPath), &path);
if (r == -ENOENT)
return -EUNATCH; /* in this case, let the caller print a message */
if (r < 0)
return log_error_errno(r, "Failed to read LoaderBootCountPath EFI variable: %m");
efi_tilt_backslashes(path);
if (!path_is_normalized(path))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Path read from LoaderBootCountPath is not normalized, refusing: %s",

View File

@ -298,24 +298,12 @@ fail:
return r;
}
static int efi_get_variable_string_and_warn(const char *variable, char **ret) {
static void read_efi_var(const char *variable, char **ret) {
int r;
r = efi_get_variable_string(variable, ret);
if (r < 0 && r != -ENOENT)
return log_warning_errno(r, "Failed to read EFI variable '%s', ignoring: %m", variable);
return r;
}
static int efi_get_variable_path_and_warn(const char *variable, char **ret) {
int r;
r = efi_get_variable_path(variable, ret);
if (r < 0 && r != -ENOENT)
return log_warning_errno(r, "Failed to read EFI variable '%s', ignoring: %m", variable);
return r;
log_warning_errno(r, "Failed to read EFI variable %s: %m", variable);
}
static void print_yes_no_line(bool first, bool good, const char *name) {
@ -408,23 +396,26 @@ int verb_status(int argc, char *argv[], void *userdata) {
{ EFI_STUB_FEATURE_MULTI_PROFILE_UKI, "Stub understands profile selector" },
{ EFI_STUB_FEATURE_REPORT_STUB_PARTITION, "Stub sets stub partition information" },
};
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL, *stub_path = NULL,
*current_entry = NULL, *oneshot_entry = NULL, *default_entry = NULL;
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
sd_id128_t loader_part_uuid = SD_ID128_NULL;
uint64_t loader_features = 0, stub_features = 0;
Tpm2Support s;
int have;
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderFirmwareType), &fw_type);
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderFirmwareInfo), &fw_info);
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderInfo), &loader);
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(StubInfo), &stub);
(void) efi_get_variable_path_and_warn(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &loader_path);
(void) efi_get_variable_path_and_warn(EFI_LOADER_VARIABLE(StubImageIdentifier), &stub_path);
read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareType), &fw_type);
read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareInfo), &fw_info);
read_efi_var(EFI_LOADER_VARIABLE(LoaderInfo), &loader);
read_efi_var(EFI_LOADER_VARIABLE(StubInfo), &stub);
read_efi_var(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &loader_path);
(void) efi_loader_get_features(&loader_features);
(void) efi_stub_get_features(&stub_features);
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderEntrySelected), &current_entry);
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderEntryOneShot), &oneshot_entry);
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderEntryDefault), &default_entry);
if (loader_path)
efi_tilt_backslashes(loader_path);
k = efi_loader_get_device_part_uuid(&loader_part_uuid);
if (k < 0 && k != -ENOENT)
r = log_warning_errno(k, "Failed to read EFI variable LoaderDevicePartUUID: %m");
SecureBootMode secure = efi_get_secure_boot_mode();
printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
@ -472,58 +463,34 @@ int verb_status(int argc, char *argv[], void *userdata) {
}
printf("\n");
if (loader) {
printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
printf(" Product: %s%s%s\n", ansi_highlight(), loader, ansi_normal());
for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++)
print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name);
printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader), ansi_normal());
sd_id128_t loader_partition_uuid;
bool have_loader_partition_uuid = efi_loader_get_device_part_uuid(&loader_partition_uuid) >= 0;
for (size_t i = 0; i < ELEMENTSOF(loader_flags); i++)
print_yes_no_line(i == 0, FLAGS_SET(loader_features, loader_flags[i].flag), loader_flags[i].name);
print_yes_no_line(false, have_loader_partition_uuid, "Boot loader set ESP information");
sd_id128_t bootloader_esp_uuid;
bool have_bootloader_esp_uuid = efi_loader_get_device_part_uuid(&bootloader_esp_uuid) >= 0;
if (current_entry)
printf("Current Entry: %s\n", current_entry);
if (default_entry)
printf("Default Entry: %s\n", default_entry);
if (oneshot_entry && !streq_ptr(oneshot_entry, default_entry))
printf("OneShot Entry: %s\n", oneshot_entry);
if (have_loader_partition_uuid && !sd_id128_is_null(esp_uuid) && !sd_id128_equal(esp_uuid, loader_partition_uuid))
printf("WARNING: The boot loader reports a different partition UUID than the detected ESP ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR")!\n",
SD_ID128_FORMAT_VAL(loader_partition_uuid), SD_ID128_FORMAT_VAL(esp_uuid));
if (!sd_id128_is_null(loader_partition_uuid))
printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
SD_ID128_FORMAT_VAL(loader_partition_uuid));
else
printf(" Partition: n/a\n");
printf(" Loader: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
printf("\n");
}
print_yes_no_line(false, have_bootloader_esp_uuid, "Boot loader sets ESP information");
if (have_bootloader_esp_uuid && !sd_id128_is_null(esp_uuid) &&
!sd_id128_equal(esp_uuid, bootloader_esp_uuid))
printf("WARNING: The boot loader reports a different ESP UUID than detected ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR")!\n",
SD_ID128_FORMAT_VAL(bootloader_esp_uuid),
SD_ID128_FORMAT_VAL(esp_uuid));
if (stub) {
printf("%sCurrent Stub:%s\n", ansi_underline(), ansi_normal());
printf(" Product: %s%s%s\n", ansi_highlight(), stub, ansi_normal());
printf(" Stub: %s\n", stub);
for (size_t i = 0; i < ELEMENTSOF(stub_flags); i++)
print_yes_no_line(i == 0, FLAGS_SET(stub_features, stub_flags[i].flag), stub_flags[i].name);
sd_id128_t stub_partition_uuid;
bool have_stub_partition_uuid = efi_stub_get_device_part_uuid(&stub_partition_uuid) >= 0;
if (have_stub_partition_uuid && (!(!sd_id128_is_null(esp_uuid) && sd_id128_equal(esp_uuid, stub_partition_uuid)) &&
!(!sd_id128_is_null(xbootldr_uuid) && sd_id128_equal(xbootldr_uuid, stub_partition_uuid))))
printf("WARNING: The stub loader reports a different UUID than the detected ESP or XBOOTDLR partition ("SD_ID128_UUID_FORMAT_STR" vs. "SD_ID128_UUID_FORMAT_STR"/"SD_ID128_UUID_FORMAT_STR")!\n",
SD_ID128_FORMAT_VAL(stub_partition_uuid), SD_ID128_FORMAT_VAL(esp_uuid), SD_ID128_FORMAT_VAL(xbootldr_uuid));
if (!sd_id128_is_null(stub_partition_uuid))
printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
SD_ID128_FORMAT_VAL(stub_partition_uuid));
else
printf(" Partition: n/a\n");
printf(" Stub: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(stub_path));
printf("\n");
}
if (!sd_id128_is_null(loader_part_uuid))
printf(" ESP: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR "\n",
SD_ID128_FORMAT_VAL(loader_part_uuid));
else
printf(" ESP: n/a\n");
printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
printf("\n");
printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), F_OK) >= 0;

View File

@ -16,14 +16,12 @@
#include "build.h"
#include "devnum-util.h"
#include "dissect-image.h"
#include "efi-loader.h"
#include "escape.h"
#include "find-esp.h"
#include "main-func.h"
#include "mount-util.h"
#include "pager.h"
#include "parse-argument.h"
#include "path-util.h"
#include "pretty-print.h"
#include "utf8.h"
#include "varlink-io.systemd.BootControl.h"
@ -40,8 +38,6 @@ char *arg_esp_path = NULL;
char *arg_xbootldr_path = NULL;
bool arg_print_esp_path = false;
bool arg_print_dollar_boot_path = false;
bool arg_print_loader_path = false;
bool arg_print_stub_path = false;
unsigned arg_print_root_device = 0;
bool arg_touch_variables = true;
bool arg_install_random_seed = true;
@ -137,71 +133,6 @@ int acquire_xbootldr(
return 1;
}
static int print_loader_or_stub_path(void) {
_cleanup_free_ char *p = NULL;
sd_id128_t uuid;
int r;
if (arg_print_loader_path) {
r = efi_loader_get_device_part_uuid(&uuid);
if (r == -ENOENT)
return log_error_errno(r, "No loader partition UUID passed.");
if (r < 0)
return log_error_errno(r, "Unable to determine loader partition UUID: %m");
r = efi_get_variable_path(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &p);
if (r == -ENOENT)
return log_error_errno(r, "No loader EFI binary path passed.");
if (r < 0)
return log_error_errno(r, "Unable to determine loader EFI binary path: %m");
} else {
assert(arg_print_stub_path);
r = efi_stub_get_device_part_uuid(&uuid);
if (r == -ENOENT)
return log_error_errno(r, "No stub partition UUID passed.");
if (r < 0)
return log_error_errno(r, "Unable to determine stub partition UUID: %m");
r = efi_get_variable_path(EFI_LOADER_VARIABLE(StubImageIdentifier), &p);
if (r == -ENOENT)
return log_error_errno(r, "No stub EFI binary path passed.");
if (r < 0)
return log_error_errno(r, "Unable to determine stub EFI binary path: %m");
}
sd_id128_t esp_uuid;
r = acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false,
/* ret_part= */ NULL, /* ret_pstart= */ NULL, /* ret_psize= */ NULL,
&esp_uuid, /* ret_devid= */ NULL);
if (r < 0)
return r;
const char *found_path = NULL;
if (sd_id128_equal(esp_uuid, uuid))
found_path = arg_esp_path;
else if (arg_print_stub_path) { /* In case of the stub, also look for things in the xbootldr partition */
sd_id128_t xbootldr_uuid;
r = acquire_xbootldr(/* unprivileged_mode= */ false, &xbootldr_uuid, /* ret_devid= */ NULL);
if (r < 0)
return r;
if (sd_id128_equal(xbootldr_uuid, uuid))
found_path = arg_xbootldr_path;
}
if (!found_path)
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Failed to discover partition " SD_ID128_FORMAT_STR " among mounted boot partitions.", SD_ID128_FORMAT_VAL(uuid));
_cleanup_free_ char *j = path_join(found_path, p);
if (!j)
return log_oom();
puts(j);
return 0;
}
static int help(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *link = NULL;
int r;
@ -251,9 +182,6 @@ static int help(int argc, char *argv[], void *userdata) {
" Where to pick files when using --root=/--image=\n"
" -p --print-esp-path Print path to the EFI System Partition mount point\n"
" -x --print-boot-path Print path to the $BOOT partition mount point\n"
" --print-loader-path\n"
" Print path to currently booted boot loader binary\n"
" --print-stub-path Print path to currently booted unified kernel binary\n"
" -R --print-root-device\n"
" Print path to the block device node backing the\n"
" root file system (returns e.g. /dev/nvme0n1p5)\n"
@ -307,8 +235,6 @@ static int parse_argv(int argc, char *argv[]) {
ARG_ARCH_ALL,
ARG_EFI_BOOT_OPTION_DESCRIPTION,
ARG_DRY_RUN,
ARG_PRINT_LOADER_PATH,
ARG_PRINT_STUB_PATH,
};
static const struct option options[] = {
@ -324,8 +250,6 @@ static int parse_argv(int argc, char *argv[]) {
{ "print-esp-path", no_argument, NULL, 'p' },
{ "print-path", no_argument, NULL, 'p' }, /* Compatibility alias */
{ "print-boot-path", no_argument, NULL, 'x' },
{ "print-loader-path", no_argument, NULL, ARG_PRINT_LOADER_PATH },
{ "print-stub-path", no_argument, NULL, ARG_PRINT_STUB_PATH },
{ "print-root-device", no_argument, NULL, 'R' },
{ "no-variables", no_argument, NULL, ARG_NO_VARIABLES },
{ "random-seed", required_argument, NULL, ARG_RANDOM_SEED },
@ -408,14 +332,6 @@ static int parse_argv(int argc, char *argv[]) {
arg_print_dollar_boot_path = true;
break;
case ARG_PRINT_LOADER_PATH:
arg_print_loader_path = true;
break;
case ARG_PRINT_STUB_PATH:
arg_print_stub_path = true;
break;
case 'R':
arg_print_root_device++;
break;
@ -498,9 +414,9 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached();
}
if (!!arg_print_esp_path + !!arg_print_dollar_boot_path + (arg_print_root_device > 0) + arg_print_loader_path + arg_print_stub_path > 1)
if (!!arg_print_esp_path + !!arg_print_dollar_boot_path + (arg_print_root_device > 0) > 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--print-esp-path/-p, --print-boot-path/-x, --print-root-device=/-R, --print-loader-path, --print-stub-path cannot be combined.");
"--print-esp-path/-p, --print-boot-path/-x, --print-root-device=/-R cannot be combined.");
if ((arg_root || arg_image) && argv[optind] && !STR_IN_SET(argv[optind], "status", "list",
"install", "update", "remove", "is-installed", "random-seed", "unlink", "cleanup"))
@ -625,9 +541,6 @@ static int run(int argc, char *argv[]) {
return 0;
}
if (arg_print_loader_path || arg_print_stub_path)
return print_loader_or_stub_path();
/* Open up and mount the image */
if (arg_image) {
assert(!arg_root);

View File

@ -13,7 +13,6 @@
#include "initrd.h"
#include "linux.h"
#include "measure.h"
#include "memory-util-fundamental.h"
#include "part-discovery.h"
#include "pe.h"
#include "proto/block-io.h"
@ -2421,18 +2420,18 @@ static EFI_STATUS initrd_prepare(
EFI_FILE *root,
const BootEntry *entry,
char16_t **ret_options,
Pages *ret_initrd_pages,
void **ret_initrd,
size_t *ret_initrd_size) {
assert(root);
assert(entry);
assert(ret_options);
assert(ret_initrd_pages);
assert(ret_initrd);
assert(ret_initrd_size);
if (entry->type != LOADER_LINUX || !entry->initrd) {
*ret_options = NULL;
*ret_initrd_pages = (Pages) {};
*ret_initrd = NULL;
*ret_initrd_size = 0;
return EFI_SUCCESS;
}
@ -2446,6 +2445,7 @@ static EFI_STATUS initrd_prepare(
EFI_STATUS err;
size_t size = 0;
_cleanup_free_ uint8_t *initrd = NULL;
STRV_FOREACH(i, entry->initrd) {
_cleanup_free_ char16_t *o = options;
@ -2464,58 +2464,30 @@ static EFI_STATUS initrd_prepare(
if (err != EFI_SUCCESS)
return err;
if (!ADD_SAFE(&size, size, ALIGN4(info->FileSize)))
return EFI_OUT_OF_RESOURCES;
}
_cleanup_pages_ Pages pages = xmalloc_pages(
AllocateMaxAddress,
EfiLoaderData,
EFI_SIZE_TO_PAGES(size),
UINT32_MAX /* Below 4G boundary. */);
uint8_t *p = PHYSICAL_ADDRESS_TO_POINTER(pages.addr);
STRV_FOREACH(i, entry->initrd) {
_cleanup_(file_closep) EFI_FILE *handle = NULL;
err = root->Open(root, &handle, *i, EFI_FILE_MODE_READ, 0);
if (err != EFI_SUCCESS)
return err;
_cleanup_free_ EFI_FILE_INFO *info = NULL;
err = get_file_info(handle, &info, NULL);
if (err != EFI_SUCCESS)
return err;
if (info->FileSize == 0) /* Automatically skip over empty files */
continue;
size_t read_size = info->FileSize;
err = chunked_read(handle, &read_size, p);
size_t new_size, read_size = info->FileSize;
if (!ADD_SAFE(&new_size, size, read_size))
return EFI_OUT_OF_RESOURCES;
initrd = xrealloc(initrd, size, new_size);
err = chunked_read(handle, &read_size, initrd + size);
if (err != EFI_SUCCESS)
return err;
/* Make sure the actual read size is what we expected. */
assert(read_size == info->FileSize);
p += read_size;
size_t pad;
pad = ALIGN4(read_size) - read_size;
if (pad == 0)
continue;
memzero(p, pad);
p += pad;
assert(size + read_size == new_size);
size = new_size;
}
assert(PHYSICAL_ADDRESS_TO_POINTER(pages.addr + size) == p);
if (entry->options) {
_cleanup_free_ char16_t *o = options;
options = xasprintf("%ls %ls", o, entry->options);
}
*ret_options = TAKE_PTR(options);
*ret_initrd_pages = TAKE_STRUCT(pages);
*ret_initrd = TAKE_PTR(initrd);
*ret_initrd_size = size;
return EFI_SUCCESS;
}
@ -2545,9 +2517,9 @@ static EFI_STATUS image_start(
return log_error_status(err, "Error making file device path: %m");
size_t initrd_size = 0;
_cleanup_pages_ Pages initrd_pages = {};
_cleanup_free_ void *initrd = NULL;
_cleanup_free_ char16_t *options_initrd = NULL;
err = initrd_prepare(image_root, entry, &options_initrd, &initrd_pages, &initrd_size);
err = initrd_prepare(image_root, entry, &options_initrd, &initrd, &initrd_size);
if (err != EFI_SUCCESS)
return log_error_status(err, "Error preparing initrd: %m");
@ -2565,7 +2537,7 @@ static EFI_STATUS image_start(
}
_cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
err = initrd_register(PHYSICAL_ADDRESS_TO_POINTER(initrd_pages.addr), initrd_size, &initrd_handle);
err = initrd_register(initrd, initrd_size, &initrd_handle);
if (err != EFI_SUCCESS)
return log_error_status(err, "Error registering initrd: %m");

View File

@ -134,8 +134,9 @@ static EFI_STATUS combine_initrds(
assert(PHYSICAL_ADDRESS_TO_POINTER(pages.addr + n) == p);
*ret_initrd_pages = TAKE_STRUCT(pages);
*ret_initrd_pages = pages;
*ret_initrd_size = n;
pages.n_pages = 0;
return EFI_SUCCESS;
}

View File

@ -2728,7 +2728,6 @@ int config_parse_environ(
COMMON_CREDS_SPECIFIERS(ltype),
{ 'h', specifier_user_home, NULL },
{ 's', specifier_user_shell, NULL },
{}
};
for (const char *p = rvalue;; ) {

View File

@ -528,15 +528,11 @@ static int append_extensions(
&result);
if (r < 0)
return r;
if (!result.path) {
if (m->ignore_enoent)
continue;
if (!result.path)
return log_debug_errno(
SYNTHETIC_ERRNO(ENOENT),
"No matching entry in .v/ directory %s found.",
m->source);
}
r = verity_settings_load(&verity, result.path, /* root_hash_path= */ NULL, /* root_hash_sig_path= */ NULL);
if (r < 0)
@ -579,6 +575,10 @@ static int append_extensions(
const char *e = *extension_directory;
bool ignore_enoent = false;
/* Pick up the counter where the ExtensionImages left it. */
if (asprintf(&mount_point, "%s/unit-extensions/%zu", private_namespace_dir, n_mount_images++) < 0)
return -ENOMEM;
/* Look for any prefixes */
if (startswith(e, "-")) {
e++;
@ -596,19 +596,11 @@ static int append_extensions(
&result);
if (r < 0)
return r;
if (!result.path) {
if (ignore_enoent)
continue;
if (!result.path)
return log_debug_errno(
SYNTHETIC_ERRNO(ENOENT),
"No matching entry in .v/ directory %s found.",
e);
}
/* Pick up the counter where the ExtensionImages left it. */
if (asprintf(&mount_point, "%s/unit-extensions/%zu", private_namespace_dir, n_mount_images++) < 0)
return -ENOMEM;
for (size_t j = 0; hierarchies && hierarchies[j]; ++j) {
char *prefixed_hierarchy = path_join(mount_point, hierarchies[j]);

View File

@ -1046,6 +1046,7 @@ static int process_socket(int fd) {
_cleanup_close_ int input_fd = -EBADF, mount_tree_fd = -EBADF;
Context context = {};
struct iovec_wrapper iovw = {};
struct iovec iovec;
bool first = true;
int r;
@ -1062,7 +1063,8 @@ static int process_socket(int fd) {
.msg_controllen = sizeof(control),
.msg_iovlen = 1,
};
ssize_t n, l;
ssize_t n;
ssize_t l;
l = next_datagram_size_fd(fd);
if (l < 0) {
@ -1070,10 +1072,8 @@ static int process_socket(int fd) {
goto finish;
}
_cleanup_(iovec_done) struct iovec iovec = {
.iov_len = l,
.iov_base = malloc(l + 1),
};
iovec.iov_len = l;
iovec.iov_base = malloc(l + 1);
if (!iovec.iov_base) {
r = log_oom();
goto finish;
@ -1083,6 +1083,7 @@ static int process_socket(int fd) {
n = recvmsg_safe(fd, &mh, MSG_CMSG_CLOEXEC);
if (n < 0) {
free(iovec.iov_base);
r = log_error_errno(n, "Failed to receive datagram: %m");
goto finish;
}
@ -1092,6 +1093,8 @@ static int process_socket(int fd) {
if (n == 0) {
struct cmsghdr *found;
free(iovec.iov_base);
found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int) * 2));
if (found) {
int fds[2] = EBADF_PAIR;
@ -1131,8 +1134,6 @@ static int process_socket(int fd) {
r = iovw_put(&iovw, iovec.iov_base, iovec.iov_len);
if (r < 0)
goto finish;
TAKE_STRUCT(iovec);
}
/* Make sure we got all data we really need */

View File

@ -7,6 +7,7 @@
#include "sd-varlink.h"
#include "build.h"
#include "build-path.h"
#include "bus-polkit.h"
#include "creds-util.h"
#include "dirent-util.h"
@ -69,11 +70,11 @@ STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
static const char* transcode_mode_table[_TRANSCODE_MAX] = {
[TRANSCODE_OFF] = "off",
[TRANSCODE_BASE64] = "base64",
[TRANSCODE_OFF] = "off",
[TRANSCODE_BASE64] = "base64",
[TRANSCODE_UNBASE64] = "unbase64",
[TRANSCODE_HEX] = "hex",
[TRANSCODE_UNHEX] = "unhex",
[TRANSCODE_HEX] = "hex",
[TRANSCODE_UNHEX] = "unhex",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(transcode_mode, TranscodeMode);
@ -714,9 +715,9 @@ static int verb_help(int argc, char **argv, void *userdata) {
" ciphertext credential file\n"
" decrypt INPUT [OUTPUT] Decrypt ciphertext credential file and write to\n"
" plaintext credential file\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
"\n%3$sOptions:%4$s\n"
" --no-pager Do not pipe output into a pager\n"
" --no-legend Do not show the headers and footers\n"
" --json=pretty|short|off\n"
@ -1046,7 +1047,7 @@ static int creds_main(int argc, char *argv[]) {
{ "decrypt", 2, 3, 0, verb_decrypt },
{ "setup", VERB_ANY, 1, 0, verb_setup },
{ "help", VERB_ANY, 1, 0, verb_help },
{ "has-tpm2", VERB_ANY, 1, 0, verb_has_tpm2 }, /* for backward compatibility */
{ "has-tpm2", VERB_ANY, 1, 0, verb_has_tpm2 },
{}
};

View File

@ -31,25 +31,3 @@ static inline bool unified_section_measure(UnifiedSection section) {
/* Max number of profiles per UKI */
#define UNIFIED_PROFILES_MAX 256U
/* The native PE machine type, if known, for a full list see:
* https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types */
#ifndef _IMAGE_FILE_MACHINE_NATIVE
# if defined(__x86_64__)
# define _IMAGE_FILE_MACHINE_NATIVE UINT16_C(0x8664)
# elif defined(__i386__)
# define _IMAGE_FILE_MACHINE_NATIVE UINT16_C(0x014c)
# elif defined(__ia64__)
# define _IMAGE_FILE_MACHINE_NATIVE UINT16_C(0x0200)
# elif defined(__aarch64__)
# define _IMAGE_FILE_MACHINE_NATIVE UINT16_C(0xaa64)
# elif defined(__arm__)
# define _IMAGE_FILE_MACHINE_NATIVE UINT16_C(0x01c0)
# elif defined(__riscv)
# if __SIZEOF_POINTER__ == 4
# define _IMAGE_FILE_MACHINE_NATIVE UINT16_C(0x5032)
# elif __SIZEOF_POINTER__ == 8
# define _IMAGE_FILE_MACHINE_NATIVE UINT16_C(0x5064)
# endif
# endif
#endif

View File

@ -222,16 +222,20 @@ int manager_new(Manager **ret) {
if (r < 0)
return r;
r = sd_event_set_signal_exit(m->event, true);
r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
if (r < 0)
return r;
r = sd_event_add_memory_pressure(m->event, /* ret_event_source= */ NULL, /* callback= */ NULL, /* userdata= */ NULL);
r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
if (r < 0)
return r;
r = sd_event_add_memory_pressure(m->event, NULL, NULL, NULL);
if (r < 0)
log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_PRIVILEGE(r) || (r == -EHOSTDOWN) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to allocate memory pressure watch, ignoring: %m");
r = sd_event_add_signal(m->event, /* ret_event_source= */ NULL, (SIGRTMIN+18)|SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, /* userdata = */ NULL);
r = sd_event_add_signal(m->event, NULL, SIGRTMIN+18, sigrtmin18_handler, NULL);
if (r < 0)
return r;

View File

@ -29,7 +29,7 @@ static int run(int argc, char *argv[]) {
umask(0022);
assert_se(sigprocmask_many(SIG_BLOCK, /* ret_old_mask= */ NULL, SIGCHLD) >= 0);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGRTMIN+18) >= 0);
r = manager_new(&m);
if (r < 0)

View File

@ -1784,15 +1784,17 @@ static int server_setup_signals(Server *s) {
assert(s);
r = sd_event_add_signal(s->event, &s->sigusr1_event_source, SIGUSR1|SD_EVENT_SIGNAL_PROCMASK, dispatch_sigusr1, s);
assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGRTMIN+1, SIGRTMIN+18) >= 0);
r = sd_event_add_signal(s->event, &s->sigusr1_event_source, SIGUSR1, dispatch_sigusr1, s);
if (r < 0)
return r;
r = sd_event_add_signal(s->event, &s->sigusr2_event_source, SIGUSR2|SD_EVENT_SIGNAL_PROCMASK, dispatch_sigusr2, s);
r = sd_event_add_signal(s->event, &s->sigusr2_event_source, SIGUSR2, dispatch_sigusr2, s);
if (r < 0)
return r;
r = sd_event_add_signal(s->event, &s->sigterm_event_source, SIGTERM|SD_EVENT_SIGNAL_PROCMASK, dispatch_sigterm, s);
r = sd_event_add_signal(s->event, &s->sigterm_event_source, SIGTERM, dispatch_sigterm, s);
if (r < 0)
return r;
@ -1803,7 +1805,7 @@ static int server_setup_signals(Server *s) {
/* When journald is invoked on the terminal (when debugging), it's useful if C-c is handled
* equivalent to SIGTERM. */
r = sd_event_add_signal(s->event, &s->sigint_event_source, SIGINT|SD_EVENT_SIGNAL_PROCMASK, dispatch_sigterm, s);
r = sd_event_add_signal(s->event, &s->sigint_event_source, SIGINT, dispatch_sigterm, s);
if (r < 0)
return r;
@ -1814,7 +1816,7 @@ static int server_setup_signals(Server *s) {
/* SIGRTMIN+1 causes an immediate sync. We process this very late, so that everything else queued at
* this point is really written to disk. Clients can watch /run/systemd/journal/synced with inotify
* until its mtime changes to see when a sync happened. */
r = sd_event_add_signal(s->event, &s->sigrtmin1_event_source, (SIGRTMIN+1)|SD_EVENT_SIGNAL_PROCMASK, dispatch_sigrtmin1, s);
r = sd_event_add_signal(s->event, &s->sigrtmin1_event_source, SIGRTMIN+1, dispatch_sigrtmin1, s);
if (r < 0)
return r;
@ -1822,7 +1824,7 @@ static int server_setup_signals(Server *s) {
if (r < 0)
return r;
r = sd_event_add_signal(s->event, /* ret_event_source= */ NULL, (SIGRTMIN+18)|SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, &s->sigrtmin18_info);
r = sd_event_add_signal(s->event, NULL, SIGRTMIN+18, sigrtmin18_handler, &s->sigrtmin18_info);
if (r < 0)
return r;

View File

@ -86,11 +86,15 @@ static int manager_new(Manager **ret) {
if (r < 0)
return r;
r = sd_event_set_signal_exit(m->event, true);
r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
if (r < 0)
return r;
r = sd_event_add_signal(m->event, /* ret_event_source= */ NULL, (SIGRTMIN+18)|SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, /* userdata= */ NULL);
r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
if (r < 0)
return r;
r = sd_event_add_signal(m->event, NULL, SIGRTMIN+18, sigrtmin18_handler, NULL);
if (r < 0)
return r;
@ -822,7 +826,7 @@ static int manager_connect_console(Manager *m) {
return log_error_errno(r, "Failed to watch foreground console: %m");
/*
* SIGRTMIN + 0 is used as global VT-release signal, SIGRTMIN + 1 is used
* SIGRTMIN is used as global VT-release signal, SIGRTMIN + 1 is used
* as VT-acquire signal. We ignore any acquire-events (yes, we still
* have to provide a valid signal-number for it!) and acknowledge all
* release events immediately.
@ -834,10 +838,11 @@ static int manager_connect_console(Manager *m) {
SIGRTMIN, SIGRTMAX);
assert_se(ignore_signals(SIGRTMIN + 1) >= 0);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN) >= 0);
r = sd_event_add_signal(m->event, /* ret_event_source= */ NULL, (SIGRTMIN + 0) | SD_EVENT_SIGNAL_PROCMASK, manager_vt_switch, m);
r = sd_event_add_signal(m->event, NULL, SIGRTMIN, manager_vt_switch, m);
if (r < 0)
return log_error_errno(r, "Failed to subscribe to SIGRTMIN+0 signal: %m");
return log_error_errno(r, "Failed to subscribe to signal: %m");
return 0;
}
@ -1092,7 +1097,7 @@ static int manager_startup(Manager *m) {
assert(m);
r = sd_event_add_signal(m->event, /* ret_event_source= */ NULL, SIGHUP|SD_EVENT_SIGNAL_PROCMASK, manager_dispatch_reload_signal, m);
r = sd_event_add_signal(m->event, NULL, SIGHUP, manager_dispatch_reload_signal, m);
if (r < 0)
return log_error_errno(r, "Failed to register SIGHUP handler: %m");
@ -1242,7 +1247,7 @@ static int run(int argc, char *argv[]) {
(void) mkdir_label("/run/systemd/users", 0755);
(void) mkdir_label("/run/systemd/sessions", 0755);
assert_se(sigprocmask_many(SIG_BLOCK, /* ret_old_mask= */ NULL, SIGCHLD) >= 0);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGHUP, SIGTERM, SIGINT, SIGCHLD, SIGRTMIN+18) >= 0);
r = manager_new(&m);
if (r < 0)

View File

@ -1220,6 +1220,8 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, PT
assert(master >= 0);
assert(name);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT) >= 0);
if (!arg_quiet) {
if (streq(name, ".host"))
log_info("Connected to the local host. Press ^] three times within 1s to exit session.");
@ -1227,9 +1229,8 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, PT
log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name);
}
r = sd_event_set_signal_exit(event, true);
if (r < 0)
return log_error_errno(r, "Failed to enable SIGINT/SITERM handling: %m");
(void) sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
(void) sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
r = pty_forward_new(event, master, flags, forward);
if (r < 0)

View File

@ -55,11 +55,15 @@ static int manager_new(Manager **ret) {
if (r < 0)
return r;
r = sd_event_set_signal_exit(m->event, true);
r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
if (r < 0)
return r;
r = sd_event_add_signal(m->event, /* ret_event_source= */ NULL, (SIGRTMIN+18)|SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, /* userdata= */ NULL);
r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
if (r < 0)
return r;
r = sd_event_add_signal(m->event, NULL, SIGRTMIN+18, sigrtmin18_handler, NULL);
if (r < 0)
return r;
@ -328,7 +332,7 @@ static int run(int argc, char *argv[]) {
* make sure this check stays in. */
(void) mkdir_label("/run/systemd/machines", 0755);
assert_se(sigprocmask_many(SIG_BLOCK, /* ret_old_mask= */ NULL, SIGCHLD) >= 0);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGRTMIN+18) >= 0);
r = manager_new(&m);
if (r < 0)

View File

@ -410,7 +410,8 @@ int manager_new(Manager **ret,
if (r < 0)
return r;
(void) sd_event_set_signal_exit(m->event, true);
(void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
(void) sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
if (timeout > 0) {
r = sd_event_add_time_relative(m->event, NULL, CLOCK_BOOTTIME, timeout, 0, NULL, INT_TO_PTR(-ETIMEDOUT));

View File

@ -204,6 +204,8 @@ static int run(int argc, char *argv[]) {
if (arg_quiet)
log_set_max_level(LOG_ERR);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT) >= 0);
r = manager_new(&m, arg_interfaces, arg_ignore, arg_required_operstate, arg_required_family, arg_any, arg_timeout);
if (r < 0)
return log_error_errno(r, "Could not create manager: %m");

View File

@ -5601,10 +5601,6 @@ static int run_container(
log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
}
/* Note: we do not use SD_EVENT_SIGNAL_PROCMASK or sd_event_set_signal_exit(), since we want the
* signals to be block continuously, even if we destroy the event loop and allocate a new one on
* container reboot. */
if (arg_kill_signal > 0) {
/* Try to kill the init system on SIGINT or SIGTERM */
(void) sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, PID_TO_PTR(*pid));

View File

@ -657,7 +657,11 @@ int manager_new(Manager **ret) {
(void) sd_event_set_watchdog(m->event, true);
r = sd_event_set_signal_exit(m->event, true);
r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
if (r < 0)
return r;
r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
if (r < 0)
return r;

View File

@ -167,6 +167,8 @@ static int run(int argc, char *argv[]) {
if (!FLAGS_SET(mask, CGROUP_MASK_MEMORY))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Requires the cgroup memory controller.");
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT) >= 0);
if (arg_mem_pressure_usec > 0 && arg_mem_pressure_usec < 1 * USEC_PER_SEC)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "DefaultMemoryPressureDurationSec= must be 0 or at least 1s");

View File

@ -5408,7 +5408,7 @@ static int partition_populate_directory(Context *context, Partition *p, char **r
assert(ret);
log_info("Preparing to populate %s filesystem.", p->format);
log_info("Populating %s filesystem.", p->format);
r = var_tmp_dir(&vt);
if (r < 0)
@ -5434,7 +5434,7 @@ static int partition_populate_directory(Context *context, Partition *p, char **r
if (r < 0)
return r;
log_info("Ready to populate %s filesystem.", p->format);
log_info("Successfully populated %s filesystem.", p->format);
*ret = TAKE_PTR(root);
return 0;

View File

@ -36,11 +36,15 @@ static int manager_new(Manager **ret) {
if (r < 0)
return r;
r = sd_event_set_signal_exit(m->event, true);
r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
if (r < 0)
return r;
r = sd_event_add_signal(m->event, /* ret_event_source= */ NULL, (SIGRTMIN+18)|SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, /* userdata= */ NULL);
r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
if (r < 0)
return r;
r = sd_event_add_signal(m->event, NULL, SIGRTMIN+18, sigrtmin18_handler, NULL);
if (r < 0)
return r;
@ -138,7 +142,7 @@ static int run(int argc, char *argv[]) {
if (argc != 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
assert_se(sigprocmask_many(SIG_BLOCK, /* ret_old_mask= */ NULL, SIGCHLD) >= 0);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, SIGRTMIN+18) >= 0);
r = manager_new(&m);
if (r < 0)

View File

@ -682,9 +682,9 @@ int manager_new(Manager **ret) {
if (r < 0)
return r;
r = sd_event_set_signal_exit(m->event, true);
if (r < 0)
return r;
(void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
(void) sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
(void) sd_event_add_signal(m->event, NULL, SIGHUP | SD_EVENT_SIGNAL_PROCMASK, manager_dispatch_reload_signal, m);
(void) sd_event_set_watchdog(m->event, true);
@ -720,25 +720,10 @@ int manager_new(Manager **ret) {
if (r < 0)
return r;
r = sd_event_add_signal(m->event, /* ret_event_source= */ NULL, SIGHUP | SD_EVENT_SIGNAL_PROCMASK, manager_dispatch_reload_signal, m);
if (r < 0)
return log_debug_errno(r, "Failed install SIGHUP handler: %m");
r = sd_event_add_signal(m->event, /* ret_event_source= */ NULL, SIGUSR1 | SD_EVENT_SIGNAL_PROCMASK, manager_sigusr1, m);
if (r < 0)
return log_debug_errno(r, "Failed install SIGUSR1 handler: %m");
r = sd_event_add_signal(m->event, /* ret_event_source= */ NULL, SIGUSR2 | SD_EVENT_SIGNAL_PROCMASK, manager_sigusr2, m);
if (r < 0)
return log_debug_errno(r, "Failed install SIGUSR2 handler: %m");
r = sd_event_add_signal(m->event, /* ret_event_source= */ NULL, (SIGRTMIN+1) | SD_EVENT_SIGNAL_PROCMASK, manager_sigrtmin1, m);
if (r < 0)
return log_debug_errno(r, "Failed install SIGRTMIN+1 handler: %m");
r = sd_event_add_signal(m->event, /* ret_event_source= */ NULL, (SIGRTMIN+18) | SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, &m->sigrtmin18_info);
if (r < 0)
return log_debug_errno(r, "Failed install SIGRTMIN+18 handler: %m");
(void) sd_event_add_signal(m->event, &m->sigusr1_event_source, SIGUSR1, manager_sigusr1, m);
(void) sd_event_add_signal(m->event, &m->sigusr2_event_source, SIGUSR2, manager_sigusr2, m);
(void) sd_event_add_signal(m->event, &m->sigrtmin1_event_source, SIGRTMIN+1, manager_sigrtmin1, m);
(void) sd_event_add_signal(m->event, NULL, SIGRTMIN+18, sigrtmin18_handler, &m->sigrtmin18_info);
manager_cleanup_saved_user(m);
@ -816,6 +801,10 @@ Manager *manager_free(Manager *m) {
sd_bus_flush_close_unref(m->bus);
sd_event_source_unref(m->sigusr1_event_source);
sd_event_source_unref(m->sigusr2_event_source);
sd_event_source_unref(m->sigrtmin1_event_source);
dns_resource_key_unref(m->llmnr_host_ipv4_key);
dns_resource_key_unref(m->llmnr_host_ipv6_key);
dns_resource_key_unref(m->mdns_host_ipv4_key);

View File

@ -123,6 +123,10 @@ struct Manager {
int hostname_fd;
sd_event_source *hostname_event_source;
sd_event_source *sigusr1_event_source;
sd_event_source *sigusr2_event_source;
sd_event_source *sigrtmin1_event_source;
unsigned n_transactions_total;
unsigned n_timeouts_total;
unsigned n_timeouts_served_stale_total;

View File

@ -67,6 +67,8 @@ static int run(int argc, char *argv[]) {
return log_error_errno(r, "Failed to drop privileges: %m");
}
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGUSR1, SIGUSR2, SIGRTMIN+1, SIGRTMIN+18) >= 0);
r = manager_new(&m);
if (r < 0)
return log_error_errno(r, "Could not create manager: %m");

View File

@ -1875,6 +1875,8 @@ static int start_transient_service(sd_bus *bus) {
return log_error_errno(r, "Failed to get event loop: %m");
if (master >= 0) {
assert_se(sigprocmask_many(SIG_BLOCK, /* old_sigset=*/ NULL, SIGWINCH) >= 0);
(void) sd_event_set_signal_exit(c.event, true);
if (!arg_quiet)

View File

@ -23,7 +23,6 @@
#include "string-table.h"
#include "strv.h"
#include "terminal-util.h"
#include "uki.h"
#include "unaligned.h"
static const char* const boot_entry_type_table[_BOOT_ENTRY_TYPE_MAX] = {
@ -49,7 +48,6 @@ static void boot_entry_free(BootEntry *entry) {
free(entry->id);
free(entry->id_old);
free(entry->id_without_profile);
free(entry->path);
free(entry->root);
free(entry->title);
@ -531,18 +529,10 @@ static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
return r;
}
r = -strverscmp_improved(a->id_without_profile ?: a->id, b->id_without_profile ?: b->id);
r = -strverscmp_improved(a->id, b->id);
if (r != 0)
return r;
if (a->id_without_profile && b->id_without_profile) {
/* The strverscmp_improved() call above already established that we are talking about the
* same image here, hence order by profile, if there is one */
r = CMP(a->profile, b->profile);
if (r != 0)
return r;
}
if (a->tries_left != UINT_MAX || b->tries_left != UINT_MAX)
return 0;
@ -646,30 +636,28 @@ static int boot_entries_find_type1(
static int boot_entry_load_unified(
const char *root,
const char *path,
unsigned profile,
const char *osrelease_text,
const char *profile_text,
const char *cmdline_text,
const char *osrelease,
const char *cmdline,
BootEntry *ret) {
_cleanup_free_ char *fname = NULL, *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL,
*os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL;
_cleanup_(boot_entry_free) BootEntry tmp = BOOT_ENTRY_INIT(BOOT_ENTRY_UNIFIED);
const char *k, *good_name, *good_version, *good_sort_key;
_cleanup_fclose_ FILE *f = NULL;
int r;
assert(root);
assert(path);
assert(osrelease_text);
assert(ret);
assert(osrelease);
k = path_startswith(path, root);
if (!k)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path is not below root: %s", path);
f = fmemopen_unlocked((void*) osrelease_text, strlen(osrelease_text), "r");
f = fmemopen_unlocked((void*) osrelease, strlen(osrelease), "r");
if (!f)
return log_oom();
return log_error_errno(errno, "Failed to open os-release buffer: %m");
r = parse_env_file(f, "os-release",
"PRETTY_NAME", &os_pretty_name,
@ -697,28 +685,10 @@ static int boot_entry_load_unified(
&good_sort_key))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path);
_cleanup_free_ char *profile_id = NULL, *profile_title = NULL;
if (profile_text) {
fclose(f);
f = fmemopen_unlocked((void*) profile_text, strlen(profile_text), "r");
if (!f)
return log_oom();
r = parse_env_file(
f, "profile",
"ID", &profile_id,
"TITLE", &profile_title);
if (r < 0)
return log_error_errno(r, "Failed to parse profile data from unified kernel image '%s': %m", path);
}
r = path_extract_filename(path, &fname);
if (r < 0)
return log_error_errno(r, "Failed to extract file name from '%s': %m", path);
_cleanup_(boot_entry_free) BootEntry tmp = BOOT_ENTRY_INIT(BOOT_ENTRY_UNIFIED);
r = boot_filename_extract_tries(fname, &tmp.id, &tmp.tries_left, &tmp.tries_done);
if (r < 0)
return r;
@ -726,19 +696,6 @@ static int boot_entry_load_unified(
if (!efi_loader_entry_name_valid(tmp.id))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry name: %s", tmp.id);
tmp.profile = profile;
if (profile_id || profile > 0) {
tmp.id_without_profile = TAKE_PTR(tmp.id);
if (profile_id)
tmp.id = strjoin(tmp.id_without_profile, "@", profile_id);
else
(void) asprintf(&tmp.id, "%s@%u", tmp.id_without_profile, profile);
if (!tmp.id)
return log_oom();
}
if (os_id && os_version_id) {
tmp.id_old = strjoin(os_id, "-", os_version_id);
if (!tmp.id_old)
@ -757,18 +714,13 @@ static int boot_entry_load_unified(
if (!tmp.kernel)
return log_oom();
tmp.options = strv_new(cmdline_text);
tmp.options = strv_new(skip_leading_chars(cmdline, WHITESPACE));
if (!tmp.options)
return log_oom();
if (profile_title)
tmp.title = strjoin(good_name, " (", profile_title, ")");
else if (profile_id)
tmp.title = strjoin(good_name, " (", profile_id, ")");
else if (profile > 0)
(void) asprintf(&tmp.title, "%s (@%u)", good_name, profile);
else
tmp.title = strdup(good_name);
delete_trailing_chars(tmp.options[0], WHITESPACE);
tmp.title = strdup(good_name);
if (!tmp.title)
return log_oom();
@ -788,7 +740,11 @@ static int boot_entry_load_unified(
return 0;
}
static int pe_load_headers_and_sections(
/* Maximum PE section we are willing to load (Note that sections we are not interested in may be larger, but
* the ones we do care about and we are willing to load into memory have this size limit.) */
#define PE_SECTION_SIZE_MAX (4U*1024U*1024U)
static int find_sections(
int fd,
const char *path,
IMAGE_SECTION_HEADER **ret_sections,
@ -818,177 +774,92 @@ static int pe_load_headers_and_sections(
return 0;
}
static const IMAGE_SECTION_HEADER* pe_find_profile_section_table(
const PeHeader *pe_header,
const IMAGE_SECTION_HEADER *sections,
unsigned profile,
size_t *ret_n_sections) {
assert(pe_header);
/* Looks for the part of the section table that defines the specified profile. If 'profile' is
* specified as UINT_MAX this will look for the base profile. */
if (le16toh(pe_header->pe.NumberOfSections) == 0)
return NULL;
assert(sections);
const IMAGE_SECTION_HEADER
*p = sections,
*e = sections + le16toh(pe_header->pe.NumberOfSections),
*start = profile == UINT_MAX ? sections : NULL,
*end;
unsigned current_profile = UINT_MAX;
for (;;) {
p = pe_section_table_find(p, e - p, ".profile");
if (!p) {
end = e;
break;
}
if (current_profile == profile) {
end = p;
break;
}
if (current_profile == UINT_MAX)
current_profile = 0;
else
current_profile++;
if (current_profile == profile)
start = p;
p++; /* Continue scanning after the .profile entry we just found */
}
if (!start)
return NULL;
if (ret_n_sections)
*ret_n_sections = end - start;
return start;
}
static int trim_cmdline(char **cmdline) {
assert(cmdline);
/* Strips leading and trailing whitespace from command line */
if (!*cmdline)
return 0;
const char *skipped = skip_leading_chars(*cmdline, WHITESPACE);
if (isempty(skipped)) {
*cmdline = mfree(*cmdline);
return 0;
}
if (skipped != *cmdline) {
_cleanup_free_ char *c = strdup(skipped);
if (!c)
return -ENOMEM;
free_and_replace(*cmdline, c);
}
delete_trailing_chars(*cmdline, WHITESPACE);
return 1;
}
/* Maximum PE section we are willing to load (Note that sections we are not interested in may be larger, but
* the ones we do care about and we are willing to load into memory have this size limit.) */
#define PE_SECTION_SIZE_MAX (4U*1024U*1024U)
static int pe_find_uki_sections(
static int find_cmdline_section(
int fd,
const char *path,
unsigned profile,
char **ret_osrelease,
char **ret_profile,
IMAGE_SECTION_HEADER *sections,
PeHeader *pe_header,
char **ret_cmdline) {
int r;
char *cmdline = NULL, *t = NULL;
_cleanup_free_ char *word = NULL;
assert(path);
if (!ret_cmdline)
return 0;
r = pe_read_section_data(fd, pe_header, sections, ".cmdline", PE_SECTION_SIZE_MAX, (void**) &cmdline, NULL);
if (r == -ENXIO) { /* cmdline is optional */
*ret_cmdline = NULL;
return 0;
}
if (r < 0)
return log_warning_errno(r, "Failed to read .cmdline section of '%s': %m", path);
word = strdup(cmdline);
if (!word)
return log_oom();
/* Quick test to check if there is actual content in the addon cmdline */
t = delete_chars(word, NULL);
if (isempty(t))
*ret_cmdline = NULL;
else
*ret_cmdline = TAKE_PTR(cmdline);
return 0;
}
static int find_osrel_section(
int fd,
const char *path,
IMAGE_SECTION_HEADER *sections,
PeHeader *pe_header,
char **ret_osrelease) {
int r;
if (!ret_osrelease)
return 0;
r = pe_read_section_data(fd, pe_header, sections, ".osrel", PE_SECTION_SIZE_MAX, (void**) ret_osrelease, NULL);
if (r < 0)
return log_error_errno(r, "Failed to read .osrel section of '%s': %m", path);
return 0;
}
static int find_uki_sections(
int fd,
const char *path,
char **ret_osrelease,
char **ret_cmdline) {
_cleanup_free_ char *osrelease_text = NULL, *profile_text = NULL, *cmdline_text = NULL;
_cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL;
_cleanup_free_ PeHeader *pe_header = NULL;
int r;
assert(fd >= 0);
assert(path);
assert(profile != UINT_MAX);
assert(ret_osrelease);
assert(ret_profile);
assert(ret_cmdline);
r = pe_load_headers_and_sections(fd, path, &sections, &pe_header);
r = find_sections(fd, path, &sections, &pe_header);
if (r < 0)
return r;
if (!pe_is_uki(pe_header, sections))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Parsed PE file '%s' is not a UKI.", path);
if (!pe_is_native(pe_header)) /* Don't process non-native UKIs */
goto nothing;
r = find_osrel_section(fd, path, sections, pe_header, ret_osrelease);
if (r < 0)
return r;
/* Find part of the section table for this profile */
size_t n_psections = 0;
const IMAGE_SECTION_HEADER *psections = pe_find_profile_section_table(pe_header, sections, profile, &n_psections);
if (!psections && profile != 0) /* Profile not found? (Profile @0 needs no explicit .profile!) */
goto nothing;
r = find_cmdline_section(fd, path, sections, pe_header, ret_cmdline);
if (r < 0)
return r;
/* Find base profile part of section table */
size_t n_bsections;
const IMAGE_SECTION_HEADER *bsections = ASSERT_PTR(pe_find_profile_section_table(pe_header, sections, UINT_MAX, &n_bsections));
struct {
const char *name;
char **data;
} table[] = {
{ ".osrel", &osrelease_text },
{ ".profile", &profile_text },
{ ".cmdline", &cmdline_text },
};
FOREACH_ELEMENT(t, table) {
const IMAGE_SECTION_HEADER *found;
/* First look in the profile part of the section table, and if we don't find anything there, look into the base part */
found = pe_section_table_find(psections, n_psections, t->name);
if (!found) {
found = pe_section_table_find(bsections, n_bsections, t->name);
if (!found)
continue;
}
/* Permit "masking" of sections in the base profile */
if (found->VirtualSize == 0)
continue;
r = pe_read_section_data(fd, found, PE_SECTION_SIZE_MAX, (void**) t->data, /* ret_data= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to load contents of section '%s': %m", t->name);
}
if (!osrelease_text)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Unified kernel image lacks .osrel data for profile @%u, refusing.", profile);
if (trim_cmdline(&cmdline_text) < 0)
return log_oom();
*ret_osrelease = TAKE_PTR(osrelease_text);
*ret_profile = TAKE_PTR(profile_text);
*ret_cmdline = TAKE_PTR(cmdline_text);
return 1;
nothing:
*ret_osrelease = *ret_profile = *ret_cmdline = NULL;
return 0;
}
static int pe_find_addon_sections(
static int find_addon_sections(
int fd,
const char *path,
char **ret_cmdline) {
@ -997,39 +868,19 @@ static int pe_find_addon_sections(
_cleanup_free_ PeHeader *pe_header = NULL;
int r;
assert(fd >= 0);
assert(path);
r = pe_load_headers_and_sections(fd, path, &sections, &pe_header);
r = find_sections(fd, path, &sections, &pe_header);
if (r < 0)
return r;
if (!pe_is_addon(pe_header, sections))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Parse PE file '%s' is not an add-on.", path);
r = find_cmdline_section(fd, path, sections, pe_header, ret_cmdline);
/* If addon cmdline is empty or contains just separators,
* don't bother tracking it.
* Don't check r because it cannot return <0 if cmdline is empty,
* as cmdline is always optional. */
if (!ret_cmdline)
return log_warning_errno(SYNTHETIC_ERRNO(ENOENT), "Addon %s contains empty cmdline and will be therefore ignored.", path);
/* Define early, before the gotos below */
_cleanup_free_ char *cmdline_text = NULL;
if (!pe_is_native(pe_header))
goto nothing;
const IMAGE_SECTION_HEADER *found = pe_section_table_find(sections, le16toh(pe_header->pe.NumberOfSections), ".cmdline");
if (!found)
goto nothing;
r = pe_read_section_data(fd, found, PE_SECTION_SIZE_MAX, (void**) &cmdline_text, /* ret_size= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to load contents of section '.cmdline': %m");
if (trim_cmdline(&cmdline_text) < 0)
return log_oom();
*ret_cmdline = TAKE_PTR(cmdline_text);
return 1;
nothing:
*ret_cmdline = NULL;
return 0;
return r;
}
static int insert_boot_entry_addon(
@ -1108,7 +959,7 @@ static int boot_entries_find_unified_addons(
if (!j)
return log_oom();
if (pe_find_addon_sections(fd, j, &cmdline) <= 0)
if (find_addon_sections(fd, j, &cmdline) < 0)
continue;
location = strdup(j);
@ -1181,13 +1032,19 @@ static int boot_entries_find_unified(
return log_error_errno(r, "Failed to open '%s/%s': %m", root, dir);
FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read %s: %m", full)) {
_cleanup_free_ char *j = NULL, *osrelease = NULL, *cmdline = NULL;
_cleanup_close_ int fd = -EBADF;
if (!dirent_is_file(de))
continue;
if (!endswith_no_case(de->d_name, ".efi"))
continue;
_cleanup_close_ int fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOFOLLOW|O_NOCTTY);
if (!GREEDY_REALLOC0(config->entries, config->n_entries + 1))
return log_oom();
fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOFOLLOW|O_NOCTTY);
if (fd < 0) {
log_warning_errno(errno, "Failed to open %s/%s, ignoring: %m", full, de->d_name);
continue;
@ -1199,30 +1056,23 @@ static int boot_entries_find_unified(
if (r == 0) /* inode already seen or otherwise not relevant */
continue;
_cleanup_free_ char *j = path_join(full, de->d_name);
j = path_join(full, de->d_name);
if (!j)
return log_oom();
for (unsigned p = 0; p < UNIFIED_PROFILES_MAX; p++) {
_cleanup_free_ char *osrelease = NULL, *profile = NULL, *cmdline = NULL;
if (find_uki_sections(fd, j, &osrelease, &cmdline) < 0)
continue;
r = pe_find_uki_sections(fd, j, p, &osrelease, &profile, &cmdline);
if (r == 0) /* this profile does not exist, we are done */
break;
if (r < 0)
continue;
r = boot_entry_load_unified(root, j, osrelease, cmdline, config->entries + config->n_entries);
if (r < 0)
continue;
if (!GREEDY_REALLOC0(config->entries, config->n_entries + 2))
return log_oom();
/* look for .efi.extra.d */
r = boot_entries_find_unified_local_addons(config, dirfd(d), de->d_name, full, config->entries + config->n_entries);
if (r < 0)
continue;
if (boot_entry_load_unified(root, j, p, osrelease, profile, cmdline, config->entries + config->n_entries) < 0)
continue;
config->n_entries++;
/* look for .efi.extra.d */
(void) boot_entries_find_unified_local_addons(config, dirfd(d), de->d_name, full, config->entries + config->n_entries);
}
config->n_entries++;
}
return 0;
@ -1798,14 +1648,8 @@ int show_boot_entry(
putchar('\n');
if (e->id) {
printf(" id: %s", e->id);
if (e->id_without_profile && !streq_ptr(e->id, e->id_without_profile))
printf(" (without profile: %s)\n", e->id_without_profile);
else
putchar('\n');
}
if (e->id)
printf(" id: %s\n", e->id);
if (e->path) {
_cleanup_free_ char *text = NULL, *link = NULL;
@ -1829,7 +1673,7 @@ int show_boot_entry(
if (e->tries_done != UINT_MAX)
printf("; %u done\n", e->tries_done);
else
putchar('\n');
printf("\n");
}
if (e->sort_key)

View File

@ -38,7 +38,6 @@ typedef struct BootEntry {
bool reported_by_loader;
char *id; /* This is the file basename (including extension!) */
char *id_old; /* Old-style ID, for deduplication purposes. */
char *id_without_profile; /* id without profile suffixed */
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 */
char *title;
@ -56,7 +55,6 @@ typedef struct BootEntry {
char **device_tree_overlay;
unsigned tries_left;
unsigned tries_done;
unsigned profile;
} BootEntry;
#define BOOT_ENTRY_INIT(t) \

View File

@ -395,9 +395,9 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
}
#endif
int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **ret) {
_cleanup_free_ char *result = NULL;
size_t n_result = 0, n_unescaped = 0;
int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) {
_cleanup_free_ char *ret = NULL;
size_t n = 0;
const char *p;
bool first = true;
int r;
@ -427,18 +427,17 @@ int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **re
break;
}
n_unescaped += r + !first; /* Count unescaped length to make max length determination below */
if (ret) {
if (!GREEDY_REALLOC(result, n_result + !first + DNS_LABEL_ESCAPED_MAX))
if (_ret) {
if (!GREEDY_REALLOC(ret, n + !first + DNS_LABEL_ESCAPED_MAX))
return -ENOMEM;
r = dns_label_escape(label, r, result + n_result + !first, DNS_LABEL_ESCAPED_MAX);
r = dns_label_escape(label, r, ret + n + !first, DNS_LABEL_ESCAPED_MAX);
if (r < 0)
return r;
if (!first)
result[n_result] = '.';
ret[n] = '.';
} else {
char escaped[DNS_LABEL_ESCAPED_MAX];
@ -447,34 +446,28 @@ int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **re
return r;
}
n_result += r + !first;
n += r + !first;
first = false;
}
finish:
if (n_unescaped == 0) {
/* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */
if (ret) {
if (!GREEDY_REALLOC(result, 2)) /* Room for dot, and already pre-allocate space for the trailing NUL byte at the same time */
return -ENOMEM;
result[n_result++] = '.';
}
n_unescaped++;
}
if (n_unescaped > DNS_HOSTNAME_MAX) /* Enforce max length check on unescaped length */
if (n > DNS_HOSTNAME_MAX)
return -EINVAL;
if (ret) {
/* Suffix with a NUL byte */
if (!GREEDY_REALLOC(result, n_result + 1))
return -ENOMEM;
if (_ret) {
if (n == 0) {
/* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */
if (!GREEDY_REALLOC(ret, 2))
return -ENOMEM;
result[n_result] = 0;
*ret = TAKE_PTR(result);
ret[n++] = '.';
} else {
if (!GREEDY_REALLOC(ret, n + 1))
return -ENOMEM;
}
ret[n] = 0;
*_ret = TAKE_PTR(ret);
}
return 0;

View File

@ -66,5 +66,9 @@ static inline bool efi_has_tpm2(void) {
#endif
static inline char *efi_tilt_backslashes(char *s) {
return string_replace_char(s, '\\', '/');
}
sd_id128_t efi_guid_to_id128(const void *guid);
void efi_id128_to_guid(sd_id128_t id, void *ret_guid);

View File

@ -61,19 +61,30 @@ int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader) {
return 0;
}
static int get_device_part_uuid(const char *variable, sd_id128_t *ret) {
int efi_loader_get_device_part_uuid(sd_id128_t *ret) {
_cleanup_free_ char *p = NULL;
int r;
unsigned parsed[16];
if (!is_efi_boot())
return -EOPNOTSUPP;
return efi_get_variable_id128(variable, ret);
}
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderDevicePartUUID), &p);
if (r < 0)
return r;
int efi_loader_get_device_part_uuid(sd_id128_t *ret) {
return get_device_part_uuid(EFI_LOADER_VARIABLE(LoaderDevicePartUUID), ret);
}
if (sscanf(p, SD_ID128_UUID_FORMAT_STR,
&parsed[0], &parsed[1], &parsed[2], &parsed[3],
&parsed[4], &parsed[5], &parsed[6], &parsed[7],
&parsed[8], &parsed[9], &parsed[10], &parsed[11],
&parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16)
return -EIO;
int efi_stub_get_device_part_uuid(sd_id128_t *ret) {
return get_device_part_uuid(EFI_LOADER_VARIABLE(StubDevicePartUUID), ret);
if (ret)
for (unsigned i = 0; i < ELEMENTSOF(parsed); i++)
ret->bytes[i] = parsed[i];
return 0;
}
int efi_loader_get_entries(char ***ret) {
@ -342,22 +353,6 @@ int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat
return 0;
}
int efi_get_variable_id128(const char *variable, sd_id128_t *ret) {
int r;
assert(variable);
/* This is placed here (rather than in basic/efivars.c) because code in basic/ is not allowed to link
* against libsystemd.so */
_cleanup_free_ char *p = NULL;
r = efi_get_variable_string(variable, &p);
if (r < 0)
return r;
return sd_id128_from_string(p, ret);
}
#endif
bool efi_loader_entry_name_valid(const char *s) {

View File

@ -11,7 +11,6 @@
#if ENABLE_EFI
int efi_loader_get_device_part_uuid(sd_id128_t *ret);
int efi_stub_get_device_part_uuid(sd_id128_t *ret);
int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader);
int efi_loader_get_entries(char ***ret);
@ -24,8 +23,6 @@ int efi_measured_uki(int log_level);
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_get_variable_id128(const char *variable, sd_id128_t *ret);
#else
static inline int efi_loader_get_device_part_uuid(sd_id128_t *u) {
@ -61,10 +58,6 @@ static inline int efi_loader_update_entry_one_shot_cache(char **cache, struct st
return -EOPNOTSUPP;
}
static inline int efi_get_variable_id128(const char *variable, sd_id128_t *ret) {
return -EOPNOTSUPP;
}
#endif
bool efi_loader_entry_name_valid(const char *s);

View File

@ -14,7 +14,6 @@
static const char * const kernel_image_type_table[_KERNEL_IMAGE_TYPE_MAX] = {
[KERNEL_IMAGE_TYPE_UNKNOWN] = "unknown",
[KERNEL_IMAGE_TYPE_UKI] = "uki",
[KERNEL_IMAGE_TYPE_ADDON] = "addon",
[KERNEL_IMAGE_TYPE_PE] = "pe",
};
@ -37,7 +36,7 @@ static int uki_read_pretty_name(
assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
assert(ret);
r = pe_read_section_data_by_name(
r = pe_read_section_data(
fd,
pe_header,
sections,
@ -92,13 +91,13 @@ static int inspect_uki(
assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
if (ret_cmdline) {
r = pe_read_section_data_by_name(fd, pe_header, sections, ".cmdline", PE_SECTION_READ_MAX, (void**) &cmdline, NULL);
r = pe_read_section_data(fd, pe_header, sections, ".cmdline", PE_SECTION_READ_MAX, (void**) &cmdline, NULL);
if (r < 0 && r != -ENXIO) /* If the section doesn't exist, that's fine */
return r;
}
if (ret_uname) {
r = pe_read_section_data_by_name(fd, pe_header, sections, ".uname", PE_SECTION_READ_MAX, (void**) &uname, NULL);
r = pe_read_section_data(fd, pe_header, sections, ".uname", PE_SECTION_READ_MAX, (void**) &uname, NULL);
if (r < 0 && r != -ENXIO) /* If the section doesn't exist, that's fine */
return r;
}
@ -160,16 +159,6 @@ int inspect_kernel(
t = KERNEL_IMAGE_TYPE_UKI;
goto done;
} else if (pe_is_addon(pe_header, sections)) {
r = inspect_uki(fd, pe_header, sections, ret_cmdline, ret_uname, /* ret_pretty_name= */ NULL);
if (r < 0)
return r;
if (ret_pretty_name)
*ret_pretty_name = NULL;
t = KERNEL_IMAGE_TYPE_ADDON;
goto done;
} else
t = KERNEL_IMAGE_TYPE_PE;

View File

@ -8,7 +8,6 @@
typedef enum KernelImageType {
KERNEL_IMAGE_TYPE_UNKNOWN,
KERNEL_IMAGE_TYPE_UKI,
KERNEL_IMAGE_TYPE_ADDON,
KERNEL_IMAGE_TYPE_PE,
_KERNEL_IMAGE_TYPE_MAX,
_KERNEL_IMAGE_TYPE_INVALID = -EINVAL,

View File

@ -6,7 +6,6 @@
#include "log.h"
#include "pe-binary.h"
#include "string-util.h"
#include "uki.h"
bool pe_header_is_64bit(const PeHeader *h) {
assert(h);
@ -38,21 +37,22 @@ const IMAGE_DATA_DIRECTORY *pe_header_get_data_directory(
return PE_HEADER_OPTIONAL_FIELD(h, DataDirectory) + i;
}
const IMAGE_SECTION_HEADER *pe_section_table_find(
const IMAGE_SECTION_HEADER *pe_header_find_section(
const PeHeader *pe_header,
const IMAGE_SECTION_HEADER *sections,
size_t n_sections,
const char *name) {
size_t n;
assert(pe_header);
assert(name);
assert(sections || n_sections == 0);
assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
n = strlen(name);
if (n > sizeof(sections[0].Name)) /* Too long? */
return NULL;
FOREACH_ARRAY(section, sections, n_sections)
FOREACH_ARRAY(section, sections, le16toh(pe_header->pe.NumberOfSections))
if (memcmp(section->Name, name, n) == 0 &&
memeqzero(section->Name + n, sizeof(section->Name) - n))
return section;
@ -60,16 +60,6 @@ const IMAGE_SECTION_HEADER *pe_section_table_find(
return NULL;
}
const IMAGE_SECTION_HEADER* pe_header_find_section(
const PeHeader *pe_header,
const IMAGE_SECTION_HEADER *sections,
const char *name) {
assert(pe_header);
return pe_section_table_find(sections, le16toh(pe_header->pe.NumberOfSections), name);
}
int pe_load_headers(
int fd,
IMAGE_DOS_HEADER **ret_dos_header,
@ -183,28 +173,43 @@ int pe_load_sections(
int pe_read_section_data(
int fd,
const IMAGE_SECTION_HEADER *section,
const PeHeader *pe_header,
const IMAGE_SECTION_HEADER *sections,
const char *name,
size_t max_size,
void **ret,
size_t *ret_size) {
assert(fd >= 0);
assert(section);
const IMAGE_SECTION_HEADER *section;
_cleanup_free_ void *data = NULL;
size_t n;
ssize_t ss;
size_t n = le32toh(section->VirtualSize);
assert(fd >= 0);
assert(pe_header);
assert(sections || pe_header->pe.NumberOfSections == 0);
assert(name);
section = pe_header_find_section(pe_header, sections, name);
if (!section)
return -ENXIO;
n = le32toh(section->VirtualSize);
if (n > MIN(max_size, (size_t) SSIZE_MAX))
return -E2BIG;
_cleanup_free_ void *data = malloc(n+1);
data = malloc(n+1);
if (!data)
return -ENOMEM;
ssize_t ss = pread(fd, data, n, le32toh(section->PointerToRawData));
ss = pread(fd, data, n, le32toh(section->PointerToRawData));
if (ss < 0)
return -errno;
if ((size_t) ss != n)
return -EIO;
((uint8_t*) data)[n] = 0; /* NUL terminate, no matter what */
if (ret_size)
*ret_size = n;
else {
@ -216,37 +221,12 @@ int pe_read_section_data(
if (nul && !memeqzero(nul, n - (nul - (const char*) data))) /* If there's a NUL it must only be NULs from there on */
return -EBADMSG;
}
if (ret) {
((uint8_t*) data)[n] = 0; /* NUL terminate, no matter what */
if (ret)
*ret = TAKE_PTR(data);
}
return 0;
}
int pe_read_section_data_by_name(
int fd,
const PeHeader *pe_header,
const IMAGE_SECTION_HEADER *sections,
const char *name,
size_t max_size,
void **ret,
size_t *ret_size) {
const IMAGE_SECTION_HEADER *section;
assert(fd >= 0);
assert(pe_header);
assert(sections || pe_header->pe.NumberOfSections == 0);
assert(name);
section = pe_header_find_section(pe_header, sections, name);
if (!section)
return -ENXIO;
return pe_read_section_data(fd, section, max_size, ret, ret_size);
}
bool pe_is_uki(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections) {
assert(pe_header);
assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
@ -260,27 +240,3 @@ bool pe_is_uki(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections)
pe_header_find_section(pe_header, sections, ".osrel") &&
pe_header_find_section(pe_header, sections, ".linux");
}
bool pe_is_addon(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections) {
assert(pe_header);
assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
if (le16toh(pe_header->optional.Subsystem) != IMAGE_SUBSYSTEM_EFI_APPLICATION)
return false;
/* Add-ons do not have a Linux kernel, but do have either .cmdline or .dtb (currently) */
return !pe_header_find_section(pe_header, sections, ".linux") &&
(pe_header_find_section(pe_header, sections, ".cmdline") ||
pe_header_find_section(pe_header, sections, ".dtb") ||
pe_header_find_section(pe_header, sections, ".ucode"));
}
bool pe_is_native(const PeHeader *pe_header) {
assert(pe_header);
#ifdef _IMAGE_FILE_MACHINE_NATIVE
return le16toh(pe_header->pe.Machine) == _IMAGE_FILE_MACHINE_NATIVE;
#else
return false;
#endif
}

View File

@ -135,15 +135,10 @@ bool pe_header_is_64bit(const PeHeader *h);
const IMAGE_DATA_DIRECTORY *pe_header_get_data_directory(const PeHeader *h, size_t i);
const IMAGE_SECTION_HEADER *pe_header_find_section(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections, const char *name);
const IMAGE_SECTION_HEADER *pe_section_table_find(const IMAGE_SECTION_HEADER *sections, size_t n_sections, const char *name);
int pe_load_headers(int fd, IMAGE_DOS_HEADER **ret_dos_header, PeHeader **ret_pe_header);
int pe_load_sections(int fd, const IMAGE_DOS_HEADER *dos_header, const PeHeader *pe_header, IMAGE_SECTION_HEADER **ret_sections);
int pe_read_section_data(int fd, const IMAGE_SECTION_HEADER *section, size_t max_size, void **ret, size_t *ret_size);
int pe_read_section_data_by_name(int fd, const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections, const char *name, size_t max_size, void **ret, size_t *ret_size);
int pe_read_section_data(int fd, const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections, const char *name, size_t max_size, void **ret, size_t *ret_size);
bool pe_is_uki(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections);
bool pe_is_addon(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections);
bool pe_is_native(const PeHeader *pe_header);

View File

@ -942,7 +942,7 @@ int pty_forward_new(
(void) sd_event_source_set_description(f->master_event_source, "ptyfwd-master");
r = sd_event_add_signal(f->event, &f->sigwinch_event_source, SIGWINCH|SD_EVENT_SIGNAL_PROCMASK, on_sigwinch_event, f);
r = sd_event_add_signal(f->event, &f->sigwinch_event_source, SIGWINCH, on_sigwinch_event, f);
if (r < 0)
return r;

View File

@ -375,9 +375,6 @@ static void test_dns_name_is_valid_one(const char *s, int ret, int ret_ldh) {
}
TEST(dns_name_is_valid) {
test_dns_name_is_valid_one("[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[._qotd._tcp.local", 1, 0);
test_dns_name_is_valid_one("[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]._qotd._tcp.local", 0, 0);
test_dns_name_is_valid_one("foo", 1, 1);
test_dns_name_is_valid_one("foo.", 1, 1);
test_dns_name_is_valid_one("foo..", 0, 0);

View File

@ -1132,21 +1132,15 @@ int manager_new(Manager **ret) {
if (r < 0)
return r;
r = sd_event_set_signal_exit(m->event, true);
if (r < 0)
return r;
r = sd_event_add_signal(m->event, /* ret_event_source= */ NULL, (SIGRTMIN+18)|SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, /* userdata= */ NULL);
if (r < 0)
log_debug_errno(r, "Failed to install SIGRTMIN+18 signal handler, ignoring: %m");
(void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
(void) sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
(void) sd_event_add_signal(m->event, NULL, SIGRTMIN+18, sigrtmin18_handler, NULL);
r = sd_event_add_memory_pressure(m->event, NULL, NULL, NULL);
if (r < 0)
log_debug_errno(r, "Failed allocate memory pressure event source, ignoring: %m");
r = sd_event_set_watchdog(m->event, true);
if (r < 0)
log_debug_errno(r, "Failed to enable watchdog handling, ignoring: %m");
(void) sd_event_set_watchdog(m->event, true);
/* Load previous synchronization state */
r = access("/run/systemd/timesync/synchronized", F_OK);

View File

@ -179,6 +179,8 @@ static int run(int argc, char *argv[]) {
return log_error_errno(r, "Failed to drop privileges: %m");
}
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGRTMIN+18) >= 0);
r = manager_new(&m);
if (r < 0)
return log_error_errno(r, "Failed to allocate manager: %m");

View File

@ -184,13 +184,19 @@ static int run(int argc, char * argv[]) {
};
int r;
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT) >= 0);
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
r = sd_event_set_signal_exit(event, true);
r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to enable SIGTERM/SIGINT handling: %m");
return log_error_errno(r, "Failed to create sigterm event source: %m");
r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to create sigint event source: %m");
r = sd_event_set_watchdog(event, true);
if (r < 0)

View File

@ -170,8 +170,6 @@ typedef struct Item {
bool try_replace:1;
bool purge:1;
OperationMask done;
} Item;
@ -3048,9 +3046,6 @@ static int purge_item(Context *c, Item *i) {
if (!needs_purge(i->type))
return 0;
if (!i->purge)
return 0;
log_debug("Running purge action for entry %c %s", (char) i->type, i->path);
if (needs_glob(i->type))
@ -3607,7 +3602,7 @@ static int parse_line(
ItemArray *existing;
OrderedHashmap *h;
bool append_or_force = false, boot = false, allow_failure = false, try_replace = false,
unbase64 = false, from_cred = false, missing_user_or_group = false, purge = false;
unbase64 = false, from_cred = false, missing_user_or_group = false;
int r;
assert(fname);
@ -3673,8 +3668,6 @@ static int parse_line(
unbase64 = true;
else if (action[pos] == '^' && !from_cred)
from_cred = true;
else if (action[pos] == '$' && !purge)
purge = true;
else {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
@ -3691,7 +3684,6 @@ static int parse_line(
i.append_or_force = append_or_force;
i.allow_failure = allow_failure;
i.try_replace = try_replace;
i.purge = purge;
r = specifier_printf(path, PATH_MAX-1, specifier_table, arg_root, NULL, &i.path);
if (ERRNO_IS_NOINFO(r))
@ -3846,12 +3838,6 @@ static int parse_line(
"Unknown command type '%c'.", (char) i.type);
}
if (i.purge && !needs_purge(i.type)) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
"Purge flag '$' combined with line type '%c' which does not support purging.", (char) i.type);
}
if (!should_include_path(i.path))
return 0;

View File

@ -11,7 +11,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
int r;
char *value = UINT_TO_PTR(0x12345678U);
char *endpos = UINT_TO_PTR(0x87654321U);
bool is_case_sensitive;
fuzz_setup_logging();
@ -19,7 +18,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
memcpy(str, data, size);
str[size] = '\0';
r = udev_rule_parse_value(str, &value, &endpos, &is_case_sensitive);
r = udev_rule_parse_value(str, &value, &endpos);
if (r < 0) {
/* not modified on failure */
assert_se(value == UINT_TO_PTR(0x12345678U));

View File

@ -4,16 +4,15 @@
#include "tests.h"
#include "udev-rules.h"
static void test_udev_rule_parse_value_one(const char *in, const char *expected_value, bool expected_case_insensitive, int expected_retval) {
static void test_udev_rule_parse_value_one(const char *in, const char *expected_value, int expected_retval) {
_cleanup_free_ char *str = NULL;
char *value = UINT_TO_PTR(0x12345678U);
char *endpos = UINT_TO_PTR(0x87654321U);
bool i;
log_info("/* %s (%s, %s, %d) */", __func__, in, strnull(expected_value), expected_retval);
assert_se(str = strdup(in));
assert_se(udev_rule_parse_value(str, &value, &endpos, &i) == expected_retval);
assert_se(udev_rule_parse_value(str, &value, &endpos) == expected_retval);
if (expected_retval < 0) {
/* not modified on failure */
assert_se(value == UINT_TO_PTR(0x12345678U));
@ -26,7 +25,6 @@ static void test_udev_rule_parse_value_one(const char *in, const char *expected_
* so it could be safely interpreted as nulstr.
*/
assert_se(value[strlen(value) + 1] == '\0');
assert_se(i == expected_case_insensitive);
}
}
@ -35,61 +33,45 @@ TEST(udev_rule_parse_value) {
* parsed: valid operand
* use the following command to help generate textual C strings:
* python3 -c 'import json; print(json.dumps(input()))' */
test_udev_rule_parse_value_one("\"valid operand\"", "valid operand", /* case_insensitive = */ false, 0);
test_udev_rule_parse_value_one("\"valid operand\"", "valid operand", 0);
/* input: "va'l\'id\"op\"erand"
* parsed: va'l\'id"op"erand */
test_udev_rule_parse_value_one("\"va'l\\'id\\\"op\\\"erand\"", "va'l\\'id\"op\"erand", /* case_insensitive = */ false, 0);
test_udev_rule_parse_value_one("no quotes", NULL, /* case_insensitive = */ false, -EINVAL);
test_udev_rule_parse_value_one("\"\\\\a\\b\\x\\y\"", "\\\\a\\b\\x\\y", /* case_insensitive = */ false, 0);
test_udev_rule_parse_value_one("\"reject\0nul\"", NULL, /* case_insensitive = */ false, -EINVAL);
test_udev_rule_parse_value_one("\"va'l\\'id\\\"op\\\"erand\"", "va'l\\'id\"op\"erand", 0);
test_udev_rule_parse_value_one("no quotes", NULL, -EINVAL);
test_udev_rule_parse_value_one("\"\\\\a\\b\\x\\y\"", "\\\\a\\b\\x\\y", 0);
test_udev_rule_parse_value_one("\"reject\0nul\"", NULL, -EINVAL);
/* input: e"" */
test_udev_rule_parse_value_one("e\"\"", "", /* case_insensitive = */ false, 0);
test_udev_rule_parse_value_one("e\"\"", "", 0);
/* input: e"1234" */
test_udev_rule_parse_value_one("e\"1234\"", "1234", /* case_insensitive = */ false, 0);
test_udev_rule_parse_value_one("e\"1234\"", "1234", 0);
/* input: e"\"" */
test_udev_rule_parse_value_one("e\"\\\"\"", "\"", /* case_insensitive = */ false, 0);
test_udev_rule_parse_value_one("e\"\\\"\"", "\"", 0);
/* input: e"\ */
test_udev_rule_parse_value_one("e\"\\", NULL, /* case_insensitive = */ false, -EINVAL);
test_udev_rule_parse_value_one("e\"\\", NULL, -EINVAL);
/* input: e"\" */
test_udev_rule_parse_value_one("e\"\\\"", NULL, /* case_insensitive = */ false, -EINVAL);
test_udev_rule_parse_value_one("e\"\\\"", NULL, -EINVAL);
/* input: e"\\" */
test_udev_rule_parse_value_one("e\"\\\\\"", "\\", /* case_insensitive = */ false, 0);
test_udev_rule_parse_value_one("e\"\\\\\"", "\\", 0);
/* input: e"\\\" */
test_udev_rule_parse_value_one("e\"\\\\\\\"", NULL, /* case_insensitive = */ false, -EINVAL);
test_udev_rule_parse_value_one("e\"\\\\\\\"", NULL, -EINVAL);
/* input: e"\\\"" */
test_udev_rule_parse_value_one("e\"\\\\\\\"\"", "\\\"", /* case_insensitive = */ false, 0);
test_udev_rule_parse_value_one("e\"\\\\\\\"\"", "\\\"", 0);
/* input: e"\\\\" */
test_udev_rule_parse_value_one("e\"\\\\\\\\\"", "\\\\", /* case_insensitive = */ false, 0);
test_udev_rule_parse_value_one("e\"\\\\\\\\\"", "\\\\", 0);
/* input: e"operand with newline\n" */
test_udev_rule_parse_value_one("e\"operand with newline\\n\"", "operand with newline\n", /* case_insensitive = */ false, 0);
test_udev_rule_parse_value_one("e\"operand with newline\\n\"", "operand with newline\n", 0);
/* input: e"single\rcharacter\t\aescape\bsequence" */
test_udev_rule_parse_value_one(
"e\"single\\rcharacter\\t\\aescape\\bsequence\"", "single\rcharacter\t\aescape\bsequence", /* case_insensitive = */ false, 0);
"e\"single\\rcharacter\\t\\aescape\\bsequence\"", "single\rcharacter\t\aescape\bsequence", 0);
/* input: e"reject\invalid escape sequence" */
test_udev_rule_parse_value_one("e\"reject\\invalid escape sequence", NULL, /* case_insensitive = */ false, -EINVAL);
test_udev_rule_parse_value_one("e\"reject\\invalid escape sequence", NULL, -EINVAL);
/* input: e"\ */
test_udev_rule_parse_value_one("e\"\\", NULL, /* case_insensitive = */ false, -EINVAL);
test_udev_rule_parse_value_one("e\"\\", NULL, -EINVAL);
/* input: "s\u1d1c\u1d04\u029c \u1d1c\u0274\u026a\u1d04\u1d0f\u1d05\u1d07 \U0001d568\U0001d560\U0001d568" */
test_udev_rule_parse_value_one(
"e\"s\\u1d1c\\u1d04\\u029c \\u1d1c\\u0274\\u026a\\u1d04\\u1d0f\\u1d05\\u1d07 \\U0001d568\\U0001d560\\U0001d568\"",
"s\xe1\xb4\x9c\xe1\xb4\x84\xca\x9c \xe1\xb4\x9c\xc9\xb4\xc9\xaa\xe1\xb4\x84\xe1\xb4\x8f\xe1\xb4\x85\xe1\xb4\x87 \xf0\x9d\x95\xa8\xf0\x9d\x95\xa0\xf0\x9d\x95\xa8",
/* case_insensitive = */ false, 0);
/* input: i"ABCD1234" */
test_udev_rule_parse_value_one("i\"ABCD1234\"", "ABCD1234", /* case_insensitive = */ true, 0);
/* input: i"ABCD1234" */
test_udev_rule_parse_value_one("e\"ABCD1234\"", "ABCD1234", /* case_insensitive = */ false, 0);
/* input: ei"\\"ABCD1234 */
test_udev_rule_parse_value_one("ei\"\\\\ABCD1234\"", "\\ABCD1234", /* case_insensitive = */ true, 0);
/* input: ie"\\"ABCD1234 */
test_udev_rule_parse_value_one("ie\"\\\\ABCD1234\"", "\\ABCD1234", /* case_insensitive = */ true, 0);
/* input: i */
test_udev_rule_parse_value_one("i", NULL, /* case_insensitive = */ false, -EINVAL);
/* input: ee"" */
test_udev_rule_parse_value_one("ee\"\"", NULL, /* case_insensitive = */ false, -EINVAL);
/* input: iei"" */
test_udev_rule_parse_value_one("iei\"\"", NULL, /* case_insensitive = */ false, -EINVAL);
/* input: a"" */
test_udev_rule_parse_value_one("a\"\"", NULL, /* case_insensitive = */ false, -EINVAL);
0);
}
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -153,6 +153,8 @@ Manager* manager_free(Manager *manager) {
sd_event_source_unref(manager->inotify_event);
sd_event_source_unref(manager->kill_workers_event);
sd_event_source_unref(manager->memory_pressure_event_source);
sd_event_source_unref(manager->sigrtmin18_event_source);
sd_event_unref(manager->event);
free(manager->cgroup);
@ -1350,13 +1352,13 @@ int manager_main(Manager *manager) {
return log_error_errno(r, "Failed to create post event source: %m");
/* Eventually, we probably want to do more here on memory pressure, for example, kill idle workers immediately */
r = sd_event_add_memory_pressure(manager->event, /* ret_event_source= */ NULL, /* callback= */ NULL, /* userdata= */ NULL);
r = sd_event_add_memory_pressure(manager->event, &manager->memory_pressure_event_source, NULL, NULL);
if (r < 0)
log_full_errno(ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_PRIVILEGE(r) || (r == -EHOSTDOWN) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to allocate memory pressure watch, ignoring: %m");
r = sd_event_add_signal(manager->event, /* ret_event_source= */ NULL,
(SIGRTMIN+18) | SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, /* userdata= */ NULL);
r = sd_event_add_signal(manager->event, &manager->memory_pressure_event_source,
(SIGRTMIN+18) | SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, NULL);
if (r < 0)
return log_error_errno(r, "Failed to allocate SIGRTMIN+18 event source, ignoring: %m");

View File

@ -35,6 +35,9 @@ typedef struct Manager {
sd_event_source *kill_workers_event;
sd_event_source *memory_pressure_event_source;
sd_event_source *sigrtmin18_event_source;
usec_t last_usec;
ResolveNameTiming resolve_name_timing;

View File

@ -63,15 +63,9 @@ typedef enum {
MATCH_TYPE_GLOB_WITH_EMPTY, /* shell globs ?,*,[] with empty string, e.g., "|foo*" */
MATCH_TYPE_SUBSYSTEM, /* "subsystem", "bus", or "class" */
_MATCH_TYPE_MAX,
_MATCH_TYPE_MASK = (1 << 5) - 1,
MATCH_REMOVE_TRAILING_WHITESPACE = 1 << 5, /* Remove trailing whitespaces in attribute */
MATCH_CASE_INSENSITIVE = 1 << 6, /* string or pattern is matched case-insensitively */
_MATCH_TYPE_INVALID = -EINVAL,
_MATCH_TYPE_INVALID = -EINVAL,
} UdevRuleMatchType;
assert_cc(_MATCH_TYPE_MAX <= _MATCH_TYPE_MASK);
typedef enum {
SUBST_TYPE_PLAIN, /* no substitution */
SUBST_TYPE_FORMAT, /* % or $ */
@ -161,7 +155,8 @@ struct UdevRuleToken {
UdevRuleTokenType type:8;
UdevRuleOperatorType op:8;
UdevRuleMatchType match_type:8;
UdevRuleSubstituteType attr_subst_type:8;
UdevRuleSubstituteType attr_subst_type:7;
bool attr_match_remove_trailing_whitespace:1;
const char *value;
void *data;
@ -300,7 +295,6 @@ struct UdevRules {
#define log_line_invalid_op(line, key) _log_line_invalid_token(line, key, "operator")
#define log_line_invalid_attr(line, key) _log_line_invalid_token(line, key, "attribute")
#define log_line_invalid_prefix(line, key) _log_line_invalid_token(line, key, "prefix 'i'")
#define log_line_invalid_attr_format(line, key, attr, offset, hint) \
log_line_error_errno(line, SYNTHETIC_ERRNO(EINVAL), \
@ -494,10 +488,12 @@ static bool type_has_nulstr_value(UdevRuleTokenType type) {
return type < TK_M_TEST || type == TK_M_RESULT;
}
static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type, UdevRuleOperatorType op, char *value, void *data, bool is_case_insensitive) {
static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type, UdevRuleOperatorType op, char *value, void *data) {
_cleanup_(udev_rule_token_freep) UdevRuleToken *token = NULL;
UdevRuleMatchType match_type = _MATCH_TYPE_INVALID;
UdevRuleSubstituteType subst_type = _SUBST_TYPE_INVALID;
bool remove_trailing_whitespace = false;
size_t len;
assert(rule_line);
assert(type >= 0 && type < _TK_TYPE_MAX);
@ -556,21 +552,16 @@ static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type,
}
if (IN_SET(type, TK_M_ATTR, TK_M_PARENTS_ATTR)) {
size_t len;
assert(value);
assert(data);
assert(match_type >= 0 && match_type < _MATCH_TYPE_MAX);
len = strlen(value);
if (len > 0 && !isspace(value[len - 1]))
match_type |= MATCH_REMOVE_TRAILING_WHITESPACE;
remove_trailing_whitespace = true;
subst_type = rule_get_substitution_type(data);
}
SET_FLAG(match_type, MATCH_CASE_INSENSITIVE, is_case_insensitive);
token = new(UdevRuleToken, 1);
if (!token)
return -ENOMEM;
@ -582,6 +573,7 @@ static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type,
.data = data,
.match_type = match_type,
.attr_subst_type = subst_type,
.attr_match_remove_trailing_whitespace = remove_trailing_whitespace,
.rule_line = rule_line,
};
@ -629,7 +621,7 @@ static int check_attr_format_and_warn(UdevRuleLine *line, const char *key, const
return 0;
}
static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, UdevRuleOperatorType op, char *value, bool is_case_insensitive) {
static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, UdevRuleOperatorType op, char *value) {
ResolveNameTiming resolve_name_timing = LINE_GET_RULES(rule_line)->resolve_name_timing;
bool is_match = IN_SET(op, OP_MATCH, OP_NOMATCH);
int r;
@ -637,39 +629,35 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
assert(key);
assert(value);
if (!is_match && is_case_insensitive)
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
"Invalid prefix 'i' for '%s'. The 'i' prefix can be specified only for '==' or '!=' operator.", key);
if (streq(key, "ACTION")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
if (!is_match)
return log_line_invalid_op(rule_line, key);
r = rule_line_add_token(rule_line, TK_M_ACTION, op, value, NULL, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_ACTION, op, value, NULL);
} else if (streq(key, "DEVPATH")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
if (!is_match)
return log_line_invalid_op(rule_line, key);
r = rule_line_add_token(rule_line, TK_M_DEVPATH, op, value, NULL, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_DEVPATH, op, value, NULL);
} else if (streq(key, "KERNEL")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
if (!is_match)
return log_line_invalid_op(rule_line, key);
r = rule_line_add_token(rule_line, TK_M_KERNEL, op, value, NULL, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_KERNEL, op, value, NULL);
} else if (streq(key, "SYMLINK")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
if (!is_match) {
check_value_format_and_warn(rule_line, key, value, false);
r = rule_line_add_token(rule_line, TK_A_DEVLINK, op, value, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_DEVLINK, op, value, NULL);
} else
r = rule_line_add_token(rule_line, TK_M_DEVLINK, op, value, NULL, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_DEVLINK, op, value, NULL);
} else if (streq(key, "NAME")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
@ -689,9 +677,9 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
"Ignoring NAME=\"\", as udev will not delete any network interfaces.");
check_value_format_and_warn(rule_line, key, value, false);
r = rule_line_add_token(rule_line, TK_A_NAME, op, value, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_NAME, op, value, NULL);
} else
r = rule_line_add_token(rule_line, TK_M_NAME, op, value, NULL, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_NAME, op, value, NULL);
} else if (streq(key, "ENV")) {
if (isempty(attr))
return log_line_invalid_attr(rule_line, key);
@ -709,15 +697,15 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
check_value_format_and_warn(rule_line, key, value, false);
r = rule_line_add_token(rule_line, TK_A_ENV, op, value, attr, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_ENV, op, value, attr);
} else
r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr);
} else if (streq(key, "CONST")) {
if (isempty(attr) || !STR_IN_SET(attr, "arch", "virt"))
return log_line_invalid_attr(rule_line, key);
if (!is_match)
return log_line_invalid_op(rule_line, key);
r = rule_line_add_token(rule_line, TK_M_CONST, op, value, attr, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_CONST, op, value, attr);
} else if (streq(key, "TAG")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
@ -729,9 +717,9 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
if (!is_match) {
check_value_format_and_warn(rule_line, key, value, true);
r = rule_line_add_token(rule_line, TK_A_TAG, op, value, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_TAG, op, value, NULL);
} else
r = rule_line_add_token(rule_line, TK_M_TAG, op, value, NULL, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_TAG, op, value, NULL);
} else if (streq(key, "SUBSYSTEM")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
@ -741,14 +729,14 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
if (STR_IN_SET(value, "bus", "class"))
log_line_warning(rule_line, "\"%s\" must be specified as \"subsystem\".", value);
r = rule_line_add_token(rule_line, TK_M_SUBSYSTEM, op, value, NULL, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_SUBSYSTEM, op, value, NULL);
} else if (streq(key, "DRIVER")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
if (!is_match)
return log_line_invalid_op(rule_line, key);
r = rule_line_add_token(rule_line, TK_M_DRIVER, op, value, NULL, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_DRIVER, op, value, NULL);
} else if (streq(key, "ATTR")) {
r = check_attr_format_and_warn(rule_line, key, attr);
if (r < 0)
@ -762,9 +750,9 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
if (!is_match) {
check_value_format_and_warn(rule_line, key, value, false);
r = rule_line_add_token(rule_line, TK_A_ATTR, op, value, attr, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_ATTR, op, value, attr);
} else
r = rule_line_add_token(rule_line, TK_M_ATTR, op, value, attr, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_ATTR, op, value, attr);
} else if (streq(key, "SYSCTL")) {
r = check_attr_format_and_warn(rule_line, key, attr);
if (r < 0)
@ -778,30 +766,30 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
if (!is_match) {
check_value_format_and_warn(rule_line, key, value, false);
r = rule_line_add_token(rule_line, TK_A_SYSCTL, op, value, attr, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_SYSCTL, op, value, attr);
} else
r = rule_line_add_token(rule_line, TK_M_SYSCTL, op, value, attr, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_SYSCTL, op, value, attr);
} else if (streq(key, "KERNELS")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
if (!is_match)
return log_line_invalid_op(rule_line, key);
r = rule_line_add_token(rule_line, TK_M_PARENTS_KERNEL, op, value, NULL, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_PARENTS_KERNEL, op, value, NULL);
} else if (streq(key, "SUBSYSTEMS")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
if (!is_match)
return log_line_invalid_op(rule_line, key);
r = rule_line_add_token(rule_line, TK_M_PARENTS_SUBSYSTEM, op, value, NULL, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_PARENTS_SUBSYSTEM, op, value, NULL);
} else if (streq(key, "DRIVERS")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
if (!is_match)
return log_line_invalid_op(rule_line, key);
r = rule_line_add_token(rule_line, TK_M_PARENTS_DRIVER, op, value, NULL, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_PARENTS_DRIVER, op, value, NULL);
} else if (streq(key, "ATTRS")) {
r = check_attr_format_and_warn(rule_line, key, attr);
if (r < 0)
@ -814,14 +802,14 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
if (strstr(attr, "../"))
log_line_warning(rule_line, "Direct reference to parent sysfs directory, may break in future kernels.");
r = rule_line_add_token(rule_line, TK_M_PARENTS_ATTR, op, value, attr, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_PARENTS_ATTR, op, value, attr);
} else if (streq(key, "TAGS")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
if (!is_match)
return log_line_invalid_op(rule_line, key);
r = rule_line_add_token(rule_line, TK_M_PARENTS_TAG, op, value, NULL, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_PARENTS_TAG, op, value, NULL);
} else if (streq(key, "TEST")) {
mode_t mode = MODE_INVALID;
@ -833,10 +821,8 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
check_value_format_and_warn(rule_line, key, value, true);
if (!is_match)
return log_line_invalid_op(rule_line, key);
if (is_case_insensitive)
return log_line_invalid_prefix(rule_line, key);
r = rule_line_add_token(rule_line, TK_M_TEST, op, value, MODE_TO_PTR(mode), is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_TEST, op, value, MODE_TO_PTR(mode));
} else if (streq(key, "PROGRAM")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
@ -845,10 +831,8 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
return log_line_invalid_op(rule_line, key);
if (!is_match)
op = OP_MATCH;
if (is_case_insensitive)
return log_line_invalid_prefix(rule_line, key);
r = rule_line_add_token(rule_line, TK_M_PROGRAM, op, value, NULL, /* is_case_insensitive */ false);
r = rule_line_add_token(rule_line, TK_M_PROGRAM, op, value, NULL);
} else if (streq(key, "IMPORT")) {
if (isempty(attr))
return log_line_invalid_attr(rule_line, key);
@ -857,20 +841,18 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
return log_line_invalid_op(rule_line, key);
if (!is_match)
op = OP_MATCH;
if (is_case_insensitive)
return log_line_invalid_prefix(rule_line, key);
if (streq(attr, "file"))
r = rule_line_add_token(rule_line, TK_M_IMPORT_FILE, op, value, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_M_IMPORT_FILE, op, value, NULL);
else if (streq(attr, "program")) {
UdevBuiltinCommand cmd;
cmd = udev_builtin_lookup(value);
if (cmd >= 0) {
log_line_debug(rule_line, "Found builtin command '%s' for %s, replacing attribute.", value, key);
r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd));
} else
r = rule_line_add_token(rule_line, TK_M_IMPORT_PROGRAM, op, value, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_M_IMPORT_PROGRAM, op, value, NULL);
} else if (streq(attr, "builtin")) {
UdevBuiltinCommand cmd;
@ -878,13 +860,13 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
if (cmd < 0)
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
"Unknown builtin command: %s", value);
r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd));
} else if (streq(attr, "db"))
r = rule_line_add_token(rule_line, TK_M_IMPORT_DB, op, value, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_M_IMPORT_DB, op, value, NULL);
else if (streq(attr, "cmdline"))
r = rule_line_add_token(rule_line, TK_M_IMPORT_CMDLINE, op, value, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_M_IMPORT_CMDLINE, op, value, NULL);
else if (streq(attr, "parent"))
r = rule_line_add_token(rule_line, TK_M_IMPORT_PARENT, op, value, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_M_IMPORT_PARENT, op, value, NULL);
else
return log_line_invalid_attr(rule_line, key);
} else if (streq(key, "RESULT")) {
@ -893,7 +875,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
if (!is_match)
return log_line_invalid_op(rule_line, key);
r = rule_line_add_token(rule_line, TK_M_RESULT, op, value, NULL, is_case_insensitive);
r = rule_line_add_token(rule_line, TK_M_RESULT, op, value, NULL);
} else if (streq(key, "OPTIONS")) {
char *tmp;
@ -905,24 +887,24 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
op = OP_ASSIGN;
if (streq(value, "string_escape=none"))
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_NONE, op, NULL, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_NONE, op, NULL, NULL);
else if (streq(value, "string_escape=replace"))
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_REPLACE, op, NULL, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_REPLACE, op, NULL, NULL);
else if (streq(value, "db_persist"))
r = rule_line_add_token(rule_line, TK_A_OPTIONS_DB_PERSIST, op, NULL, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_OPTIONS_DB_PERSIST, op, NULL, NULL);
else if (streq(value, "watch"))
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(1), /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(1));
else if (streq(value, "nowatch"))
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(0), /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(0));
else if ((tmp = startswith(value, "static_node=")))
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STATIC_NODE, op, tmp, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STATIC_NODE, op, tmp, NULL);
else if ((tmp = startswith(value, "link_priority="))) {
int prio;
r = safe_atoi(tmp, &prio);
if (r < 0)
return log_line_error_errno(rule_line, r, "Failed to parse link priority '%s': %m", tmp);
r = rule_line_add_token(rule_line, TK_A_OPTIONS_DEVLINK_PRIORITY, op, NULL, INT_TO_PTR(prio), /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_OPTIONS_DEVLINK_PRIORITY, op, NULL, INT_TO_PTR(prio));
} else if ((tmp = startswith(value, "log_level="))) {
int level;
@ -933,7 +915,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
if (level < 0)
return log_line_error_errno(rule_line, level, "Failed to parse log level '%s': %m", tmp);
}
r = rule_line_add_token(rule_line, TK_A_OPTIONS_LOG_LEVEL, op, NULL, INT_TO_PTR(level), /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_OPTIONS_LOG_LEVEL, op, NULL, INT_TO_PTR(level));
} else {
log_line_warning(rule_line, "Invalid value for OPTIONS key, ignoring: '%s'", value);
return 0;
@ -951,17 +933,17 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
}
if (parse_uid(value, &uid) >= 0)
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid), /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid));
else if (resolve_name_timing == RESOLVE_NAME_EARLY &&
rule_get_substitution_type(value) == SUBST_TYPE_PLAIN) {
r = rule_resolve_user(rule_line, value, &uid);
if (r < 0)
return log_line_error_errno(rule_line, r, "Failed to resolve user name '%s': %m", value);
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid), /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid));
} else if (resolve_name_timing != RESOLVE_NAME_NEVER) {
check_value_format_and_warn(rule_line, key, value, true);
r = rule_line_add_token(rule_line, TK_A_OWNER, op, value, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_OWNER, op, value, NULL);
} else {
log_line_debug(rule_line, "User name resolution is disabled, ignoring %s=\"%s\".", key, value);
return 0;
@ -979,17 +961,17 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
}
if (parse_gid(value, &gid) >= 0)
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid), /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid));
else if (resolve_name_timing == RESOLVE_NAME_EARLY &&
rule_get_substitution_type(value) == SUBST_TYPE_PLAIN) {
r = rule_resolve_group(rule_line, value, &gid);
if (r < 0)
return log_line_error_errno(rule_line, r, "Failed to resolve group name '%s': %m", value);
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid), /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid));
} else if (resolve_name_timing != RESOLVE_NAME_NEVER) {
check_value_format_and_warn(rule_line, key, value, true);
r = rule_line_add_token(rule_line, TK_A_GROUP, op, value, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_GROUP, op, value, NULL);
} else {
log_line_debug(rule_line, "Resolving group name is disabled, ignoring GROUP=\"%s\".", value);
return 0;
@ -1007,10 +989,10 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
}
if (parse_mode(value, &mode) >= 0)
r = rule_line_add_token(rule_line, TK_A_MODE_ID, op, NULL, MODE_TO_PTR(mode), /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_MODE_ID, op, NULL, MODE_TO_PTR(mode));
else {
check_value_format_and_warn(rule_line, key, value, true);
r = rule_line_add_token(rule_line, TK_A_MODE, op, value, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_MODE, op, value, NULL);
}
} else if (streq(key, "SECLABEL")) {
if (isempty(attr))
@ -1023,13 +1005,13 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
op = OP_ASSIGN;
}
r = rule_line_add_token(rule_line, TK_A_SECLABEL, op, value, attr, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_SECLABEL, op, value, attr);
} else if (streq(key, "RUN")) {
if (is_match || op == OP_REMOVE)
return log_line_invalid_op(rule_line, key);
check_value_format_and_warn(rule_line, key, value, true);
if (!attr || streq(attr, "program"))
r = rule_line_add_token(rule_line, TK_A_RUN_PROGRAM, op, value, NULL, /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_RUN_PROGRAM, op, value, NULL);
else if (streq(attr, "builtin")) {
UdevBuiltinCommand cmd;
@ -1037,7 +1019,7 @@ static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, Ude
if (cmd < 0)
return log_line_error_errno(rule_line, SYNTHETIC_ERRNO(EINVAL),
"Unknown builtin command '%s', ignoring.", value);
r = rule_line_add_token(rule_line, TK_A_RUN_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false);
r = rule_line_add_token(rule_line, TK_A_RUN_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd));
} else
return log_line_invalid_attr(rule_line, key);
} else if (streq(key, "GOTO")) {
@ -1137,30 +1119,13 @@ static void check_token_delimiters(UdevRuleLine *rule_line, const char *line) {
}
}
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos, bool *ret_is_case_insensitive) {
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) {
char *i, *j;
bool is_escaped = false, is_case_insensitive = false;
assert(str);
assert(ret_value);
assert(ret_endpos);
assert(ret_is_case_insensitive);
/* check if string is prefixed with:
* - "e" for escaped
* - "i" for case insensitive match
*
* Note both e and i can be set but do not allow duplicates ("eei", "eii"). */
for (const char *k = str; *k != '"' && k < str + 2; k++)
if (*k == 'e' && !is_escaped)
is_escaped = true;
else if (*k == 'i' && !is_case_insensitive)
is_case_insensitive = true;
else
return -EINVAL;
bool is_escaped;
/* value must be double quotated */
str += is_escaped + is_case_insensitive;
is_escaped = str[0] == 'e';
str += is_escaped;
if (str[0] != '"')
return -EINVAL;
@ -1207,11 +1172,10 @@ int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos, bool *
*ret_value = str;
*ret_endpos = i + 1;
*ret_is_case_insensitive = is_case_insensitive;
return 0;
}
static int parse_line(char **line, char **ret_key, char **ret_attr, UdevRuleOperatorType *ret_op, char **ret_value, bool *ret_is_case_insensitive) {
static int parse_line(char **line, char **ret_key, char **ret_attr, UdevRuleOperatorType *ret_op, char **ret_value) {
char *key_begin, *key_end, *attr, *tmp;
UdevRuleOperatorType op;
int r;
@ -1221,7 +1185,6 @@ static int parse_line(char **line, char **ret_key, char **ret_attr, UdevRuleOper
assert(ret_key);
assert(ret_op);
assert(ret_value);
assert(ret_is_case_insensitive);
key_begin = skip_leading_chars(*line, WHITESPACE ",");
@ -1256,7 +1219,7 @@ static int parse_line(char **line, char **ret_key, char **ret_attr, UdevRuleOper
tmp += op == OP_ASSIGN ? 1 : 2;
tmp = skip_leading_chars(tmp, NULL);
r = udev_rule_parse_value(tmp, ret_value, line, ret_is_case_insensitive);
r = udev_rule_parse_value(tmp, ret_value, line);
if (r < 0)
return r;
@ -1328,18 +1291,17 @@ static int rule_add_line(UdevRuleFile *rule_file, const char *line_str, unsigned
for (p = rule_line->line; !isempty(p); ) {
char *key, *attr, *value;
UdevRuleOperatorType op;
bool is_case_insensitive;
if (extra_checks)
check_token_delimiters(rule_line, p);
r = parse_line(&p, &key, &attr, &op, &value, &is_case_insensitive);
r = parse_line(&p, &key, &attr, &op, &value);
if (r < 0)
return log_line_error_errno(rule_line, r, "Invalid key/value pair, ignoring.");
if (r == 0)
break;
r = parse_token(rule_line, key, attr, op, value, is_case_insensitive);
r = parse_token(rule_line, key, attr, op, value);
if (r < 0)
return r;
}
@ -1450,6 +1412,7 @@ static bool tokens_eq(const UdevRuleToken *a, const UdevRuleToken *b) {
assert(b);
return a->attr_subst_type == b->attr_subst_type &&
a->attr_match_remove_trailing_whitespace == b->attr_match_remove_trailing_whitespace &&
token_type_and_value_eq(a, b) &&
token_type_and_data_eq(a, b);
}
@ -1464,6 +1427,7 @@ static bool nulstr_tokens_conflict(const UdevRuleToken *a, const UdevRuleToken *
a->op == OP_MATCH &&
a->match_type == b->match_type &&
a->attr_subst_type == b->attr_subst_type &&
a->attr_match_remove_trailing_whitespace == b->attr_match_remove_trailing_whitespace &&
token_type_and_data_eq(a, b)))
return false;
@ -1732,7 +1696,7 @@ bool udev_rules_should_reload(UdevRules *rules) {
static bool token_match_string(UdevRuleToken *token, const char *str) {
const char *value;
bool match = false, case_insensitive;
bool match = false;
assert(token);
assert(token->value);
@ -1740,17 +1704,13 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
str = strempty(str);
value = token->value;
case_insensitive = FLAGS_SET(token->match_type, MATCH_CASE_INSENSITIVE);
switch (token->match_type & _MATCH_TYPE_MASK) {
switch (token->match_type) {
case MATCH_TYPE_EMPTY:
match = isempty(str);
break;
case MATCH_TYPE_SUBSYSTEM:
if (case_insensitive)
match = STRCASE_IN_SET(str, "subsystem", "class", "bus");
else
match = STR_IN_SET(str, "subsystem", "class", "bus");
match = STR_IN_SET(str, "subsystem", "class", "bus");
break;
case MATCH_TYPE_PLAIN_WITH_EMPTY:
if (isempty(str)) {
@ -1760,7 +1720,7 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
_fallthrough_;
case MATCH_TYPE_PLAIN:
NULSTR_FOREACH(i, value)
if (case_insensitive ? strcaseeq(i, str) : streq(i, str)) {
if (streq(i, str)) {
match = true;
break;
}
@ -1773,7 +1733,7 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
_fallthrough_;
case MATCH_TYPE_GLOB:
NULSTR_FOREACH(i, value)
if ((fnmatch(i, str, case_insensitive ? FNM_CASEFOLD : 0) == 0)) {
if ((fnmatch(i, str, 0) == 0)) {
match = true;
break;
}
@ -1813,7 +1773,7 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev
return false;
/* remove trailing whitespace, if not asked to match for it */
if (FLAGS_SET(token->match_type, MATCH_REMOVE_TRAILING_WHITESPACE)) {
if (token->attr_match_remove_trailing_whitespace) {
strscpy(vbuf, sizeof(vbuf), value);
value = delete_trailing_chars(vbuf, NULL);
}
@ -1825,7 +1785,7 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev
return false;
/* remove trailing whitespace, if not asked to match for it */
if (FLAGS_SET(token->match_type, MATCH_REMOVE_TRAILING_WHITESPACE))
if (token->attr_match_remove_trailing_whitespace)
delete_trailing_chars(vbuf, NULL);
return token_match_string(token, vbuf);

View File

@ -29,7 +29,7 @@ typedef enum ResolveNameTiming {
_RESOLVE_NAME_TIMING_INVALID = -EINVAL,
} ResolveNameTiming;
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos, bool *ret_is_case_insensitive);
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos);
int udev_rules_parse_file(UdevRules *rules, const char *filename, bool extra_checks, UdevRuleFile **ret);
unsigned udev_rule_file_get_issues(UdevRuleFile *rule_file);
UdevRules* udev_rules_new(ResolveNameTiming resolve_name_timing);

View File

@ -208,11 +208,9 @@ int monitor_main(int argc, char *argv[], void *userdata) {
goto finalize;
}
r = sd_event_set_signal_exit(event, true);
if (r < 0) {
log_error_errno(r, "Failed to install SIGINT/SIGTERM handling: %m");
goto finalize;
}
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT) >= 0);
(void) sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
(void) sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
printf("monitor will print the received events for:\n");
if (arg_print_udev) {

View File

@ -2124,7 +2124,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
return r;
}
assert_se(sigprocmask_many(SIG_BLOCK, /* ret_old_mask=*/ NULL, SIGCHLD) >= 0);
assert_se(sigprocmask_many(SIG_BLOCK, /* old_sigset=*/ NULL, SIGCHLD, SIGWINCH) >= 0);
_cleanup_(sd_event_source_unrefp) sd_event_source *notify_event_source = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;

View File

@ -2313,17 +2313,6 @@ SUBSYSTEMS=="scsi", PROGRAM=="/bin/sh -c \"printf %%s 'foo1 foo2' | grep 'foo1 f
SUBSYSTEM=="block", SUBSYSTEMS=="scsi", KERNEL=="sd*", SYMLINK+="blockdev"
KERNEL=="sda6", OPTIONS+="link_priority=10"
"""),
Rules.new(
"case insensitive match",
Device(
"/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1",
exp_links = ["ok"],
),
rules = r"""
KERNEL==i"SDA1", SUBSYSTEMS==i"SCSI", ATTRS{vendor}==i"a?a", SYMLINK+="ok"
"""),
]
def fork_and_run_udev(action: str, rules: Rules) -> None:

View File

@ -237,8 +237,6 @@ test_syntax_error 'ENV=="b"' 'Invalid attribute for ENV.'
test_syntax_error 'ENV{a}-="b"' 'Invalid operator for ENV.'
test_syntax_error 'ENV{a}:="b"' "ENV key takes '==', '!=', '=', or '+=' operator, assuming '='."
test_syntax_error 'ENV{ACTION}="b"' "Invalid ENV attribute. 'ACTION' cannot be set."
test_syntax_error 'ENV{a}=i"b"' "Invalid prefix 'i' for 'ENV'. The 'i' prefix can be specified only for '==' or '!=' operator."
test_syntax_error 'ENV{a}+=i"b"' "Invalid prefix 'i' for 'ENV'. The 'i' prefix can be specified only for '==' or '!=' operator."
test_syntax_error 'CONST=="b"' 'Invalid attribute for CONST.'
test_syntax_error 'CONST{a}=="b"' 'Invalid attribute for CONST.'
test_syntax_error 'CONST{arch}="b"' 'Invalid operator for CONST.'
@ -277,12 +275,10 @@ test_syntax_error 'TEST{0644}="b"' 'Invalid operator for TEST.'
test_syntax_error 'PROGRAM{a}=="b"' 'Invalid attribute for PROGRAM.'
test_syntax_error 'PROGRAM-="b"' 'Invalid operator for PROGRAM.'
test_syntax_error 'PROGRAM=="%", NAME="b"' 'Invalid value "%" for PROGRAM (char 1: invalid substitution type), ignoring.'
test_syntax_error 'PROGRAM==i"b"' "Invalid prefix 'i' for PROGRAM."
test_syntax_error 'IMPORT="b"' 'Invalid attribute for IMPORT.'
test_syntax_error 'IMPORT{a}="b"' 'Invalid attribute for IMPORT.'
test_syntax_error 'IMPORT{a}-="b"' 'Invalid operator for IMPORT.'
test_syntax_error 'IMPORT{file}=="%", NAME="b"' 'Invalid value "%" for IMPORT (char 1: invalid substitution type), ignoring.'
test_syntax_error 'IMPORT{file}==i"a", NAME="b"' "Invalid prefix 'i' for IMPORT."
test_syntax_error 'IMPORT{builtin}!="foo"' 'Unknown builtin command: foo'
test_syntax_error 'RESULT{a}=="b"' 'Invalid attribute for RESULT.'
test_syntax_error 'RESULT:="b"' 'Invalid operator for RESULT.'

View File

@ -9,39 +9,26 @@ set -o pipefail
export SYSTEMD_LOG_LEVEL=debug
c='
d$ /tmp/somedir
f$ /tmp/somedir/somefile - - - - baz
f /tmp/someotherfile - - - - qux
d /tmp/somedir
f /tmp/somedir/somefile - - - - baz
'
systemd-tmpfiles --create - <<<"$c"
test -f /tmp/somedir/somefile
grep -q baz /tmp/somedir/somefile
grep -q qux /tmp/someotherfile
systemd-tmpfiles --purge --dry-run - <<<"$c"
test -f /tmp/somedir/somefile
grep -q baz /tmp/somedir/somefile
grep -q qux /tmp/someotherfile
systemd-tmpfiles --purge - <<<"$c"
test ! -f /tmp/somedir/somefile
test ! -d /tmp/somedir/
grep -q qux /tmp/someotherfile
systemd-tmpfiles --create --purge --dry-run - <<<"$c"
test ! -f /tmp/somedir/somefile
test ! -d /tmp/somedir/
grep -q qux /tmp/someotherfile
systemd-tmpfiles --create --purge - <<<"$c"
test -f /tmp/somedir/somefile
grep -q baz /tmp/somedir/somefile
grep -q qux /tmp/someotherfile
systemd-tmpfiles --purge - <<<"$c"
test ! -f /tmp/somedir/somefile
test ! -d /tmp/somedir/
grep -q qux /tmp/someotherfile
rm /tmp/someotherfile

View File

@ -1,152 +0,0 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
# shellcheck disable=SC2233,SC2235
set -eux
set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
# Arrays cannot be exported, so redefine in each test script
ARGS=()
if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
# If we're running under sanitizers, we need to use a less restrictive
# profile, otherwise LSan syscall would get blocked by seccomp
ARGS+=(--profile=trusted)
fi
unsquashfs -dest /tmp/minimal_0 /usr/share/minimal_0.raw
unsquashfs -dest /tmp/minimal_1 /usr/share/minimal_1.raw
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0
systemctl is-active minimal-app0.service
systemctl is-active minimal-app0-foo.service
systemctl is-active minimal-app0-bar.service && exit 1
portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 minimal-app0
systemctl is-active minimal-app0.service
systemctl is-active minimal-app0-bar.service
systemctl is-active minimal-app0-foo.service && exit 1
portablectl list | grep -q -F "minimal_1"
busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
portablectl detach --now --enable --runtime /tmp/minimal_1 minimal-app0
portablectl list | grep -q -F "No images."
busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
mount /tmp/app0.raw /tmp/app0
mount /tmp/app1.raw /tmp/app1
mount /usr/share/minimal_0.raw /tmp/rootdir
# Fix up os-release to drop the valid PORTABLE_SERVICES field (because we are
# bypassing the sysext logic in portabled here it will otherwise not see the
# extensions additional valid prefix)
grep -v "^PORTABLE_PREFIXES=" /tmp/rootdir/etc/os-release >/tmp/os-release-fix/etc/os-release
mount -t overlay overlay -o lowerdir=/tmp/os-release-fix:/tmp/app1:/tmp/rootdir /tmp/overlay
grep . /tmp/overlay/usr/lib/extension-release.d/*
grep . /tmp/overlay/etc/os-release
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/overlay app1
systemctl is-active app1.service
portablectl detach --now --runtime overlay app1
# Ensure --force works also when symlinking
mkdir -p /run/systemd/system.attached/app1.service.d
cat <<EOF >/run/systemd/system.attached/app1.service
[Unit]
Description=App 1
EOF
cat <<EOF >/run/systemd/system.attached/app1.service.d/10-profile.conf
[Unit]
Description=App 1
EOF
cat <<EOF >/run/systemd/system.attached/app1.service.d/20-portable.conf
[Unit]
Description=App 1
EOF
systemctl daemon-reload
portablectl "${ARGS[@]}" attach --force --copy=symlink --now --runtime /tmp/overlay app1
systemctl is-active app1.service
portablectl detach --now --runtime overlay app1
umount /tmp/overlay
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
systemctl is-active app0.service
systemctl is-active app1.service
portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/rootdir/usr/lib/os-release
portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0
portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2
portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/systemd/system/app1.service
portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/systemd/system/app0.service
grep -q -F "LogExtraFields=PORTABLE=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app1.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app1.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app1.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app1.service.d/20-portable.conf
portablectl detach --clean --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
# Ensure --clean remove state and other directories belonging to the portable image being detached
test ! -d /var/lib/app0
test ! -d /run/app0
# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned)
portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
test -d /run/portables/app0
test -d /run/portables/app1
test -d /run/portables/rootdir
test -f /run/systemd/system.attached/app0.service
test -f /run/systemd/system.attached/app1.service
test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
test -L /run/systemd/system.attached/app1.service.d/10-profile.conf
portablectl detach --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
# Attempt to disable the app unit during detaching. Requires --copy=symlink to reproduce.
# Provides coverage for https://github.com/systemd/systemd/issues/23481
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0
portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0
# attach and detach again to check if all drop-in configs are removed even if the main unit files are removed
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0
portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0
# The wrong file should be ignored, given the right one has the xattr set
trap 'rm -rf /var/cache/wrongext' EXIT
mkdir -p /var/cache/wrongext/usr/lib/extension-release.d /var/cache/wrongext/usr/lib/systemd/system/
echo "[Service]" > /var/cache/wrongext/usr/lib/systemd/system/app0.service
touch /var/cache/wrongext/usr/lib/extension-release.d/extension-release.wrongext_somethingwrong.txt
cp /tmp/rootdir/usr/lib/os-release /var/cache/wrongext/usr/lib/extension-release.d/extension-release.app0
setfattr -n user.extension-release.strict -v "false" /var/cache/wrongext/usr/lib/extension-release.d/extension-release.app0
portablectl "${ARGS[@]}" attach --runtime --extension /var/cache/wrongext /tmp/rootdir app0
status="$(portablectl is-attached --extension wrongext rootdir)"
[[ "${status}" == "attached-runtime" ]]
portablectl detach --runtime --extension /var/cache/wrongext /tmp/rootdir app0
umount /tmp/rootdir
umount /tmp/app0
umount /tmp/app1

View File

@ -1,240 +0,0 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
# shellcheck disable=SC2233,SC2235
set -eux
set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
# Arrays cannot be exported, so redefine in each test script
ARGS=()
if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
# If we're running under sanitizers, we need to use a less restrictive
# profile, otherwise LSan syscall would get blocked by seccomp
ARGS+=(--profile=trusted)
fi
portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw minimal-app0
portablectl is-attached minimal-app0
portablectl inspect /usr/share/minimal_0.raw minimal-app0.service
systemctl is-active minimal-app0.service
systemctl is-active minimal-app0-foo.service
systemctl is-active minimal-app0-bar.service && exit 1
portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0
portablectl is-attached minimal-app0
portablectl inspect /usr/share/minimal_0.raw minimal-app0.service
systemctl is-active minimal-app0.service
systemctl is-active minimal-app0-bar.service
systemctl is-active minimal-app0-foo.service && exit 1
portablectl list | grep -q -F "minimal_1"
busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
portablectl detach --now --runtime /usr/share/minimal_1.raw minimal-app0
portablectl list | grep -q -F "No images."
busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
# Ensure we don't regress (again) when using --force
mkdir -p /run/systemd/system.attached/minimal-app0.service.d/
cat <<EOF >/run/systemd/system.attached/minimal-app0.service
[Unit]
Description=Minimal App 0
EOF
cat <<EOF >/run/systemd/system.attached/minimal-app0.service.d/10-profile.conf
[Unit]
Description=Minimal App 0
EOF
cat <<EOF >/run/systemd/system.attached/minimal-app0.service.d/20-portable.conf
[Unit]
Description=Minimal App 0
EOF
systemctl daemon-reload
portablectl "${ARGS[@]}" attach --force --now --runtime /usr/share/minimal_0.raw minimal-app0
portablectl is-attached --force minimal-app0
portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service
systemctl is-active minimal-app0.service
systemctl is-active minimal-app0-foo.service
systemctl is-active minimal-app0-bar.service && exit 1
portablectl "${ARGS[@]}" reattach --force --now --runtime /usr/share/minimal_1.raw minimal-app0
portablectl is-attached --force minimal-app0
portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service
systemctl is-active minimal-app0.service
systemctl is-active minimal-app0-bar.service
systemctl is-active minimal-app0-foo.service && exit 1
portablectl list | grep -q -F "minimal_1"
busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
portablectl detach --force --now --runtime /usr/share/minimal_1.raw minimal-app0
portablectl list | grep -q -F "No images."
busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
systemctl is-active app0.service
status="$(portablectl is-attached --extension app0 minimal_0)"
[[ "${status}" == "running-runtime" ]]
grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0
systemctl is-active app0.service
status="$(portablectl is-attached --extension app0 minimal_1)"
[[ "${status}" == "running-runtime" ]]
grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_1.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
portablectl detach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0
# Ensure versioned images are accepted without needing to use --force to override the extension-release
# matching
cp /tmp/app0.raw /tmp/app0_1.0.raw
portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_0.raw app0
systemctl is-active app0.service
status="$(portablectl is-attached --extension app0_1 minimal_0)"
[[ "${status}" == "running-runtime" ]]
portablectl detach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_1.raw app0
rm -f /tmp/app0_1.0.raw
portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
systemctl is-active app1.service
status="$(portablectl is-attached --extension app1 minimal_0)"
[[ "${status}" == "running-runtime" ]]
# Ensure that adding or removing a version to the image doesn't break reattaching
cp /tmp/app1.raw /tmp/app1_2.raw
portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1_2.raw /usr/share/minimal_1.raw app1
systemctl is-active app1.service
status="$(portablectl is-attached --extension app1_2 minimal_1)"
[[ "${status}" == "running-runtime" ]]
portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1
systemctl is-active app1.service
status="$(portablectl is-attached --extension app1 minimal_1)"
[[ "${status}" == "running-runtime" ]]
portablectl detach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1
portablectl "${ARGS[@]}" attach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
systemctl daemon-reload
systemctl restart app1.service
systemctl is-active app1.service
status="$(portablectl is-attached --extension app1 minimal_0)"
[[ "${status}" == "running-runtime" ]]
portablectl detach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
# Ensure vpick works, including reattaching to a new image
mkdir -p /tmp/app1.v/
cp /tmp/app1.raw /tmp/app1.v/app1_1.0.raw
cp /tmp/app1_2.raw /tmp/app1.v/app1_2.0.raw
portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
systemctl is-active app1.service
status="$(portablectl is-attached --extension app1_2.0.raw minimal_1)"
[[ "${status}" == "running-runtime" ]]
rm -f /tmp/app1.v/app1_2.0.raw
portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
systemctl is-active app1.service
status="$(portablectl is-attached --extension app1_1.0.raw minimal_1)"
[[ "${status}" == "running-runtime" ]]
portablectl detach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_0.raw app1
rm -f /tmp/app1.v/app1_1.0.raw
# Ensure that the combination of read-only images, state directory and dynamic user works, and that
# state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while
# after the service is attached before the file appears.
grep -q -F bar "${STATE_DIRECTORY}/app0/foo"
grep -q -F baz "${STATE_DIRECTORY}/app1/foo"
# Ensure that we can override the check on extension-release.NAME
cp /tmp/app0.raw /tmp/app10.raw
portablectl "${ARGS[@]}" attach --force --now --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0
systemctl is-active app0.service
status="$(portablectl is-attached --extension /tmp/app10.raw /usr/share/minimal_0.raw)"
[[ "${status}" == "running-runtime" ]]
portablectl inspect --force --cat --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/app10.raw"
# Ensure that we can detach even when an image has been deleted already (stop the unit manually as
# portablectl won't find it)
rm -f /tmp/app10.raw
systemctl stop app0.service
portablectl detach --force --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0
# portablectl also accepts confexts
portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0
systemctl is-active app0.service
status="$(portablectl is-attached --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw)"
[[ "${status}" == "running-runtime" ]]
portablectl inspect --force --cat --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/conf0.raw"
portablectl detach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0
# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned)
portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
test -f /run/portables/app0.raw
test -f /run/portables/minimal_0.raw
test -f /run/systemd/system.attached/app0.service
test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
# Ensure that when two portables share the same base image, removing one doesn't remove the other too
portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
status="$(portablectl is-attached --extension app0 minimal_0)"
[[ "${status}" == "attached-runtime" ]]
status="$(portablectl is-attached --extension app1 minimal_0)"
[[ "${status}" == "attached-runtime" ]]
(! portablectl detach --runtime /usr/share/minimal_0.raw app)
status="$(portablectl is-attached --extension app0 minimal_0)"
[[ "${status}" == "attached-runtime" ]]
status="$(portablectl is-attached --extension app1 minimal_0)"
[[ "${status}" == "attached-runtime" ]]
# Ensure 'portablectl list' shows the correct status for both images
portablectl list
portablectl list | grep -F "minimal_0" | grep -q -F "attached-runtime"
portablectl list | grep -F "app0" | grep -q -F "attached-runtime"
portablectl list | grep -F "app1" | grep -q -F "attached-runtime"
portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app
status="$(portablectl is-attached --extension app1 minimal_0)"
[[ "${status}" == "attached-runtime" ]]
portablectl detach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app

View File

@ -5,9 +5,6 @@
set -eux
set -o pipefail
# shellcheck source=test/units/test-control.sh
. "$(dirname "$0")"/test-control.sh
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
@ -21,14 +18,9 @@ DefaultEnvironment=SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
ManagerEnvironment=SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
EOF
mkdir -p /run/systemd/system/systemd-portabled.service.d/
cat <<EOF >/run/systemd/system/systemd-portabled.service.d/override.conf
[Service]
Environment=SYSTEMD_LOG_LEVEL=debug
EOF
systemctl daemon-reexec
export SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
udevadm control --log-level debug
@ -41,11 +33,6 @@ if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
# With the trusted profile DynamicUser is disabled, so the storage is not in private/
STATE_DIRECTORY=/var/lib/
fi
export STATE_DIRECTORY
export SYSTEMD_LOG_LEVEL=debug
export SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
# Quick smoke tests
systemd-dissect --no-pager /usr/share/minimal_0.raw | grep -q '✓ portable service'
systemd-dissect --no-pager /usr/share/minimal_1.raw | grep -q '✓ portable service'
@ -53,6 +40,373 @@ systemd-dissect --no-pager /tmp/app0.raw | grep -q '✓ sysext for portable serv
systemd-dissect --no-pager /tmp/app1.raw | grep -q '✓ sysext for portable service'
systemd-dissect --no-pager /tmp/conf0.raw | grep -q '✓ confext for portable service'
export SYSTEMD_LOG_LEVEL=debug
mkdir -p /run/systemd/system/systemd-portabled.service.d/
cat <<EOF >/run/systemd/system/systemd-portabled.service.d/override.conf
[Service]
Environment=SYSTEMD_LOG_LEVEL=debug
EOF
portablectl "${ARGS[@]}" attach --now --runtime /usr/share/minimal_0.raw minimal-app0
portablectl is-attached minimal-app0
portablectl inspect /usr/share/minimal_0.raw minimal-app0.service
systemctl is-active minimal-app0.service
systemctl is-active minimal-app0-foo.service
systemctl is-active minimal-app0-bar.service && exit 1
portablectl "${ARGS[@]}" reattach --now --runtime /usr/share/minimal_1.raw minimal-app0
portablectl is-attached minimal-app0
portablectl inspect /usr/share/minimal_0.raw minimal-app0.service
systemctl is-active minimal-app0.service
systemctl is-active minimal-app0-bar.service
systemctl is-active minimal-app0-foo.service && exit 1
portablectl list | grep -q -F "minimal_1"
busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
portablectl detach --now --runtime /usr/share/minimal_1.raw minimal-app0
portablectl list | grep -q -F "No images."
busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
# Ensure we don't regress (again) when using --force
mkdir -p /run/systemd/system.attached/minimal-app0.service.d/
cat <<EOF >/run/systemd/system.attached/minimal-app0.service
[Unit]
Description=Minimal App 0
EOF
cat <<EOF >/run/systemd/system.attached/minimal-app0.service.d/10-profile.conf
[Unit]
Description=Minimal App 0
EOF
cat <<EOF >/run/systemd/system.attached/minimal-app0.service.d/20-portable.conf
[Unit]
Description=Minimal App 0
EOF
systemctl daemon-reload
portablectl "${ARGS[@]}" attach --force --now --runtime /usr/share/minimal_0.raw minimal-app0
portablectl is-attached --force minimal-app0
portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service
systemctl is-active minimal-app0.service
systemctl is-active minimal-app0-foo.service
systemctl is-active minimal-app0-bar.service && exit 1
portablectl "${ARGS[@]}" reattach --force --now --runtime /usr/share/minimal_1.raw minimal-app0
portablectl is-attached --force minimal-app0
portablectl inspect --force /usr/share/minimal_0.raw minimal-app0.service
systemctl is-active minimal-app0.service
systemctl is-active minimal-app0-bar.service
systemctl is-active minimal-app0-foo.service && exit 1
portablectl list | grep -q -F "minimal_1"
busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
portablectl detach --force --now --runtime /usr/share/minimal_1.raw minimal-app0
portablectl list | grep -q -F "No images."
busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
# portablectl also works with directory paths rather than images
unsquashfs -dest /tmp/minimal_0 /usr/share/minimal_0.raw
unsquashfs -dest /tmp/minimal_1 /usr/share/minimal_1.raw
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/minimal_0 minimal-app0
systemctl is-active minimal-app0.service
systemctl is-active minimal-app0-foo.service
systemctl is-active minimal-app0-bar.service && exit 1
portablectl "${ARGS[@]}" reattach --now --enable --runtime /tmp/minimal_1 minimal-app0
systemctl is-active minimal-app0.service
systemctl is-active minimal-app0-bar.service
systemctl is-active minimal-app0-foo.service && exit 1
portablectl list | grep -q -F "minimal_1"
busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1'
portablectl detach --now --enable --runtime /tmp/minimal_1 minimal-app0
portablectl list | grep -q -F "No images."
busctl tree org.freedesktop.portable1 --no-pager | grep -q -F '/org/freedesktop/portable1/image/minimal_5f1' && exit 1
portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
systemctl is-active app0.service
status="$(portablectl is-attached --extension app0 minimal_0)"
[[ "${status}" == "running-runtime" ]]
grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0
systemctl is-active app0.service
status="$(portablectl is-attached --extension app0 minimal_1)"
[[ "${status}" == "running-runtime" ]]
grep -q -F "LogExtraFields=PORTABLE_ROOT=minimal_1.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
portablectl detach --now --runtime --extension /tmp/app0.raw /usr/share/minimal_1.raw app0
# Ensure versioned images are accepted without needing to use --force to override the extension-release
# matching
cp /tmp/app0.raw /tmp/app0_1.0.raw
portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_0.raw app0
systemctl is-active app0.service
status="$(portablectl is-attached --extension app0_1 minimal_0)"
[[ "${status}" == "running-runtime" ]]
portablectl detach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_1.raw app0
rm -f /tmp/app0_1.0.raw
portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
systemctl is-active app1.service
status="$(portablectl is-attached --extension app1 minimal_0)"
[[ "${status}" == "running-runtime" ]]
# Ensure that adding or removing a version to the image doesn't break reattaching
cp /tmp/app1.raw /tmp/app1_2.raw
portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1_2.raw /usr/share/minimal_1.raw app1
systemctl is-active app1.service
status="$(portablectl is-attached --extension app1_2 minimal_1)"
[[ "${status}" == "running-runtime" ]]
portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1
systemctl is-active app1.service
status="$(portablectl is-attached --extension app1 minimal_1)"
[[ "${status}" == "running-runtime" ]]
portablectl detach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_1.raw app1
portablectl "${ARGS[@]}" attach --force --no-reload --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
systemctl daemon-reload
systemctl restart app1.service
systemctl is-active app1.service
status="$(portablectl is-attached --extension app1 minimal_0)"
[[ "${status}" == "running-runtime" ]]
portablectl detach --now --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
# Ensure vpick works, including reattaching to a new image
mkdir -p /tmp/app1.v/
cp /tmp/app1.raw /tmp/app1.v/app1_1.0.raw
cp /tmp/app1_2.raw /tmp/app1.v/app1_2.0.raw
portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
systemctl is-active app1.service
status="$(portablectl is-attached --extension app1_2.0.raw minimal_1)"
[[ "${status}" == "running-runtime" ]]
rm -f /tmp/app1.v/app1_2.0.raw
portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1
systemctl is-active app1.service
status="$(portablectl is-attached --extension app1_1.0.raw minimal_1)"
[[ "${status}" == "running-runtime" ]]
portablectl detach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_0.raw app1
rm -f /tmp/app1.v/app1_1.0.raw
# Ensure that the combination of read-only images, state directory and dynamic user works, and that
# state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while
# after the service is attached before the file appears.
grep -q -F bar "${STATE_DIRECTORY}/app0/foo"
grep -q -F baz "${STATE_DIRECTORY}/app1/foo"
# Ensure that we can override the check on extension-release.NAME
cp /tmp/app0.raw /tmp/app10.raw
portablectl "${ARGS[@]}" attach --force --now --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0
systemctl is-active app0.service
status="$(portablectl is-attached --extension /tmp/app10.raw /usr/share/minimal_0.raw)"
[[ "${status}" == "running-runtime" ]]
portablectl inspect --force --cat --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/app10.raw"
# Ensure that we can detach even when an image has been deleted already (stop the unit manually as
# portablectl won't find it)
rm -f /tmp/app10.raw
systemctl stop app0.service
portablectl detach --force --runtime --extension /tmp/app10.raw /usr/share/minimal_0.raw app0
# portablectl also accepts confexts
portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0
systemctl is-active app0.service
status="$(portablectl is-attached --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw)"
[[ "${status}" == "running-runtime" ]]
portablectl inspect --force --cat --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0 | grep -q -F "Extension Release: /tmp/conf0.raw"
portablectl detach --now --runtime --extension /tmp/app0.raw --extension /tmp/conf0.raw /usr/share/minimal_0.raw app0
# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned)
portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
test -f /run/portables/app0.raw
test -f /run/portables/minimal_0.raw
test -f /run/systemd/system.attached/app0.service
test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
# Ensure that when two portables share the same base image, removing one doesn't remove the other too
portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
portablectl "${ARGS[@]}" attach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app1
status="$(portablectl is-attached --extension app0 minimal_0)"
[[ "${status}" == "attached-runtime" ]]
status="$(portablectl is-attached --extension app1 minimal_0)"
[[ "${status}" == "attached-runtime" ]]
(! portablectl detach --runtime /usr/share/minimal_0.raw app)
status="$(portablectl is-attached --extension app0 minimal_0)"
[[ "${status}" == "attached-runtime" ]]
status="$(portablectl is-attached --extension app1 minimal_0)"
[[ "${status}" == "attached-runtime" ]]
# Ensure 'portablectl list' shows the correct status for both images
portablectl list
portablectl list | grep -F "minimal_0" | grep -q -F "attached-runtime"
portablectl list | grep -F "app0" | grep -q -F "attached-runtime"
portablectl list | grep -F "app1" | grep -q -F "attached-runtime"
portablectl detach --runtime --extension /tmp/app0.raw /usr/share/minimal_0.raw app
status="$(portablectl is-attached --extension app1 minimal_0)"
[[ "${status}" == "attached-runtime" ]]
portablectl detach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app
# portablectl also works with directory paths rather than images
mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
mount /tmp/app0.raw /tmp/app0
mount /tmp/app1.raw /tmp/app1
mount /usr/share/minimal_0.raw /tmp/rootdir
# Fix up os-release to drop the valid PORTABLE_SERVICES field (because we are
# bypassing the sysext logic in portabled here it will otherwise not see the
# extensions additional valid prefix)
grep -v "^PORTABLE_PREFIXES=" /tmp/rootdir/etc/os-release >/tmp/os-release-fix/etc/os-release
mount -t overlay overlay -o lowerdir=/tmp/os-release-fix:/tmp/app1:/tmp/rootdir /tmp/overlay
grep . /tmp/overlay/usr/lib/extension-release.d/*
grep . /tmp/overlay/etc/os-release
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/overlay app1
systemctl is-active app1.service
portablectl detach --now --runtime overlay app1
# Ensure --force works also when symlinking
mkdir -p /run/systemd/system.attached/app1.service.d
cat <<EOF >/run/systemd/system.attached/app1.service
[Unit]
Description=App 1
EOF
cat <<EOF >/run/systemd/system.attached/app1.service.d/10-profile.conf
[Unit]
Description=App 1
EOF
cat <<EOF >/run/systemd/system.attached/app1.service.d/20-portable.conf
[Unit]
Description=App 1
EOF
systemctl daemon-reload
portablectl "${ARGS[@]}" attach --force --copy=symlink --now --runtime /tmp/overlay app1
systemctl is-active app1.service
portablectl detach --now --runtime overlay app1
umount /tmp/overlay
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
systemctl is-active app0.service
systemctl is-active app1.service
portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/rootdir/usr/lib/os-release
portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0
portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2
portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/systemd/system/app1.service
portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/systemd/system/app0.service
grep -q -F "LogExtraFields=PORTABLE=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app0.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_ROOT=rootdir" /run/systemd/system.attached/app1.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app0" /run/systemd/system.attached/app1.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd/system.attached/app1.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION=app1" /run/systemd/system.attached/app1.service.d/20-portable.conf
grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app_1" /run/systemd/system.attached/app1.service.d/20-portable.conf
portablectl detach --clean --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
# Ensure --clean remove state and other directories belonging to the portable image being detached
test ! -d /var/lib/app0
test ! -d /run/app0
# Ensure that mixed mode copies the images and units (client-owned) but symlinks the profile (OS owned)
portablectl "${ARGS[@]}" attach --copy=mixed --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
test -d /run/portables/app0
test -d /run/portables/app1
test -d /run/portables/rootdir
test -f /run/systemd/system.attached/app0.service
test -f /run/systemd/system.attached/app1.service
test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
test -L /run/systemd/system.attached/app1.service.d/10-profile.conf
portablectl detach --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
# Attempt to disable the app unit during detaching. Requires --copy=symlink to reproduce.
# Provides coverage for https://github.com/systemd/systemd/issues/23481
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0
portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0
# attach and detach again to check if all drop-in configs are removed even if the main unit files are removed
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0
portablectl detach --now --runtime --enable /tmp/rootdir minimal-app0
# The wrong file should be ignored, given the right one has the xattr set
trap 'rm -rf /var/cache/wrongext' EXIT
mkdir -p /var/cache/wrongext/usr/lib/extension-release.d /var/cache/wrongext/usr/lib/systemd/system/
echo "[Service]" > /var/cache/wrongext/usr/lib/systemd/system/app0.service
touch /var/cache/wrongext/usr/lib/extension-release.d/extension-release.wrongext_somethingwrong.txt
cp /tmp/rootdir/usr/lib/os-release /var/cache/wrongext/usr/lib/extension-release.d/extension-release.app0
setfattr -n user.extension-release.strict -v "false" /var/cache/wrongext/usr/lib/extension-release.d/extension-release.app0
portablectl "${ARGS[@]}" attach --runtime --extension /var/cache/wrongext /tmp/rootdir app0
status="$(portablectl is-attached --extension wrongext rootdir)"
[[ "${status}" == "attached-runtime" ]]
portablectl detach --runtime --extension /var/cache/wrongext /tmp/rootdir app0
umount /tmp/rootdir
umount /tmp/app0
umount /tmp/app1
# Lack of ID field in os-release should be rejected, but it caused a crash in the past instead
mkdir -p /tmp/emptyroot/usr/lib
mkdir -p /tmp/emptyext/usr/lib/extension-release.d
@ -63,8 +417,4 @@ touch /tmp/emptyext/usr/lib/extension-release.d/extension-release.emptyext
res="$(! portablectl attach --extension /tmp/emptyext /tmp/emptyroot 2> >(grep "Remote peer disconnected"))"
test -z "${res}"
: "Run subtests"
run_subtests
touch /testok

View File

@ -427,15 +427,14 @@ systemctl is-active testservice-50e.service
# Check vpick support in ExtensionImages=
VBASE="vtest$RANDOM"
VDIR="/tmp/$VBASE.v"
EMPTY_VDIR="/tmp/$VBASE-empty.v"
mkdir "$VDIR" "$EMPTY_VDIR"
mkdir "$VDIR"
ln -s /tmp/app0.raw "$VDIR/${VBASE}_0.raw"
ln -s /tmp/app1.raw "$VDIR/${VBASE}_1.raw"
systemd-run -P -p ExtensionImages="$VDIR -$EMPTY_VDIR" bash -c '/opt/script1.sh | grep ID'
systemd-run -P -p ExtensionImages="$VDIR" bash -c '/opt/script1.sh | grep ID'
rm -rf "$VDIR" "$EMPTY_VDIR"
rm -rf "$VDIR"
# ExtensionDirectories will set up an overlay
mkdir -p "$IMAGE_DIR/app0" "$IMAGE_DIR/app1" "$IMAGE_DIR/app-nodistro" "$IMAGE_DIR/service-scoped-test"
@ -503,15 +502,14 @@ systemctl is-active testservice-50f.service
# Check vpick support in ExtensionDirectories=
VBASE="vtest$RANDOM"
VDIR="/tmp/$VBASE.v"
EMPTY_VDIR="/tmp/$VBASE-empty.v"
mkdir "$VDIR" "$EMPTY_VDIR"
mkdir "$VDIR"
ln -s "$IMAGE_DIR/app0" "$VDIR/${VBASE}_0"
ln -s "$IMAGE_DIR/app1" "$VDIR/${VBASE}_1"
systemd-run -P --property ExtensionDirectories="$VDIR -$EMPTY_VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2"
systemd-run -P --property ExtensionDirectories="$VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2"
rm -rf "$VDIR" "$EMPTY_VDIR"
rm -rf "$VDIR"
systemd-dissect --umount "$IMAGE_DIR/app0"
systemd-dissect --umount "$IMAGE_DIR/app1"

View File

@ -8,5 +8,5 @@
# See tmpfiles.d(5) for details
{% if LINK_SHELL_EXTRA_DROPIN %}
L$ {{SHELLPROFILEDIR}}/70-systemd-shell-extra.sh - - - - {{LIBEXECDIR}}/profile.d/70-systemd-shell-extra.sh
L {{SHELLPROFILEDIR}}/70-systemd-shell-extra.sh - - - - {{LIBEXECDIR}}/profile.d/70-systemd-shell-extra.sh
{% endif %}

View File

@ -8,7 +8,7 @@
# See tmpfiles.d(5) for details
{% if LINK_SSH_PROXY_DROPIN %}
L$ {{SSHCONFDIR}}/20-systemd-ssh-proxy.conf - - - - {{LIBEXECDIR}}/ssh_config.d/20-systemd-ssh-proxy.conf
L {{SSHCONFDIR}}/20-systemd-ssh-proxy.conf - - - - {{LIBEXECDIR}}/ssh_config.d/20-systemd-ssh-proxy.conf
{% endif %}
{% if CREATE_SSHDPRIVSEPDIR %}
d {{SSHDPRIVSEPDIR}} 0755

View File

@ -13,7 +13,7 @@
d /run/lock 0755 root root -
L /var/lock - - - - ../run/lock
{% if CREATE_LOG_DIRS %}
L$ /var/log/README - - - - ../..{{DOC_DIR}}/README.logs
L /var/log/README - - - - ../..{{DOC_DIR}}/README.logs
{% endif %}
# /run/lock/subsys is used for serializing SysV service execution, and

View File

@ -7,7 +7,7 @@
# See tmpfiles.d(5) for details
d$ /run/systemd/netif 0755 systemd-network systemd-network -
d$ /run/systemd/netif/links 0755 systemd-network systemd-network -
d$ /run/systemd/netif/leases 0755 systemd-network systemd-network -
d$ /var/lib/systemd/network 0755 systemd-network systemd-network -
d /run/systemd/netif 0755 systemd-network systemd-network -
d /run/systemd/netif/links 0755 systemd-network systemd-network -
d /run/systemd/netif/leases 0755 systemd-network systemd-network -
d /var/lib/systemd/network 0755 systemd-network systemd-network -

View File

@ -19,5 +19,5 @@ Q /var/lib/machines 0700 - - -
# systemd-nspawn --ephemeral places snapshots) we are more strict, to
# avoid removing unrelated temporary files.
R!$ /var/lib/machines/.#*
R!$ /.#machine.*
R! /var/lib/machines/.#*
R! /.#machine.*

View File

@ -14,10 +14,10 @@ x /var/tmp/systemd-private-%b-*
X /var/tmp/systemd-private-%b-*/tmp
# Remove top-level private temporary directories on each boot
R!$ /tmp/systemd-private-*
R!$ /var/tmp/systemd-private-*
R! /tmp/systemd-private-*
R! /var/tmp/systemd-private-*
# Handle lost systemd-coredump temp files. They could be lost on old filesystems,
# for example, after hard reboot.
x /var/lib/systemd/coredump/.#core*.%b*
r!$ /var/lib/systemd/coredump/.#*
r! /var/lib/systemd/coredump/.#*

View File

@ -13,11 +13,11 @@ f+! /run/utmp 0664 root utmp -
{% endif %}
d /run/systemd/ask-password 0755 root root -
d$ /run/systemd/seats 0755 root root -
d$ /run/systemd/sessions 0755 root root -
d$ /run/systemd/users 0755 root root -
d /run/systemd/seats 0755 root root -
d /run/systemd/sessions 0755 root root -
d /run/systemd/users 0755 root root -
d /run/systemd/machines 0755 root root -
d$ /run/systemd/shutdown 0755 root root -
d /run/systemd/shutdown 0755 root root -
d /run/log 0755 root root -