Compare commits

...

68 Commits

Author SHA1 Message Date
Lennart Poettering 92e38817d0 man: update PASSWORD_AGENTS spec, and introduce unpriv pw queries
Fixes: #1232 #2217
2024-09-18 10:55:03 +02:00
Lennart Poettering c6ffcaa82e ask-password-tool: add --user/--system flag to systemd-ask-password tool
This allows selecting which agents to ask about this: system-level
agents, or per-user agents.

Fixes: #1232 #2217
2024-09-18 10:55:03 +02:00
Lennart Poettering 510480a1ca ask-password-api: add support for querying pws from unpriv agents 2024-09-18 10:55:03 +02:00
Lennart Poettering db3d573e3a ask-password-api: minor modernizations 2024-09-18 10:47:13 +02:00
Lennart Poettering 476117a71d tty-ask-password-agent: support for watching both system-wide and per-user askpw dir
Fixes: #1232 #2217
2024-09-18 10:47:13 +02:00
Lennart Poettering 8500234805 tty-ask-password-agent: minor modernizations 2024-09-18 10:47:13 +02:00
Lennart Poettering 9a92548dc5 core: modernize askpw handling a bit 2024-09-18 10:47:13 +02:00
Daan De Meyer 81af8f998e repart: Support specifying multiple directories to ExcludeFiles= 2024-09-18 10:22:33 +02:00
chenjiayi 4fc8a63f9e systemd: rewatch pids under cgroup v1 when sigchld of processes more than main pid and control pid is captured
If `Delegate` is configured in service, cgroup agent will never send out
any datagram as .control subcgroup is generated. Thus systemd will watch
all processes on the cgroup hierarchy for SIGCHLD to deal with unreliable
cgroup notifications.

In this way, systemd should rewatch all processes when any SIGCHLD is
captured, more than the control pid or main pid.
2024-09-18 10:13:20 +02:00
Jason Yundt dfb3155419 man: document ShowStatus and SetShowStatus()
SetShowStatus() was added in order to fix #11447. Recently, I ran into
the exact same problem that OP was experiencing in #11447. I wasn’t able
to figure out how to deal with the problem until I found #11447, and it
took me a while to find #11447.

This commit takes what I learned from reading #11447 and adds it to the
documentation. Hopefully, this will make it easier for other people who
run into the same problem in the future.
2024-09-18 10:11:55 +02:00
Daan De Meyer fc5037e7d7
Merge pull request #34464 from yuwata/test-space-in-path
test: allow to run tests under directory that contains spaces
2024-09-18 08:50:38 +02:00
Yu Watanabe 13f6ec7ce7 test: quote paths to executables
Fixes #34459.
2024-09-18 09:47:04 +09:00
Yu Watanabe 6e1816ef16 kernel-install: unquote plugin paths in KERNEL_INSTALL_PLUGINS
To support the case that paths to plugins contain spaces.

Prompted by #34459
2024-09-18 09:47:00 +09:00
Yu Watanabe 7ac1ad90d0
Merge pull request #34460 from yuwata/test-86-follow-ups
test: follow-ups for TEST-86
2024-09-18 09:31:17 +09:00
Daan De Meyer 099b16c3e7 tmpfiles.d: Remove purge flag from lines that don't support it
Fixes db15657dfb
2024-09-17 23:02:01 +02:00
Daan De Meyer 7a7f306b6c ukify: Remove debug log
This prints a python data structure which we shouldn't do during
normal operation.
2024-09-17 22:34:13 +02:00
Yu Watanabe 4f2975385f
Merge pull request #34040 from AdrianVovk/repart-dollar-boot
repart: Implement $BOOT support
2024-09-18 05:09:20 +09:00
Daan De Meyer 0432e28394
Merge pull request #34440 from yuwata/network-log-no-matching-network
network: log when no matching .network file found
2024-09-17 21:09:19 +02:00
Yu Watanabe fc956a3973 network/dhcp4: use device_get_property_bool() at link_needs_dhcp_broadcast()
No functional change, just refactoring.
2024-09-17 21:03:59 +02:00
Yu Watanabe d265b8afb7 test: drop unused test.sh for TEST-86-MULTI-PROFILE-UKI
The test cannot run with the bash test runner, as it requires python.
Hence, test.sh is not necessary.

Follow-up for a37640653c.
2024-09-18 04:00:05 +09:00
Yu Watanabe 1aab0a5b10 test: minor coding style fixlets
Follow-up for a37640653c.
2024-09-18 03:50:46 +09:00
Yu Watanabe b0dbb4aa3a
Merge pull request #34457 from poettering/uki-with-many-testcase
multi-profile UKIs: test case
2024-09-18 03:48:45 +09:00
Michael Ferrari 91ea3dcf35 homed: wait for user input during firstboot
This mirrors the behavior of `systemd-firstboot` and allows bootup
messages to settle down before user input is actually processed.

See: https://github.com/systemd/systemd/issues/34448
2024-09-18 03:21:11 +09:00
Yu Watanabe a95ae2d36a conf-parser: use hashmap_ensure_put() at one more place 2024-09-18 03:13:47 +09:00
Yu Watanabe be8e4b1a87 conf-parser: log errors in config_parse_many_files() and friends
Previously, if an file cannot be opened, e.g. due to its permission,
config_parse_many() or so did not log the error even if CONFIG_PARSE_WARN
flag is set. This makes all error paths in these functions are logged,
and the log level is controlled by the flag.

Prompted by #34436.
2024-09-18 03:13:25 +09:00
Adrian Vovk cf612c5fd5
repart: Add tests for supplement partitions 2024-09-17 14:06:51 -04:00
Adrian Vovk 2cb9c68c3a
repart: Add SupplementFor= logic
This was designed to deal with $BOOT, as defined by the Boot Loader
Specification, but it was made a generic mechanism because it is useful
elsewhere too. See the updated man page for usage examples, motivation,
and an explanation of how this works.
2024-09-17 14:06:50 -04:00
Adrian Vovk 78e9059208
repart: Consider existing partitions when placing
Fixes an oversight in `context_allocate_partitions` that makes it
succeed in cases where it should fail. Essentially, there was nothing
actually enforcing SizeMinBytes= and PaddingMinBytes= for partitions
that exist, only for new partitions. This behavior is inconsistent with
the docs, which state that existing partitions will be grown to at least
the specified minimum size, and that "If the backing device does not
provide enough space to fulfill the constraints placing the partition
will fail".
2024-09-17 14:06:49 -04:00
Adrian Vovk e671bdc5c3
strv: Fixup STRV_FOREACH_PAIR macro
The macro didn't properly parenthesize a caller-controlled argument.
For example: `STRV_FOREACH_PAIR(a, b, something ?: something_else)`
would expand to `typeof(*something ?: something_else)`, which would
cause compile failures
2024-09-17 14:06:26 -04:00
Yu Watanabe 572d031eca log: introduce log_oom_full() 2024-09-18 02:50:19 +09:00
Yu Watanabe 25da422bd1 network: log loaded .network and .netdev files 2024-09-18 02:35:28 +09:00
Yu Watanabe 5872ea7008 network: log when no matching .network file found
When an interface enters unmanaged state, there are two possibilities:
- no matching .network file found,
- found a matching .network with Unmanaged=yes.

When a matching .network file is found, networkd logs the filename.
Let's also log when no matching .network file is found.

This also slightly adjust the log message when a matching .network file
found.

Closes #34436.
2024-09-18 02:27:13 +09:00
PavlNekrasov d80a9042ca
Use correct error code in log message in output_waiting_jobs (#34404)
The error code `r` from the read function is being logged, but the error code `rc` from the table data insertion function should be logged instead.
2024-09-17 19:17:21 +09:00
Yu Watanabe a7afe5a3e7
Merge pull request #34443 from yuwata/network-sysctl-monitor-follow-ups
network/sysctl-monitor: several follow-ups and cleanups
2024-09-17 19:15:12 +09:00
Lennart Poettering a2369d0224 update TODO 2024-09-17 10:40:51 +02:00
Lennart Poettering a37640653c ci: add testcase for multi-profile UKIs
This tests the whole shebang:

1. That ukify can generate them properly
2. That systemd-boot can dissect them properly
3. That systemd-stub can accept profile selection propery
4. That the profile information ends up in /run/systemd/stub/ properly
5. That systemd-measure correctly calculates the expected PCR 11 values
   for each profile and that we can unlock a public-key bound LUKS
   volume with it
2024-09-17 10:40:51 +02:00
Yu Watanabe a65b864835 docs: fix typo in filename: REATLIME -> REALTIME 2024-09-17 10:21:54 +02:00
Yu Watanabe 9959681a0d test/repart: fix mkfs checker
Follow-up for 27cacec939.
2024-09-17 10:15:21 +02:00
Daan De Meyer b3ebd480d6 Fix generator logging
log_setup() overrides the previously set log target again so we
can't use it in log_setup_generator().

Follow-up for aa976d8788
2024-09-17 15:10:39 +09:00
Arian van Putten 6695ff4c15 CONTROL_GROUP_INTERFACE: fix link to systemd-run code 2024-09-17 15:09:48 +09:00
Yu Watanabe 4d6ad22f8d network: drop unnecessary BPF related objects from Manager when disabled 2024-09-17 15:00:06 +09:00
Yu Watanabe 099ee34ca1 network/sysctl-monitor: do not allocate sysctl_shadow when eBPF is not supported
When eBPF is disabled, the hashmap will be never used. Let's not
allocate it.
2024-09-17 14:53:29 +09:00
Yu Watanabe a2fbe9f3f9 network/sysctl-monitor: fix use-after-free
Previously, manager_free() did not assign NULL to Manager.sysctl_shadow,
hence sysctl_clear_link_shadows() called by link_free() will causes
use-after-free. To fix the issue, this makes Manager.sysctl_shadow will be
set to NULL after it is freed,

Fixes a bug introduced by 6d9ef22acd.
2024-09-16 15:12:47 +09:00
Yu Watanabe 7c778cecdb network/sysctl: several cleanups for sysctl_add_monitor()
- rename rootcg -> root_cgroup_fd, to emphasize it is a fd,
- drop nested function call, and check error code.
2024-09-16 14:36:54 +09:00
Yu Watanabe 46718d344f bpf-link: introduce bpf_ring_buffer_free() and friends
Then, replace rb_free() in networkd.

Follow-up for 6d9ef22acd.
2024-09-16 14:36:54 +09:00
Yu Watanabe 9295c7ae09 network/sysctl: use wrapped free functions
No functional change, just refactoring.

Follow-up for 6d9ef22acd.
2024-09-16 14:36:54 +09:00
Yu Watanabe 41afafbf2a network/sysctl-monitor: fix sanity check in cut_last()
This also adds basic comment about the return code.

Follow-up for 6d9ef22acd.
2024-09-16 14:36:54 +09:00
Yu Watanabe 9671efff78 NEWS: fix typo
Follow-up for dcc359010c.
2024-09-16 11:50:48 +09:00
Yu Watanabe 4f0bc2582e man: fix typo
Follow-up for a632d8dd9f.
2024-09-16 11:49:04 +09:00
Yu Watanabe 3292120adf nspawn: fix typo
Follow-up for d7a6bb9891.
2024-09-16 11:47:43 +09:00
Yu Watanabe f6cc5e1c8d
Merge pull request #34393 from poettering/tmpfiles-ownership-flag
tmpfiles: introduce an explicit line flag $ for enabling purge logic …
2024-09-16 10:51:09 +09:00
Yu Watanabe 590f430cac
Merge pull request #34425 from yuwata/udev-rules-case-insensitive-match
udev-rules: support case insensitive match
2024-09-16 10:42:37 +09:00
Mike Yuan 93d2d36638 basic/build: also include BTF status 2024-09-16 10:42:16 +09:00
Lennart Poettering 369b12375b coredump: use _cleanup_(iovec_done) where appropriate 2024-09-16 10:42:02 +09:00
Yu Watanabe b5ec8f77e0
Merge pull request #34434 from poettering/bootctl-stub-paths
bootctl: expose new stub path efi vars and related
2024-09-16 10:41:24 +09:00
Lennart Poettering 3e0a3a0259 bootctl: show whether a PE file is an addon in 'booctl kernel-identify' 2024-09-16 10:41:10 +09:00
Celeste Liu 6573f0c82c hwdb: add Kensington SlimBlade Pro trackball (Bluetooth mode)
Wired and 2.4G dongle connectivity is covered by general trackball rule,
but with Bluetooth connectivity Kensington SlimBlade Pro uses the name
"SlimBlade Pro" which doesn't contain "[Tt]rack[Bb]all". We need to
process it specially.

Signed-off-by: Celeste Liu <CoelacanthusHex@gmail.com>
2024-09-16 10:40:56 +09:00
Daan De Meyer e0258ac886 repart: Fix log messages in partition_populate_directory()
We're not actually populating a filesystem here, we're preparing
to populate a filesystem, so update the log messages accordingly.
2024-09-15 22:40:10 +02:00
Lennart Poettering a859d0d378 tmpfiles.d: add $ flag to all lines which are clearly private to our packages, and should be removed on package removal
(This excludes any dirs that contain resources placed there by the user)

(I also didn't bother marking resources belonging to components that are
really not optional for us)
2024-09-15 19:44:05 +02:00
Lennart Poettering db15657dfb tmpfiles: introduce an explicit line flag $ for enabling purge logic for a line
Let's make the risk of accidental misuse, and mark lines that shall be
covered by --purge with an explicit new flag "$".

See: #33349
2024-09-15 19:43:09 +02:00
Lennart Poettering 2aa3005ad2 bootctl: also show current/default/oneshot entry literally in output 2024-09-15 19:34:19 +02:00
Lennart Poettering 90cf998875 bootctl: add --print-loader-path + --print-stub-path
These are inspired by the existing commands that return the path to the
boot or ESP partitions. However, these new commands show the path to the
boot loader (systemd-boot) or UKI/stub (systemd-stub) that was used on
the current boot. This information is derived from EFI variables.
2024-09-15 19:34:19 +02:00
Lennart Poettering c8d60ae79d efivars: add helper that reads an fs path from an efi var 2024-09-15 19:34:19 +02:00
Lennart Poettering bfcf48b842 bootctl: show stub partition data too in "status" too 2024-09-15 19:33:48 +02:00
Mike Yuan 3a41a21666 man/bootup: rename initrd to exitrd at one more place
Follow-up for f2c2fa87b6
2024-09-16 01:35:31 +09:00
Luca Boccassi 37c2010bcf test: fix ASAN options in TEST-29-PORTABLE
Bash arrays cannot be exported, so we need to redefine it in each
subtest

Follow-up for 680dec33f2
2024-09-15 18:10:29 +02:00
Yu Watanabe 5f5c5c48b9 udev-rules: support case insensitive match
This introduces 'i' prefix for match string. When specified, string or
pattern will match case-insensitively.

Closes #34359.

Co-authored-by: Ryan Wilson <ryantimwilson@meta.com>
2024-09-15 23:09:26 +09:00
Yu Watanabe 68fdef46a7 udev-rules: embed UdevRuleToken.attr_match_remove_trailing_whitespace flag into UdevRuleMatchType
No functional change, just refactoring and preparation for later change.
2024-09-15 13:52:50 +09:00
90 changed files with 1826 additions and 730 deletions

13
NEWS
View File

@ -2,6 +2,15 @@ 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
@ -85,7 +94,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 reenabled during late shutdown, so that the user
* Ctrl-Alt-Delete is re-enabled 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
@ -202,7 +211,7 @@ CHANGES WITH 257 in spe:
versions.
* systemd-sysupdate gained a new --transfer-source= option to set the
directory to which transfer sources cofigured with
directory to which transfer sources configured with
PathRelativeTo=explicit will be interpreted.
Miscellaneous:

4
TODO
View File

@ -189,6 +189,8 @@ Features:
* go through our codebase, and convert "vertical tables" (i.e. things such as
"systemctl status") to use table_new_vertical() for output
* pcrlock: add support for multi-profile UKIs
* logind: when logging in use new tmpfs quota support to configure quota on
/tmp/ + /dev/shm/. But do so only in case of tmpfs, because otherwise quota
is persistent and any persistent settings mean we don#t have to reapply them.
@ -1860,7 +1862,7 @@ Features:
* fstab-generator: default to tmpfs-as-root if only usr= is specified on the kernel cmdline
* docs: bring https://systemd.io/MY_SERVICE_CANT_GET_REATLIME up to date
* docs: bring https://systemd.io/MY_SERVICE_CANT_GET_REALTIME up to date
* add a job mode that will fail if a transaction would mean stopping
running units. Use this in timedated to manage the NTP service

View File

@ -247,4 +247,4 @@ Note that scope units created by `machined`'s `CreateMachine()` call have this f
### Example
Please see the [systemd-run sources](http://cgit.freedesktop.org/systemd/systemd/plain/src/run/run.c) for a relatively simple example how to create scope or service units transiently and pass properties to them.
Please see the [systemd-run sources](https://github.com/systemd/systemd/blob/main/src/run/run.c) for a relatively simple example how to create scope or service units transiently and pass properties to them.

View File

@ -104,7 +104,7 @@ A: Use:
**Q: Whenever my service tries to acquire RT scheduling for one of its threads this is refused with EPERM even though my service is running with full privileges. This works fine on my non-systemd system!**
A: By default, systemd places all systemd daemons in their own cgroup in the "cpu" hierarchy. Unfortunately, due to a kernel limitation, this has the effect of disallowing RT entirely for the service. See [My Service Can't Get Realtime!](/MY_SERVICE_CANT_GET_REATLIME) for a longer discussion and what to do about this.
A: By default, systemd places all systemd daemons in their own cgroup in the "cpu" hierarchy. Unfortunately, due to a kernel limitation, this has the effect of disallowing RT entirely for the service. See [My Service Can't Get Realtime!](/MY_SERVICE_CANT_GET_REALTIME) for a longer discussion and what to do about this.
**Q: My service is ordered after `network.target` but at boot it is still called before the network is up. What's going on?**

View File

@ -7,25 +7,30 @@ SPDX-License-Identifier: LGPL-2.1-or-later
# Password Agents
systemd 12 and newer support lightweight password agents which can be used to query the user for system-level passwords or passphrases.
These are passphrases that are not related to a specific user, but to some kind of hardware or service.
Right now this is used exclusively for encrypted hard-disk passphrases but later on this is likely to be used to query passphrases of SSL certificates at Apache startup time as well.
The basic idea is that a system component requesting a password entry can simply drop a simple .ini-style file into `/run/systemd/ask-password` which multiple different agents may watch via `inotify()`, and query the user as necessary.
The answer is then sent back to the querier via an `AF_UNIX`/`SOCK_DGRAM` socket.
Multiple agents might be running at the same time in which case they all should query the user and the agent which answers first wins.
Right now systemd ships with the following passphrase agents:
systemd 12 and newer support lightweight password agents which can be used to
query the user for system-level passwords or passphrases. These are
passphrases that are not related to a specific user, but to some kind of
hardware or service. This is used for encrypted hard-disk passphrases or to
query passphrases of SSL certificates at web server start-up time. The basic
idea is that a system component requesting a password entry can simply drop a
simple .ini-style file into `/run/systemd/ask-password/` which multiple
different agents may watch via `inotify()`, and query the user as necessary.
The answer is then sent back to the querier via an `AF_UNIX`/`SOCK_DGRAM`
socket. Multiple agents might be running at the same time in which case they
all should query the user and the agent which answers first wins. Right now
systemd ships with the following passphrase agents:
* A Plymouth agent used for querying passwords during boot-up
* A console agent used in similar situations if Plymouth is not available
* A GNOME agent which can be run as part of the normal user session which pops up a notification message and icon which when clicked receives the passphrase from the user.
This is useful and necessary in case an encrypted system hard-disk is plugged in when the machine is already up.
* A [`wall(1)`](https://man7.org/linux/man-pages/man1/wall.1.html) agent which sends wall messages as soon as a password shall be entered.
* A simple tty agent which is built into "`systemctl start`" (and similar commands) and asks passwords to the user during manual startup of a service
* A simple tty agent which can be run manually to respond to all queued passwords
## Implementing Agents
It is easy to write additional agents. The basic algorithm to follow looks like this:
* Create an inotify watch on /run/systemd/ask-password, watch for `IN_CLOSE_WRITE|IN_MOVED_TO`
* Create an inotify watch on `/run/systemd/ask-password/`, watch for `IN_CLOSE_WRITE|IN_MOVED_TO`
* Ignore all events on files in that directory that do not start with "`ask.`"
* As soon as a file named "`ask.xxxx`" shows up, read it. It's a simple `.ini` file that may be parsed with the usual parsers. The `xxxx` suffix is randomized.
* Make sure to ignore unknown `.ini` file keys in those files, so that we can easily extend the format later on.
@ -42,23 +47,57 @@ It is easy to write additional agents. The basic algorithm to follow looks like
* Make sure to hide a password query dialog as soon as a) the `ask.xxxx` file is deleted, watch this with inotify. b) the `NotAfter=` time elapses, if it is set `!= 0`.
* Access to the socket is restricted to privileged users.
To acquire the necessary privileges to send the answer back, consider using PolicyKit.
In fact, the GNOME agent we ship does that, and you may simply piggyback on that, by executing "`/usr/bin/pkexec /lib/systemd/systemd-reply-password 1 /path/to/socket`" or "`/usr/bin/pkexec /lib/systemd/systemd-reply-password 0 /path/to/socket`" and writing the password to its standard input.
For convenience, an reference implementation is provided by executing "`/usr/bin/pkexec /lib/systemd/systemd-reply-password 1 /path/to/socket`" or "`/usr/bin/pkexec /lib/systemd/systemd-reply-password 0 /path/to/socket`" and writing the password to its standard input.
Use '`1`' as argument if a password was entered by the user, or '`0`' if the user canceled the request.
* If you do not want to use PK ensure to acquire the necessary privileges in some other way and send a single datagram
to the socket consisting of the password string either prefixed with "`+`" or with "`-`" depending on whether the password entry was successful or not.
You may but don't have to include a final `NUL` byte in your message.
Again, it is essential that you stop showing the password box/notification/status icon if the `ask.xxx` file is removed or when `NotAfter=` elapses (if it is set `!= 0`)!
Again, it is essential that you stop showing the password
box/notification/status icon if the `ask.xxxx` file is removed or when
`NotAfter=` elapses (if it is set `!= 0`)!
It may happen that multiple password entries are pending at the same time.
Your agent needs to be able to deal with that. Depending on your environment you may either choose to show all outstanding passwords at the same time or instead only one and as soon as the user has replied to that one go on to the next one.
Your agent needs to be able to deal with that. Depending on your environment
you may either choose to show all outstanding passwords at the same time or
instead only one and as soon as the user has replied to that one go on to the
next one.
You may test this all with manually invoking the "`systemd-ask-password`" tool on the command line.
Pass `--no-tty` to ensure the password is asked via the agent system.
Note that only privileged users may use this tool (after all this is intended purely for system-level passwords).
If you write a system level agent, a smart way to activate it is using systemd
`.path` units. This will ensure that systemd will watch the
`/run/systemd/ask-password/` directory and spawn the agent as soon as that
directory becomes non-empty. In fact, the console, wall and Plymouth agents
are started like this. If systemd is used to maintain user sessions as well
you can use a similar scheme to automatically spawn your user password agent as
well.
If you write a system level agent a smart way to activate it is using systemd `.path` units.
This will ensure that systemd will watch the `/run/systemd/ask-password` directory and spawn the agent as soon as that directory becomes non-empty.
In fact, the console, wall and Plymouth agents are started like this.
If systemd is used to maintain user sessions as well you can use a similar scheme to automatically spawn your user password agent as well.
(As of this moment we have not switched any DE over to use systemd for session management, however.)
## Implementing Queriers
It's also easy to implement applications that want to query passwords this way
(i.e. client for the agents above). Simply bind an `AF_UNIX`/`SOCK_DGRAM`
socket somewhere (suggestion: you can do this in `/run/systemd/ask-password/`
under a randomized socket name, not beginning with `ask.`). Then, create an
`/run/systemd/ask-password/ask.xxxx` (replace the `xxxx` by some randomized
string) file, with the appropriate `Message=`, `PID=`, `Icon=`, `Echo=`,
`NotAfter=` fields in the `[Ask]` section. Most importantly, include `Socket=`
pointing to your socket entrypoint. Then, just wait until the password is
delivered to you on the socket. Finally, don't forget to remove the file and
the socket once done.
## Testing
You may test agents by manually invoking the "`systemd-ask-password`" tool from
a shell. Pass `--no-tty` to ensure the password is asked via the agent system.
You may test queriers by manually invoking the
"`systemd-tty-ask-password-agent`" from a shell.
## Unprivileged Per-User Password Agents
Starting with systemd v257 the scheme is extended to per-user password
agents. A second per-user directory `$XDG_RUNTIME_DIR/systemd/ask-password/` is
now available, with the same protocol as the system-wide
counterpart. Unprivileged, per-directory agents should watch this directory in
parallel to the system-wide one. Unprivileged queriers (i.e. clients to these
agents) should pick the per-user directory to place their password request
files in.

View File

@ -310,6 +310,10 @@ 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,7 +267,8 @@
<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>pe</literal>, and <literal>unknown</literal>.
one of <literal>uki</literal>, <literal>addon</literal>, <literal>pe</literal>, and
<literal>unknown</literal>.
</para>
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
@ -360,6 +361,24 @@
<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,11 +46,10 @@
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 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>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>Additional information about the system boot process may be
found in

View File

@ -593,8 +593,6 @@ node /org/freedesktop/systemd1 {
<!--method GetJobBefore is not documented!-->
<!--method SetShowStatus is not documented!-->
<!--method ListUnitsFiltered is not documented!-->
<!--method ListUnitsByPatterns is not documented!-->
@ -673,8 +671,6 @@ node /org/freedesktop/systemd1 {
<!--property ConfirmSpawn is not documented!-->
<!--property ShowStatus is not documented!-->
<!--property DefaultStandardOutput is not documented!-->
<!--property DefaultStandardError is not documented!-->
@ -1362,6 +1358,24 @@ node /org/freedesktop/systemd1 {
<para><function>ResetFailedUnit()</function> resets the "failed" state of a specific unit.</para>
<para><function>SetShowStatus()</function> configures the display of status messages during bootup and
shutdown. The <varname>mode</varname> parameter can be set to any value that's valid for the
<varname>systemd.show_status</varname> kernel parameter. For more information about
<varname>systemd.show_status</varname>, see
<citerefentry project="man-pages"><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
The <varname>mode</varname> parameter can also be set to an empty string. When <varname>mode</varname>
is set to an empty string, <function>SetShowStatus()</function> will reset
<varname>ShowStatus</varname> back to its original value. You can use
<function>SetShowStatus()</function> create a service that does something like this:
<orderedlist>
<listitem><para>Send a D-Bus message that will turn off status messages.</para></listitem>
<listitem><para>Block until a reply to that message is received.</para></listitem>
<listitem><para>Print multiples lines without being interrupted by status messages.</para></listitem>
<listitem><para>Send a D-Bus message that will reset <varname>ShowStatus</varname> back to its
original value.</para></listitem>
</orderedlist>
</para>
<para><function>ResetFailed()</function> resets the "failed" state of all units.</para>
<para><function>ListUnits()</function> returns an array of all currently loaded units. Note that
@ -1788,6 +1802,12 @@ node /org/freedesktop/systemd1 {
<para><varname>Environment</varname> encodes the environment block passed to all executed services. It
may be altered with bus calls such as <function>SetEnvironment()</function> (see above).</para>
<para><varname>ShowStatus</varname> encodes systemd's current policy for displaying status messages
during bootup and shutdown. Its value can be any valid value for the
<varname>systemd.show_status</varname> kernel parameter (see
<citerefentry project="man-pages"><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>).
It may be altered using <function>SetShowStatus()</function> (see above).</para>
<para><varname>UnitPath</varname> encodes the currently active unit file search path. It is an array of
file system paths encoded as strings.</para>

View File

@ -76,16 +76,7 @@
<term><varname>Type=</varname></term>
<listitem><para>The GPT partition type UUID to match. This may be a GPT partition type UUID such as
<constant>4f68bce3-e8cd-4db1-96e7-fbcaf984b709</constant>, or an identifier.
Architecture specific partition types can use one of these architecture identifiers:
<constant>alpha</constant>, <constant>arc</constant>, <constant>arm</constant> (32-bit),
<constant>arm64</constant> (64-bit, aka aarch64), <constant>ia64</constant>,
<constant>loongarch64</constant>, <constant>mips-le</constant>, <constant>mips64-le</constant>,
<constant>parisc</constant>, <constant>ppc</constant>, <constant>ppc64</constant>,
<constant>ppc64-le</constant>, <constant>riscv32</constant>, <constant>riscv64</constant>,
<constant>s390</constant>, <constant>s390x</constant>, <constant>tilegx</constant>,
<constant>x86</constant> (32-bit, aka i386) and <constant>x86-64</constant> (64-bit, aka amd64).
</para>
<constant>4f68bce3-e8cd-4db1-96e7-fbcaf984b709</constant>, or an identifier.</para>
<para>The supported identifiers are:</para>
@ -237,7 +228,14 @@
</tgroup>
</table>
<para>This setting defaults to <constant>linux-generic</constant>.</para>
<para>Architecture specific partition types can use one of these architecture identifiers:
<constant>alpha</constant>, <constant>arc</constant>, <constant>arm</constant> (32-bit),
<constant>arm64</constant> (64-bit, aka aarch64), <constant>ia64</constant>,
<constant>loongarch64</constant>, <constant>mips-le</constant>, <constant>mips64-le</constant>,
<constant>parisc</constant>, <constant>ppc</constant>, <constant>ppc64</constant>,
<constant>ppc64-le</constant>, <constant>riscv32</constant>, <constant>riscv64</constant>,
<constant>s390</constant>, <constant>s390x</constant>, <constant>tilegx</constant>,
<constant>x86</constant> (32-bit, aka i386) and <constant>x86-64</constant> (64-bit, aka amd64).</para>
<para>Most of the partition type UUIDs listed above are defined in the <ulink
url="https://uapi-group.org/specifications/specs/discoverable_partitions_specification">Discoverable Partitions
@ -485,18 +483,18 @@
<term><varname>ExcludeFiles=</varname></term>
<term><varname>ExcludeFilesTarget=</varname></term>
<listitem><para>Takes an absolute file system path referring to a source file or directory on the
host. This setting may be used to exclude files or directories from the host from being copied into
the file system when <varname>CopyFiles=</varname> is used. This option may be used multiple times to
exclude multiple files or directories from host from being copied into the newly formatted file
system.</para>
<listitem><para>Takes one or more absolute paths, separated by whitespace, each referring to a
source file or directory on the host. This setting may be used to exclude files or directories from
the host from being copied into the file system when <varname>CopyFiles=</varname> is used. This
option may be used multiple times to exclude multiple files or directories from host from being
copied into the newly formatted file system.</para>
<para>If the path is a directory and ends with <literal>/</literal>, only the directory's
contents are excluded but not the directory itself. If the path is a directory and does not end with
<literal>/</literal>, both the directory and its contents are excluded.</para>
<para><varname>ExcludeFilesTarget=</varname> is like <varname>ExcludeFiles=</varname> except that
instead of excluding the path on the host from being copied into the partition, we exclude any files
instead of excluding the path on the host from being copied into the partition, it exclude any files
and directories from being copied into the given path in the partition.</para>
<para>When
@ -897,6 +895,59 @@
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>SupplementFor=</varname></term>
<listitem><para>Takes a partition definition name, such as <literal>10-esp</literal>. If specified,
<command>systemd-repart</command> will avoid creating this partition and instead prefer to partially
merge the two definitions. However, depending on the existing layout of partitions on disk,
<command>systemd-repart</command> may be forced to fall back onto un-merging the definitions and
using them as originally written, potentially creating this partition. Specifically,
<command>systemd-repart</command> will fall back if this partition is found to already exist on disk,
or if the target partition already exists on disk but is too small, or if it cannot allocate space
for the merged partition for some other reason.</para>
<para>The following fields are merged into the target definition in the specified ways:
<varname>Weight=</varname> and <varname>PaddingWeight=</varname> are simply overwritten;
<varname>SizeMinBytes=</varname> and <varname>PaddingMinBytes=</varname> use the larger of the two
values; <varname>SizeMaxBytes=</varname> and <varname>PaddingMaxBytes=</varname> use the smaller
value; and <varname>CopyFiles=</varname>, <varname>ExcludeFiles=</varname>,
<varname>ExcludeFilesTarget=</varname>, <varname>MakeDirectories=</varname>, and
<varname>Subvolumes=</varname> are concatenated.</para>
<para>Usage of this option in combination with <varname>CopyBlocks=</varname>,
<varname>Encrypt=</varname>, or <varname>Verity=</varname> is not supported. The target definition
cannot set these settings either. A definition cannot simultaneously be a supplement and act as a
target for some other supplement definition. A target cannot have more than one supplement partition
associated with it.</para>
<para>For example, distributions can use this to implement <variable>$BOOT</variable> as defined in
the <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification/">Boot Loader
Specification</ulink>. Distributions may prefer to use the ESP as <variable>$BOOT</variable> whenever
possible, but to adhere to the spec XBOOTLDR must sometimes be used instead. So, they should create
two definitions: the first defining an ESP big enough to hold just the bootloader, and a second for
the XBOOTLDR that's sufficiently large to hold kernels and configured as a supplement for the ESP.
Whenever possible, <command>systemd-repart</command> will try to merge the two definitions to create
one large ESP, but if that's not allowable due to the existing conditions on disk a small ESP and a
large XBOOTLDR will be created instead.</para>
<para>As another example, distributions can also use this to seamlessly share a single
<filename>/home</filename> partition in a multi-boot scenario, while preferring to keep
<filename>/home</filename> on the root partition by default. Having a <filename>/home</filename>
partition separated from the root partition entails some extra complexity: someone has to decide how
to split the space between the two partitions. On the other hand, it allows a user to share their
home area between multiple installed OSs (i.e. via <citerefentry><refentrytitle>systemd-homed.service
</refentrytitle><manvolnum>8</manvolnum></citerefentry>). Distributions should create two definitions:
the first for a root partition that takes up some relatively small percentage of the disk, and the
second as a supplement for the first to create a <filename>/home</filename> partition that takes up
all the remaining free space. On first boot, if <command>systemd-repart</command> finds an existing
<filename>/home</filename> partition on disk, it'll un-merge the definitions and create just a small
root partition. Otherwise, the definitions will be merged and a single large root partition will be
created.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -30,26 +30,22 @@
<refsect1>
<title>Description</title>
<para><command>systemd-ask-password</command> may be used to query
a system password or passphrase from the user, using a question
message specified on the command line. When run from a TTY it will
query a password on the TTY and print it to standard output. When
run with no TTY or with <option>--no-tty</option> it will use the
system-wide query mechanism, which allows active users to respond via
several agents, listed below.</para>
<para><command>systemd-ask-password</command> may be used to query a password or passphrase interactively
from the user, using a question prompt specified on the command line. When run from a TTY it will query a
password on the TTY and print it to standard output. When run with no TTY or with
<option>--no-tty</option> it will use a system-wide or per-user agent-based query mechanism, which allows
active users to respond via several agents, listed below.</para>
<para>The purpose of this tool is to query system-wide passwords
— that is passwords not attached to a specific user account.
Examples include: unlocking encrypted hard disks when they are
plugged in or at boot, entering an SSL certificate passphrase for
web and VPN servers.</para>
<para>The purpose of this tool is to query system-wide or per-user passwords — the former includes
passwords possibly not associated to a specific user account. Examples include: unlocking encrypted hard
disks when they are plugged in or at boot, entering an SSL certificate passphrase for web and VPN
servers.</para>
<para>Existing agents are:
<para>Existing system-level agents are:
<itemizedlist>
<listitem><para>A boot-time password agent asking the user for
passwords using
<citerefentry project='die-net'><refentrytitle>plymouth</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<listitem><para>A boot-time password agent asking the user for passwords using <citerefentry
project='die-net'><refentrytitle>plymouth</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
</para></listitem>
<listitem><para>A boot-time password agent querying the user
@ -77,17 +73,15 @@
all the agents listed above (except for the last one), run as privileged
system services. The last one also needs elevated privileges, so
should be run through
<citerefentry project='die-net'><refentrytitle>sudo</refentrytitle><manvolnum>8</manvolnum></citerefentry>
<citerefentry><refentrytitle>run0</refentrytitle><manvolnum>1</manvolnum></citerefentry>
or similar.</para>
<para>Additional password agents may be implemented according to
the <ulink url="https://systemd.io/PASSWORD_AGENTS/">systemd Password Agent
Specification</ulink>.</para>
<para>Additional password agents may be implemented according to the <ulink
url="https://systemd.io/PASSWORD_AGENTS/">systemd Password Agent Specification</ulink>.</para>
<para>If a password is queried on a TTY, the user may press TAB to
hide the asterisks normally shown for each character typed.
Pressing Backspace as first key achieves the same effect.</para>
</refsect1>
<refsect1>
@ -241,6 +235,17 @@
<xi:include href="version-info.xml" xpointer="v249"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--user</option></term>
<term><option>--system</option></term>
<listitem><para>Controls whether to query the system-wide or the per-user password agents. By default
if invoked privileged the system-wide agents are queried, otherwise the per-user ones. These options
allow to override this automatic behaviour.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
</variablelist>

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 overriden) are
the PE sections of the selected profile (and those of the base profile, except if overridden) are
measured.</para>
<para>If non-zero, the selected numeric profile is measured into PCR 12.</para>

View File

@ -152,10 +152,11 @@
<varlistentry>
<term><option>--purge</option></term>
<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>,
<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>,
<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

@ -3001,7 +3001,12 @@ SystemCallErrorNumber=EPERM</programlisting>
<para><option>tty</option> connects standard output to a tty (as configured via <varname>TTYPath=</varname>,
see below). If the TTY is used for output only, the executed process will not become the controlling process of
the terminal, and will not fail or wait for other processes to release the terminal.</para>
the terminal, and will not fail or wait for other processes to release the terminal. Note: if a unit
tries to print multiple lines to a TTY during bootup or shutdown, then there's a chance that those
lines will be broken up by status messages. <function>SetShowStatus()</function> can be used to
prevent this problem. See
<citerefentry project="man-pages"><refentrytitle>org.freedesktop.systemd1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details.</para>
<para><option>journal</option> connects standard output with the journal, which is accessible via
<citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>. Note

View File

@ -568,7 +568,11 @@
<listitem><para>Enables display of status messages on the
console, as controlled via
<varname>systemd.show_status=1</varname> on the kernel command
line.</para></listitem>
line.</para>
<para>You may want to use <function>SetShowStatus()</function> instead of
<constant>SIGRTMIN+20</constant> in order to prevent race conditions. See
<citerefentry project="man-pages"><refentrytitle>org.freedesktop.systemd1</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para></listitem>
</varlistentry>
<varlistentry>
@ -579,7 +583,11 @@
controlled via
<varname>systemd.show_status=0</varname>
on the kernel command
line.</para></listitem>
line.</para>
<para>You may want to use <function>SetShowStatus()</function> instead of
<constant>SIGRTMIN+21</constant> in order to prevent race conditions. See
<citerefentry project="man-pages"><refentrytitle>org.freedesktop.systemd1</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -539,6 +539,10 @@ 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,6 +141,12 @@
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

@ -38,7 +38,7 @@ static int help(void) {
return log_oom();
printf("%1$s [OPTIONS...] MESSAGE\n\n"
"%3$sQuery the user for a system passphrase, via the TTY or a UI agent.%4$s\n\n"
"%3$sQuery the user for a passphrase, via the TTY or a UI agent.%4$s\n\n"
" -h --help Show this help\n"
" --icon=NAME Icon name\n"
" --id=ID Query identifier (e.g. \"cryptsetup:/dev/sda5\")\n"
@ -58,6 +58,8 @@ static int help(void) {
" --no-output Do not print password to standard output\n"
" -n Do not suffix password written to standard output with\n"
" newline\n"
" --user Ask only our own user's agents\n"
" --system Ask agents of the system and of all users\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
@ -81,6 +83,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_NO_OUTPUT,
ARG_VERSION,
ARG_CREDENTIAL,
ARG_USER,
ARG_SYSTEM,
};
static const struct option options[] = {
@ -97,6 +101,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "keyname", required_argument, NULL, ARG_KEYNAME },
{ "no-output", no_argument, NULL, ARG_NO_OUTPUT },
{ "credential", required_argument, NULL, ARG_CREDENTIAL },
{ "user", no_argument, NULL, ARG_USER },
{ "system", no_argument, NULL, ARG_SYSTEM },
{}
};
@ -183,6 +189,14 @@ static int parse_argv(int argc, char *argv[]) {
arg_credential_name = optarg;
break;
case ARG_USER:
arg_flags |= ASK_PASSWORD_USER;
break;
case ARG_SYSTEM:
arg_flags &= ~ASK_PASSWORD_USER;
break;
case 'n':
arg_newline = false;
break;
@ -228,6 +242,9 @@ static int run(int argc, char *argv[]) {
log_setup();
/* Unprivileged? Then imply ASK_PASSWORD_USER by default */
SET_FLAG(arg_flags, ASK_PASSWORD_USER, geteuid() != 0);
r = parse_argv(argc, argv);
if (r <= 0)
return r;

View File

@ -221,6 +221,12 @@ const char* const systemd_features =
" -BPF_FRAMEWORK"
#endif
#if HAVE_VMLINUX_H
" +BTF"
#else
" -BTF"
#endif
#if HAVE_XKBCOMMON
" +XKBCOMMON"
#else
@ -247,7 +253,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,8 +145,10 @@ int efi_get_variable(
int efi_get_variable_string(const char *variable, char **ret) {
_cleanup_free_ void *s = NULL;
size_t ss = 0;
int r;
char *x;
int r;
assert(variable);
r = efi_get_variable(variable, NULL, &s, &ss);
if (r < 0)
@ -156,10 +158,27 @@ int efi_get_variable_string(const char *variable, char **ret) {
if (!x)
return -ENOMEM;
if (ret)
*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,6 +11,7 @@
#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)
@ -47,6 +48,7 @@
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);
@ -68,6 +70,10 @@ 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;
}
@ -100,3 +106,7 @@ 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

@ -300,9 +300,10 @@ int log_emergency_level(void);
#define log_dump(level, buffer) \
log_dump_internal(level, 0, PROJECT_FILE, __LINE__, __func__, buffer)
#define log_oom() log_oom_internal(LOG_ERR, PROJECT_FILE, __LINE__, __func__)
#define log_oom_debug() log_oom_internal(LOG_DEBUG, PROJECT_FILE, __LINE__, __func__)
#define log_oom_warning() log_oom_internal(LOG_WARNING, PROJECT_FILE, __LINE__, __func__)
#define log_oom_full(level) log_oom_internal(level, PROJECT_FILE, __LINE__, __func__)
#define log_oom() log_oom_full(LOG_ERR)
#define log_oom_debug() log_oom_full(LOG_DEBUG)
#define log_oom_warning() log_oom_full(LOG_WARNING)
bool log_on_console(void) _pure_;

View File

@ -153,7 +153,7 @@ bool strv_overlap(char * const *a, char * const *b) _pure_;
_STRV_FOREACH_BACKWARDS(s, l, UNIQ_T(h, UNIQ), UNIQ_T(i, UNIQ))
#define _STRV_FOREACH_PAIR(x, y, l, i) \
for (typeof(*l) *x, *y, *i = (l); \
for (typeof(*(l)) *x, *y, *i = (l); \
i && *(x = i) && *(y = i + 1); \
i += 2)

View File

@ -255,6 +255,25 @@ int ask_string(char **ret, const char *text, ...) {
return 0;
}
bool any_key_to_proceed(void) {
char key = 0;
bool need_nl = true;
/*
* Insert a new line here as well as to when the user inputs, as this is also used during the
* boot up sequence when status messages may be interleaved with the current program output.
* This ensures that the status messages aren't appended on the same line as this message.
*/
puts("-- Press any key to proceed --");
(void) read_one_char(stdin, &key, USEC_INFINITY, &need_nl);
if (need_nl)
putchar('\n');
return key != 'q';
}
int open_terminal(const char *name, int mode) {
_cleanup_close_ int fd = -EBADF;
unsigned c = 0;

View File

@ -78,6 +78,7 @@ int chvt(int vt);
int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
int ask_char(char *ret, const char *replies, const char *text, ...) _printf_(3, 4);
int ask_string(char **ret, const char *text, ...) _printf_(2, 3);
bool any_key_to_proceed(void);
int vt_disallocate(const char *name);

View File

@ -219,14 +219,12 @@ static int acquire_boot_count_path(
uint64_t left, done;
int r;
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderBootCountPath), &path);
r = efi_get_variable_path(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,12 +298,24 @@ fail:
return r;
}
static void read_efi_var(const char *variable, char **ret) {
static int efi_get_variable_string_and_warn(const char *variable, char **ret) {
int r;
r = efi_get_variable_string(variable, ret);
if (r < 0 && r != -ENOENT)
log_warning_errno(r, "Failed to read EFI variable %s: %m", variable);
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;
}
static void print_yes_no_line(bool first, bool good, const char *name) {
@ -396,26 +408,23 @@ 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;
sd_id128_t loader_part_uuid = SD_ID128_NULL;
_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;
uint64_t loader_features = 0, stub_features = 0;
Tpm2Support s;
int have;
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_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);
(void) efi_loader_get_features(&loader_features);
(void) efi_stub_get_features(&stub_features);
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");
(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);
SecureBootMode secure = efi_get_secure_boot_mode();
printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
@ -463,34 +472,58 @@ 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(), strna(loader), 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);
sd_id128_t bootloader_esp_uuid;
bool have_bootloader_esp_uuid = efi_loader_get_device_part_uuid(&bootloader_esp_uuid) >= 0;
sd_id128_t loader_partition_uuid;
bool have_loader_partition_uuid = efi_loader_get_device_part_uuid(&loader_partition_uuid) >= 0;
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));
print_yes_no_line(false, have_loader_partition_uuid, "Boot loader set ESP information");
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");
}
if (stub) {
printf(" Stub: %s\n", stub);
printf("%sCurrent Stub:%s\n", ansi_underline(), ansi_normal());
printf(" Product: %s%s%s\n", ansi_highlight(), stub, ansi_normal());
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);
}
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));
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(" ESP: n/a\n");
printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(loader_path));
printf(" Partition: n/a\n");
printf(" Stub: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT), strna(stub_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,12 +16,14 @@
#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"
@ -38,6 +40,8 @@ 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;
@ -133,6 +137,71 @@ 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;
@ -182,6 +251,9 @@ 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"
@ -235,6 +307,8 @@ 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[] = {
@ -250,6 +324,8 @@ 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 },
@ -332,6 +408,14 @@ 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;
@ -414,9 +498,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) > 1)
if (!!arg_print_esp_path + !!arg_print_dollar_boot_path + (arg_print_root_device > 0) + arg_print_loader_path + arg_print_stub_path > 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--print-esp-path/-p, --print-boot-path/-x, --print-root-device=/-R cannot be combined.");
"--print-esp-path/-p, --print-boot-path/-x, --print-root-device=/-R, --print-loader-path, --print-stub-path 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"))
@ -541,6 +625,9 @@ 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

@ -282,13 +282,18 @@ static int have_ask_password(void) {
if (!dir) {
if (errno == ENOENT)
return false;
else
return -errno;
}
FOREACH_DIRENT_ALL(de, dir, return -errno)
FOREACH_DIRENT_ALL(de, dir, return -errno) {
if (!IN_SET(de->d_type, DT_REG, DT_UNKNOWN))
continue;
if (startswith(de->d_name, "ask."))
return true;
}
return false;
}
@ -300,9 +305,8 @@ static int manager_dispatch_ask_password_fd(sd_event_source *source,
m->have_ask_password = have_ask_password();
if (m->have_ask_password < 0)
/* Log error but continue. Negative have_ask_password
* is treated as unknown status. */
log_error_errno(m->have_ask_password, "Failed to list /run/systemd/ask-password: %m");
/* Log error but continue. Negative have_ask_password is treated as unknown status. */
log_warning_errno(m->have_ask_password, "Failed to list /run/systemd/ask-password/, ignoring: %m");
return 0;
}
@ -311,7 +315,6 @@ static void manager_close_ask_password(Manager *m) {
assert(m);
m->ask_password_event_source = sd_event_source_disable_unref(m->ask_password_event_source);
m->ask_password_inotify_fd = safe_close(m->ask_password_inotify_fd);
m->have_ask_password = -EINVAL;
}
@ -320,37 +323,43 @@ static int manager_check_ask_password(Manager *m) {
assert(m);
/* We only care about passwords prompts when running in system mode (because that's the only time we
* manage a console) */
if (!MANAGER_IS_SYSTEM(m))
return 0;
if (!m->ask_password_event_source) {
assert(m->ask_password_inotify_fd < 0);
(void) mkdir_p_label("/run/systemd/ask-password", 0755);
m->ask_password_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
if (m->ask_password_inotify_fd < 0)
_cleanup_close_ int inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
if (inotify_fd < 0)
return log_error_errno(errno, "Failed to create inotify object: %m");
r = inotify_add_watch_and_warn(m->ask_password_inotify_fd,
"/run/systemd/ask-password",
IN_CREATE|IN_DELETE|IN_MOVE);
if (r < 0) {
manager_close_ask_password(m);
(void) mkdir_p_label("/run/systemd/ask-password", 0755);
r = inotify_add_watch_and_warn(inotify_fd, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_DELETE|IN_MOVED_TO|IN_ONLYDIR);
if (r < 0)
return r;
}
r = sd_event_add_io(m->event, &m->ask_password_event_source,
m->ask_password_inotify_fd, EPOLLIN,
manager_dispatch_ask_password_fd, m);
if (r < 0) {
log_error_errno(r, "Failed to add event source for /run/systemd/ask-password: %m");
manager_close_ask_password(m);
return r;
}
_cleanup_(sd_event_source_disable_unrefp) sd_event_source *event_source = NULL;
r = sd_event_add_io(
m->event,
&event_source,
inotify_fd,
EPOLLIN,
manager_dispatch_ask_password_fd,
m);
if (r < 0)
return log_error_errno(r, "Failed to add event source for /run/systemd/ask-password/: %m");
(void) sd_event_source_set_description(m->ask_password_event_source, "manager-ask-password");
r = sd_event_source_set_io_fd_own(event_source, true);
if (r < 0)
return log_error_errno(r, "Failed to pass ownership of /run/systemd/ask-password/ inotify fd to event source: %m");
TAKE_FD(inotify_fd);
(void) sd_event_source_set_description(event_source, "manager-ask-password");
m->ask_password_event_source = TAKE_PTR(event_source);
/* Queries might have been added meanwhile... */
manager_dispatch_ask_password_fd(m->ask_password_event_source,
m->ask_password_inotify_fd, EPOLLIN, m);
(void) manager_dispatch_ask_password_fd(m->ask_password_event_source, sd_event_source_get_io_fd(m->ask_password_event_source), EPOLLIN, m);
}
return m->have_ask_password;
@ -908,7 +917,6 @@ int manager_new(RuntimeScope runtime_scope, ManagerTestRunFlags test_run_flags,
.dev_autofs_fd = -EBADF,
.cgroup_inotify_fd = -EBADF,
.pin_cgroupfs_fd = -EBADF,
.ask_password_inotify_fd = -EBADF,
.idle_pipe = { -EBADF, -EBADF, -EBADF, -EBADF},
/* start as id #1, so that we can leave #0 around as "null-like" value */

View File

@ -433,7 +433,6 @@ struct Manager {
/* Do we have any outstanding password prompts? */
int have_ask_password;
int ask_password_inotify_fd;
sd_event_source *ask_password_event_source;
/* Type=idle pipes */

View File

@ -4169,7 +4169,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
* detect when the cgroup becomes empty. Note that the control process is always
* our child so it's pointless to watch all other processes. */
if (!control_pid_good(s))
if (!s->main_pid_known || s->main_pid_alien)
if (!s->main_pid_known || s->main_pid_alien || unit_cgroup_delegate(u))
(void) unit_enqueue_rewatch_pids(u);
}

View File

@ -1046,7 +1046,6 @@ 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;
@ -1063,8 +1062,7 @@ static int process_socket(int fd) {
.msg_controllen = sizeof(control),
.msg_iovlen = 1,
};
ssize_t n;
ssize_t l;
ssize_t n, l;
l = next_datagram_size_fd(fd);
if (l < 0) {
@ -1072,8 +1070,10 @@ static int process_socket(int fd) {
goto finish;
}
iovec.iov_len = l;
iovec.iov_base = malloc(l + 1);
_cleanup_(iovec_done) struct iovec iovec = {
.iov_len = l,
.iov_base = malloc(l + 1),
};
if (!iovec.iov_base) {
r = log_oom();
goto finish;
@ -1083,7 +1083,6 @@ 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;
}
@ -1093,8 +1092,6 @@ 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;
@ -1134,6 +1131,8 @@ 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

@ -93,20 +93,6 @@ STATIC_DESTRUCTOR_REGISTER(arg_root_shell, freep);
STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
static bool press_any_key(void) {
char k = 0;
bool need_nl = true;
puts("-- Press any key to proceed --");
(void) read_one_char(stdin, &k, USEC_INFINITY, &need_nl);
if (need_nl)
putchar('\n');
return k != 'q';
}
static void print_welcome(int rfd) {
_cleanup_free_ char *pretty_name = NULL, *os_name = NULL, *ansi_color = NULL;
static bool done = false;
@ -141,7 +127,7 @@ static void print_welcome(int rfd) {
printf("\nPlease configure your system!\n\n");
press_any_key();
any_key_to_proceed();
done = true;
}
@ -184,7 +170,7 @@ static int show_menu(char **x, unsigned n_columns, unsigned width, unsigned perc
/* on the first screen we reserve 2 extra lines for the title */
if (i % break_lines == break_modulo) {
if (!press_any_key())
if (!any_key_to_proceed())
return 0;
}
}

View File

@ -2434,6 +2434,8 @@ static int create_interactively(void) {
return 0;
}
any_key_to_proceed();
r = acquire_bus(&bus);
if (r < 0)
return r;

View File

@ -404,15 +404,16 @@ static int context_set_path_strv(Context *c, char* const* strv, const char *sour
static int context_set_plugins(Context *c, const char *s, const char *source) {
_cleanup_strv_free_ char **v = NULL;
int r;
assert(c);
if (c->plugins || !s)
return 0;
v = strv_split(s, NULL);
if (!v)
return log_oom();
r = strv_split_full(&v, s, NULL, EXTRACT_UNQUOTE);
if (r < 0)
return log_error_errno(r, "Failed to parse plugin paths from %s: %m", source);
return context_set_path_strv(c, v, source, "plugins", &c->plugins);
}

View File

@ -46,7 +46,13 @@ echo 'DTBDTBDTBDTB' >"$D/sources/subdir/whatever.dtb"
export KERNEL_INSTALL_CONF_ROOT="$D/sources"
# We "install" multiple plugins, but control which ones will be active via install.conf.
export KERNEL_INSTALL_PLUGINS="${ukify_install} ${loaderentry_install} ${uki_copy_install}"
KERNEL_INSTALL_PLUGINS="'${loaderentry_install}' '${uki_copy_install}'"
if [[ -n "$ukify_install" ]]; then
# shellcheck disable=SC2089
KERNEL_INSTALL_PLUGINS="'${ukify_install}' $KERNEL_INSTALL_PLUGINS"
fi
# shellcheck disable=SC2090
export KERNEL_INSTALL_PLUGINS
export BOOT_ROOT="$D/boot"
export BOOT_MNT="$D/boot"
export MACHINE_ID='3e0484f3634a418b8e6a39e8828b03e3'

View File

@ -36,23 +36,22 @@ struct str {
static long cut_last(u32 i, struct str *str) {
char *s;
/* Sanity check for the preverifier */
if (i >= str->l)
return 1; /* exit from the loop */
i = str->l - i - 1;
s = str->s + i;
/* Sanity check for the preverifier */
if (i >= str->l)
return 1;
if (*s == 0)
return 0;
return 0; /* continue */
if (*s == '\n' || *s == '\r' || *s == ' ' || *s == '\t') {
*s = 0;
return 0;
return 0; /* continue */
}
return 1;
return 1; /* exit from the loop */
}
/* Cut off trailing whitespace and newlines */

View File

@ -973,7 +973,7 @@ int netdev_load_one(Manager *manager, const char *filename) {
if (r < 0)
return r;
log_netdev_debug(netdev, "loaded \"%s\"", netdev_kind_to_string(netdev->kind));
log_syntax(/* unit = */ NULL, LOG_DEBUG, filename, /* config_line = */ 0, /* error = */ 0, "Successfully loaded.");
r = netdev_request_to_create(netdev);
if (r < 0)

View File

@ -6,6 +6,7 @@
#include <linux/if_arp.h>
#include "alloc-util.h"
#include "device-private.h"
#include "dhcp-client-internal.h"
#include "hostname-setup.h"
#include "hostname-util.h"
@ -1428,27 +1429,33 @@ static int dhcp4_set_request_address(Link *link) {
}
static bool link_needs_dhcp_broadcast(Link *link) {
const char *val;
int r;
assert(link);
assert(link->network);
/* Return the setting in DHCP[4].RequestBroadcast if specified. Otherwise return the device property
* ID_NET_DHCP_BROADCAST setting, which may be set for interfaces requiring that the DHCPOFFER message
* is being broadcast because they can't handle unicast messages while not fully configured.
* If neither is set or a failure occurs, return false, which is the default for this flag.
*/
r = link->network->dhcp_broadcast;
if (r < 0 && link->dev && sd_device_get_property_value(link->dev, "ID_NET_DHCP_BROADCAST", &val) >= 0) {
r = parse_boolean(val);
if (r < 0)
log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to parse ID_NET_DHCP_BROADCAST, ignoring: %m");
else
log_link_debug(link, "DHCPv4 CLIENT: Detected ID_NET_DHCP_BROADCAST='%d'.", r);
* ID_NET_DHCP_BROADCAST setting, which may be set for interfaces requiring that the DHCPOFFER
* message is being broadcast because they can't handle unicast messages while not fully configured.
* If neither is set or a failure occurs, return false, which is the default for this flag. */
r = link->network->dhcp_broadcast;
if (r >= 0)
return r;
if (!link->dev)
return false;
r = device_get_property_bool(link->dev, "ID_NET_DHCP_BROADCAST");
if (r < 0) {
if (r != -ENOENT)
log_link_warning_errno(link, r, "DHCPv4 CLIENT: Failed to get or parse ID_NET_DHCP_BROADCAST, ignoring: %m");
return false;
}
return r == true;
log_link_debug(link, "DHCPv4 CLIENT: Detected ID_NET_DHCP_BROADCAST='%d'.", r);
return r;
}
static bool link_dhcp4_ipv6_only_mode(Link *link) {

View File

@ -221,7 +221,7 @@ int link_set_ipv6ll_stable_secret(Link *link) {
}
return sysctl_write_ip_property(AF_INET6, link->ifname, "stable_secret",
IN6_ADDR_TO_STRING(&a), &link->manager->sysctl_shadow);
IN6_ADDR_TO_STRING(&a), manager_get_sysctl_shadow(link->manager));
}
int link_set_ipv6ll_addrgen_mode(Link *link, IPv6LinkLocalAddressGenMode mode) {
@ -232,7 +232,7 @@ int link_set_ipv6ll_addrgen_mode(Link *link, IPv6LinkLocalAddressGenMode mode) {
if (mode == link->ipv6ll_address_gen_mode)
return 0;
return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "addr_gen_mode", mode, &link->manager->sysctl_shadow);
return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "addr_gen_mode", mode, manager_get_sysctl_shadow(link->manager));
}
static const char* const ipv6_link_local_address_gen_mode_table[_IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX] = {

View File

@ -1293,9 +1293,9 @@ static int link_get_network(Link *link, Network **ret) {
}
log_link_full(link, warn ? LOG_WARNING : LOG_DEBUG,
"found matching network '%s'%s.",
network->filename,
warn ? ", based on potentially unpredictable interface name" : "");
"Found matching .network file%s: %s",
warn ? ", based on potentially unpredictable interface name" : "",
network->filename);
if (network->unmanaged)
return -ENOENT;
@ -1304,7 +1304,7 @@ static int link_get_network(Link *link, Network **ret) {
return 0;
}
return -ENOENT;
return log_link_debug_errno(link, SYNTHETIC_ERRNO(ENOENT), "No matching .network found.");
}
int link_reconfigure_impl(Link *link, bool force) {

View File

@ -604,7 +604,9 @@ int manager_new(Manager **ret, bool test_mode) {
.duid_product_uuid.type = DUID_TYPE_UUID,
.dhcp_server_persist_leases = true,
.ip_forwarding = { -1, -1, },
#if HAVE_VMLINUX_H
.cgroup_fd = -EBADF,
#endif
};
*ret = TAKE_PTR(m);
@ -624,8 +626,6 @@ Manager* manager_free(Manager *m) {
HASHMAP_FOREACH(link, m->links_by_index)
(void) link_stop_engines(link, true);
hashmap_free(m->sysctl_shadow);
m->request_queue = ordered_set_free(m->request_queue);
m->remove_request_queue = ordered_set_free(m->remove_request_queue);

View File

@ -122,12 +122,14 @@ struct Manager {
/* sysctl */
int ip_forwarding[2];
#if HAVE_VMLINUX_H
Hashmap *sysctl_shadow;
sd_event_source *sysctl_event_source;
struct ring_buffer *sysctl_buffer;
struct sysctl_monitor_bpf *sysctl_skel;
struct bpf_link *sysctl_link;
int cgroup_fd;
#endif
};
int manager_new(Manager **ret, bool test_mode);
@ -150,4 +152,12 @@ int manager_set_timezone(Manager *m, const char *timezone);
int manager_reload(Manager *m, sd_bus_message *message);
static inline Hashmap** manager_get_sysctl_shadow(Manager *manager) {
#if HAVE_VMLINUX_H
return &ASSERT_PTR(manager)->sysctl_shadow;
#else
return NULL;
#endif
}
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);

View File

@ -987,7 +987,7 @@ static int ndisc_router_process_reachable_time(Link *link, sd_ndisc_router *rt)
}
/* Set the reachable time for Neighbor Solicitations. */
r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "base_reachable_time_ms", (uint32_t) msec, &link->manager->sysctl_shadow);
r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "base_reachable_time_ms", (uint32_t) msec, manager_get_sysctl_shadow(link->manager));
if (r < 0)
log_link_warning_errno(link, r, "Failed to apply neighbor reachable time (%"PRIu64"), ignoring: %m", msec);
@ -1021,7 +1021,7 @@ static int ndisc_router_process_retransmission_time(Link *link, sd_ndisc_router
}
/* Set the retransmission time for Neighbor Solicitations. */
r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", (uint32_t) msec, &link->manager->sysctl_shadow);
r = sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", (uint32_t) msec, manager_get_sysctl_shadow(link->manager));
if (r < 0)
log_link_warning_errno(link, r, "Failed to apply neighbor retransmission time (%"PRIu64"), ignoring: %m", msec);
@ -1057,7 +1057,7 @@ static int ndisc_router_process_hop_limit(Link *link, sd_ndisc_router *rt) {
if (hop_limit <= 0)
return 0;
r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "hop_limit", (uint32_t) hop_limit, &link->manager->sysctl_shadow);
r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "hop_limit", (uint32_t) hop_limit, manager_get_sysctl_shadow(link->manager));
if (r < 0)
log_link_warning_errno(link, r, "Failed to apply hop_limit (%u), ignoring: %m", hop_limit);

View File

@ -590,6 +590,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
return log_warning_errno(r, "%s: Failed to store configuration into hashmap: %m", filename);
TAKE_PTR(network);
log_syntax(/* unit = */ NULL, LOG_DEBUG, filename, /* config_line = */ 0, /* error = */ 0, "Successfully loaded.");
return 0;
}

View File

@ -34,13 +34,7 @@ static struct sysctl_monitor_bpf* sysctl_monitor_bpf_free(struct sysctl_monitor_
return NULL;
}
static struct ring_buffer* rb_free(struct ring_buffer *rb) {
sym_ring_buffer__free(rb);
return NULL;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(struct sysctl_monitor_bpf *, sysctl_monitor_bpf_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ring_buffer *, rb_free);
static int sysctl_event_handler(void *ctx, void *data, size_t data_sz) {
struct sysctl_write_event *we = ASSERT_PTR(data);
@ -99,10 +93,10 @@ static int on_ringbuf_io(sd_event_source *s, int fd, uint32_t revents, void *use
int sysctl_add_monitor(Manager *manager) {
_cleanup_(sysctl_monitor_bpf_freep) struct sysctl_monitor_bpf *obj = NULL;
_cleanup_(bpf_link_freep) struct bpf_link *sysctl_link = NULL;
_cleanup_(rb_freep) struct ring_buffer *sysctl_buffer = NULL;
_cleanup_close_ int cgroup_fd = -EBADF, rootcg = -EBADF;
_cleanup_(bpf_ring_buffer_freep) struct ring_buffer *sysctl_buffer = NULL;
_cleanup_close_ int cgroup_fd = -EBADF, root_cgroup_fd = -EBADF;
_cleanup_free_ char *cgroup = NULL;
int idx = 0, r;
int idx = 0, r, fd;
assert(manager);
@ -116,9 +110,9 @@ int sysctl_add_monitor(Manager *manager) {
if (r < 0)
return log_warning_errno(r, "Failed to get cgroup path, ignoring: %m.");
rootcg = cg_path_open(SYSTEMD_CGROUP_CONTROLLER, "/");
if (rootcg < 0)
return log_warning_errno(rootcg, "Failed to open cgroup, ignoring: %m.");
root_cgroup_fd = cg_path_open(SYSTEMD_CGROUP_CONTROLLER, "/");
if (root_cgroup_fd < 0)
return log_warning_errno(root_cgroup_fd, "Failed to open cgroup, ignoring: %m.");
obj = sysctl_monitor_bpf__open_and_load();
if (!obj) {
@ -133,21 +127,27 @@ int sysctl_add_monitor(Manager *manager) {
if (sym_bpf_map_update_elem(sym_bpf_map__fd(obj->maps.cgroup_map), &idx, &cgroup_fd, BPF_ANY))
return log_warning_errno(errno, "Failed to update cgroup map: %m");
sysctl_link = sym_bpf_program__attach_cgroup(obj->progs.sysctl_monitor, rootcg);
sysctl_link = sym_bpf_program__attach_cgroup(obj->progs.sysctl_monitor, root_cgroup_fd);
r = bpf_get_error_translated(sysctl_link);
if (r < 0) {
log_info_errno(r, "Unable to attach sysctl monitor BPF program to cgroup, ignoring: %m.");
return 0;
}
sysctl_buffer = sym_ring_buffer__new(
sym_bpf_map__fd(obj->maps.written_sysctls),
sysctl_event_handler, &manager->sysctl_shadow, NULL);
fd = sym_bpf_map__fd(obj->maps.written_sysctls);
if (fd < 0)
return log_warning_errno(fd, "Failed to get fd of sysctl maps: %m");
sysctl_buffer = sym_ring_buffer__new(fd, sysctl_event_handler, &manager->sysctl_shadow, NULL);
if (!sysctl_buffer)
return log_warning_errno(errno, "Failed to create ring buffer: %m");
fd = sym_ring_buffer__epoll_fd(sysctl_buffer);
if (fd < 0)
return log_warning_errno(fd, "Failed to get poll fd of ring buffer: %m");
r = sd_event_add_io(manager->event, &manager->sysctl_event_source,
sym_ring_buffer__epoll_fd(sysctl_buffer), EPOLLIN, on_ringbuf_io, sysctl_buffer);
fd, EPOLLIN, on_ringbuf_io, sysctl_buffer);
if (r < 0)
return log_warning_errno(r, "Failed to watch sysctl event ringbuffer: %m");
@ -163,23 +163,11 @@ void sysctl_remove_monitor(Manager *manager) {
assert(manager);
manager->sysctl_event_source = sd_event_source_disable_unref(manager->sysctl_event_source);
if (manager->sysctl_buffer) {
sym_ring_buffer__free(manager->sysctl_buffer);
manager->sysctl_buffer = NULL;
}
if (manager->sysctl_link) {
sym_bpf_link__destroy(manager->sysctl_link);
manager->sysctl_link = NULL;
}
if (manager->sysctl_skel) {
sysctl_monitor_bpf__destroy(manager->sysctl_skel);
manager->sysctl_skel = NULL;
}
manager->sysctl_buffer = bpf_ring_buffer_free(manager->sysctl_buffer);
manager->sysctl_link = bpf_link_free(manager->sysctl_link);
manager->sysctl_skel = sysctl_monitor_bpf_free(manager->sysctl_skel);
manager->cgroup_fd = safe_close(manager->cgroup_fd);
manager->sysctl_shadow = hashmap_free(manager->sysctl_shadow);
}
int sysctl_clear_link_shadows(Link *link) {
@ -222,13 +210,13 @@ static void manager_set_ip_forwarding(Manager *manager, int family) {
return; /* keep */
/* First, set the default value. */
r = sysctl_write_ip_property_boolean(family, "default", "forwarding", t, &manager->sysctl_shadow);
r = sysctl_write_ip_property_boolean(family, "default", "forwarding", t, manager_get_sysctl_shadow(manager));
if (r < 0)
log_warning_errno(r, "Failed to %s the default %s forwarding: %m",
enable_disable(t), af_to_ipv4_ipv6(family));
/* Then, set the value to all interfaces. */
r = sysctl_write_ip_property_boolean(family, "all", "forwarding", t, &manager->sysctl_shadow);
r = sysctl_write_ip_property_boolean(family, "all", "forwarding", t, manager_get_sysctl_shadow(manager));
if (r < 0)
log_warning_errno(r, "Failed to %s %s forwarding for all interfaces: %m",
enable_disable(t), af_to_ipv4_ipv6(family));
@ -273,7 +261,7 @@ static int link_update_ipv6_sysctl(Link *link) {
if (!link_ipv6_enabled(link))
return 0;
return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", false, &link->manager->sysctl_shadow);
return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", false, manager_get_sysctl_shadow(link->manager));
}
static int link_set_proxy_arp(Link *link) {
@ -286,7 +274,7 @@ static int link_set_proxy_arp(Link *link) {
if (link->network->proxy_arp < 0)
return 0;
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0, &link->manager->sysctl_shadow);
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0, manager_get_sysctl_shadow(link->manager));
}
static int link_set_proxy_arp_pvlan(Link *link) {
@ -299,7 +287,7 @@ static int link_set_proxy_arp_pvlan(Link *link) {
if (link->network->proxy_arp_pvlan < 0)
return 0;
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp_pvlan", link->network->proxy_arp_pvlan > 0, &link->manager->sysctl_shadow);
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp_pvlan", link->network->proxy_arp_pvlan > 0, manager_get_sysctl_shadow(link->manager));
}
int link_get_ip_forwarding(Link *link, int family) {
@ -341,7 +329,7 @@ static int link_set_ip_forwarding_impl(Link *link, int family) {
if (t < 0)
return 0; /* keep */
r = sysctl_write_ip_property_boolean(family, link->ifname, "forwarding", t, &link->manager->sysctl_shadow);
r = sysctl_write_ip_property_boolean(family, link->ifname, "forwarding", t, manager_get_sysctl_shadow(link->manager));
if (r < 0)
return log_link_warning_errno(link, r, "Failed to %s %s forwarding, ignoring: %m",
enable_disable(t), af_to_ipv4_ipv6(family));
@ -418,7 +406,7 @@ static int link_set_ipv4_rp_filter(Link *link) {
if (link->network->ipv4_rp_filter < 0)
return 0;
return sysctl_write_ip_property_int(AF_INET, link->ifname, "rp_filter", link->network->ipv4_rp_filter, &link->manager->sysctl_shadow);
return sysctl_write_ip_property_int(AF_INET, link->ifname, "rp_filter", link->network->ipv4_rp_filter, manager_get_sysctl_shadow(link->manager));
}
static int link_set_ipv6_privacy_extensions(Link *link) {
@ -438,7 +426,7 @@ static int link_set_ipv6_privacy_extensions(Link *link) {
if (val == IPV6_PRIVACY_EXTENSIONS_KERNEL)
return 0;
return sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) val, &link->manager->sysctl_shadow);
return sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) val, manager_get_sysctl_shadow(link->manager));
}
static int link_set_ipv6_accept_ra(Link *link) {
@ -448,7 +436,7 @@ static int link_set_ipv6_accept_ra(Link *link) {
if (!link_is_configured_for_family(link, AF_INET6))
return 0;
return sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0", &link->manager->sysctl_shadow);
return sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0", manager_get_sysctl_shadow(link->manager));
}
static int link_set_ipv6_dad_transmits(Link *link) {
@ -461,7 +449,7 @@ static int link_set_ipv6_dad_transmits(Link *link) {
if (link->network->ipv6_dad_transmits < 0)
return 0;
return sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits, &link->manager->sysctl_shadow);
return sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits, manager_get_sysctl_shadow(link->manager));
}
static int link_set_ipv6_hop_limit(Link *link) {
@ -474,7 +462,7 @@ static int link_set_ipv6_hop_limit(Link *link) {
if (link->network->ipv6_hop_limit <= 0)
return 0;
return sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit, &link->manager->sysctl_shadow);
return sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit, manager_get_sysctl_shadow(link->manager));
}
static int link_set_ipv6_retransmission_time(Link *link) {
@ -493,7 +481,7 @@ static int link_set_ipv6_retransmission_time(Link *link) {
if (retrans_time_ms <= 0 || retrans_time_ms > UINT32_MAX)
return 0;
return sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", retrans_time_ms, &link->manager->sysctl_shadow);
return sysctl_write_ip_neighbor_property_uint32(AF_INET6, link->ifname, "retrans_time_ms", retrans_time_ms, manager_get_sysctl_shadow(link->manager));
}
static int link_set_ipv6_proxy_ndp(Link *link) {
@ -510,7 +498,7 @@ static int link_set_ipv6_proxy_ndp(Link *link) {
else
v = !set_isempty(link->network->ipv6_proxy_ndp_addresses);
return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v, &link->manager->sysctl_shadow);
return sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v, manager_get_sysctl_shadow(link->manager));
}
int link_set_ipv6_mtu(Link *link, int log_level) {
@ -538,7 +526,7 @@ int link_set_ipv6_mtu(Link *link, int log_level) {
mtu = link->mtu;
}
return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu, &link->manager->sysctl_shadow);
return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu, manager_get_sysctl_shadow(link->manager));
}
static int link_set_ipv4_accept_local(Link *link) {
@ -551,7 +539,7 @@ static int link_set_ipv4_accept_local(Link *link) {
if (link->network->ipv4_accept_local < 0)
return 0;
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "accept_local", link->network->ipv4_accept_local > 0, &link->manager->sysctl_shadow);
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "accept_local", link->network->ipv4_accept_local > 0, manager_get_sysctl_shadow(link->manager));
}
static int link_set_ipv4_route_localnet(Link *link) {
@ -564,7 +552,7 @@ static int link_set_ipv4_route_localnet(Link *link) {
if (link->network->ipv4_route_localnet < 0)
return 0;
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "route_localnet", link->network->ipv4_route_localnet > 0, &link->manager->sysctl_shadow);
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "route_localnet", link->network->ipv4_route_localnet > 0, manager_get_sysctl_shadow(link->manager));
}
static int link_set_ipv4_promote_secondaries(Link *link) {
@ -579,7 +567,7 @@ static int link_set_ipv4_promote_secondaries(Link *link) {
* otherwise. The way systemd-networkd works is that the new IP of a lease is added as a
* secondary IP and when the primary one expires it relies on the kernel to promote the
* secondary IP. See also https://github.com/systemd/systemd/issues/7163 */
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "promote_secondaries", true, &link->manager->sysctl_shadow);
return sysctl_write_ip_property_boolean(AF_INET, link->ifname, "promote_secondaries", true, manager_get_sysctl_shadow(link->manager));
}
int link_set_sysctl(Link *link) {

View File

@ -5602,7 +5602,7 @@ static int run_container(
}
/* Note: we do not use SD_EVENT_SIGNAL_PROCMASK or sd_event_set_signal_exit(), since we want the
* signals to be block continously, even if we destroy the event loop and allocate a new one on
* 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) {

View File

@ -6,6 +6,7 @@
#include "sd-daemon.h"
#include "bpf-dlopen.h"
#include "bpf-link.h"
#include "build-path.h"
#include "common-signal.h"
#include "env-util.h"
@ -141,8 +142,7 @@ Manager* manager_free(Manager *m) {
#if HAVE_VMLINUX_H
sd_event_source_disable_unref(m->userns_restrict_bpf_ring_buffer_event_source);
if (m->userns_restrict_bpf_ring_buffer)
sym_ring_buffer__free(m->userns_restrict_bpf_ring_buffer);
bpf_ring_buffer_free(m->userns_restrict_bpf_ring_buffer);
userns_restrict_bpf_free(m->userns_restrict_bpf);
#endif

View File

@ -404,6 +404,10 @@ typedef struct Partition {
PartitionEncryptedVolume *encrypted_volume;
char *supplement_for_name;
struct Partition *supplement_for, *supplement_target_for;
struct Partition *suppressing;
struct Partition *siblings[_VERITY_MODE_MAX];
LIST_FIELDS(struct Partition, partitions);
@ -411,6 +415,7 @@ typedef struct Partition {
#define PARTITION_IS_FOREIGN(p) (!(p)->definition_path)
#define PARTITION_EXISTS(p) (!!(p)->current_partition)
#define PARTITION_SUPPRESSED(p) ((p)->supplement_for && (p)->supplement_for->suppressing == (p))
struct FreeArea {
Partition *after;
@ -520,6 +525,28 @@ static Partition *partition_new(void) {
return p;
}
static void partition_unlink_supplement(Partition *p) {
assert(p);
assert(!p->supplement_for || !p->supplement_target_for); /* Can't be both */
if (p->supplement_target_for) {
assert(p->supplement_target_for->supplement_for == p);
p->supplement_target_for->supplement_for = NULL;
}
if (p->supplement_for) {
assert(p->supplement_for->supplement_target_for == p);
assert(!p->supplement_for->suppressing || p->supplement_for->suppressing == p);
p->supplement_for->supplement_target_for = p->supplement_for->suppressing = NULL;
}
p->supplement_for_name = mfree(p->supplement_for_name);
p->supplement_target_for = p->supplement_for = p->suppressing = NULL;
}
static Partition* partition_free(Partition *p) {
if (!p)
return NULL;
@ -563,6 +590,8 @@ static Partition* partition_free(Partition *p) {
partition_encrypted_volume_free(p->encrypted_volume);
partition_unlink_supplement(p);
return mfree(p);
}
@ -608,6 +637,8 @@ static void partition_foreignize(Partition *p) {
p->n_mountpoints = 0;
p->encrypted_volume = partition_encrypted_volume_free(p->encrypted_volume);
partition_unlink_supplement(p);
}
static bool partition_type_exclude(const GptPartitionType *type) {
@ -740,6 +771,10 @@ static void partition_drop_or_foreignize(Partition *p) {
p->dropped = true;
p->allocated_to_area = NULL;
/* If a supplement partition is dropped, we don't want to merge in its settings. */
if (PARTITION_SUPPRESSED(p))
p->supplement_for->suppressing = NULL;
}
}
@ -775,7 +810,7 @@ static bool context_drop_or_foreignize_one_priority(Context *context) {
}
static uint64_t partition_min_size(const Context *context, const Partition *p) {
uint64_t sz;
uint64_t sz, override_min;
assert(context);
assert(p);
@ -817,11 +852,13 @@ static uint64_t partition_min_size(const Context *context, const Partition *p) {
sz = d;
}
return MAX(round_up_size(p->size_min != UINT64_MAX ? p->size_min : DEFAULT_MIN_SIZE, context->grain_size), sz);
override_min = p->suppressing ? MAX(p->size_min, p->suppressing->size_min) : p->size_min;
return MAX(round_up_size(override_min != UINT64_MAX ? override_min : DEFAULT_MIN_SIZE, context->grain_size), sz);
}
static uint64_t partition_max_size(const Context *context, const Partition *p) {
uint64_t sm;
uint64_t sm, override_max;
/* Calculate how large the partition may become at max. This is generally the configured maximum
* size, except when it already exists and is larger than that. In that case it's the existing size,
@ -839,10 +876,11 @@ static uint64_t partition_max_size(const Context *context, const Partition *p) {
if (p->verity == VERITY_SIG)
return VERITY_SIG_SIZE;
if (p->size_max == UINT64_MAX)
override_max = p->suppressing ? MIN(p->size_max, p->suppressing->size_max) : p->size_max;
if (override_max == UINT64_MAX)
return UINT64_MAX;
sm = round_down_size(p->size_max, context->grain_size);
sm = round_down_size(override_max, context->grain_size);
if (p->current_size != UINT64_MAX)
sm = MAX(p->current_size, sm);
@ -851,13 +889,17 @@ static uint64_t partition_max_size(const Context *context, const Partition *p) {
}
static uint64_t partition_min_padding(const Partition *p) {
uint64_t override_min;
assert(p);
return p->padding_min != UINT64_MAX ? p->padding_min : 0;
override_min = p->suppressing ? MAX(p->padding_min, p->suppressing->padding_min) : p->padding_min;
return override_min != UINT64_MAX ? override_min : 0;
}
static uint64_t partition_max_padding(const Partition *p) {
assert(p);
return p->padding_max;
return p->suppressing ? MIN(p->padding_max, p->suppressing->padding_max) : p->padding_max;
}
static uint64_t partition_min_size_with_padding(Context *context, const Partition *p) {
@ -977,14 +1019,22 @@ static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_
uint64_t required;
FreeArea *a = NULL;
/* Skip partitions we already dropped or that already exist */
if (p->dropped || PARTITION_EXISTS(p))
if (p->dropped || PARTITION_IS_FOREIGN(p) || PARTITION_SUPPRESSED(p))
continue;
/* How much do we need to fit? */
required = partition_min_size_with_padding(context, p);
assert(required % context->grain_size == 0);
/* For existing partitions, we should verify that they'll actually fit */
if (PARTITION_EXISTS(p)) {
if (p->current_size + p->current_padding < required)
return false; /* 😢 We won't be able to grow to the required min size! */
continue;
}
/* For new partitions, see if there's a free area big enough */
for (size_t i = 0; i < context->n_free_areas; i++) {
a = context->free_areas[i];
@ -1007,6 +1057,57 @@ static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_
return true;
}
static bool context_unmerge_and_allocate_partitions(Context *context) {
assert(context);
/* This should only be called after plain context_allocate_partitions fails. This algorithm will
* try, in the order that minimizes the number of created supplement partitions, all combinations of
* un-suppressing supplement partitions until it finds one that works. */
/* First, let's try to un-suppress just one supplement partition and see if that gets us anywhere */
LIST_FOREACH(partitions, p, context->partitions) {
Partition *unsuppressed;
if (!p->suppressing)
continue;
unsuppressed = TAKE_PTR(p->suppressing);
if (context_allocate_partitions(context, NULL))
return true;
p->suppressing = unsuppressed;
}
/* Looks like not. So we have to un-suppress at least two partitions. We can do this recursively */
LIST_FOREACH(partitions, p, context->partitions) {
Partition *unsuppressed;
if (!p->suppressing)
continue;
unsuppressed = TAKE_PTR(p->suppressing);
if (context_unmerge_and_allocate_partitions(context))
return true;
p->suppressing = unsuppressed;
}
/* No combination of un-suppressed supplements made it possible to fit the partitions */
return false;
}
static uint32_t partition_weight(const Partition *p) {
assert(p);
return p->suppressing ? p->suppressing->weight : p->weight;
}
static uint32_t partition_padding_weight(const Partition *p) {
assert(p);
return p->suppressing ? p->suppressing->padding_weight : p->padding_weight;
}
static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) {
uint64_t weight_sum = 0;
@ -1020,13 +1121,11 @@ static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) {
if (p->padding_area != a && p->allocated_to_area != a)
continue;
if (p->weight > UINT64_MAX - weight_sum)
if (!INC_SAFE(&weight_sum, partition_weight(p)))
goto overflow_sum;
weight_sum += p->weight;
if (p->padding_weight > UINT64_MAX - weight_sum)
if (!INC_SAFE(&weight_sum, partition_padding_weight(p)))
goto overflow_sum;
weight_sum += p->padding_weight;
}
*ret = weight_sum;
@ -1091,7 +1190,6 @@ static bool context_grow_partitions_phase(
* get any additional room from the left-overs. Similar, if two partitions have the same weight they
* should get the same space if possible, even if one has a smaller minimum size than the other. */
LIST_FOREACH(partitions, p, context->partitions) {
/* Look only at partitions associated with this free area, i.e. immediately
* preceding it, or allocated into it */
if (p->allocated_to_area != a && p->padding_area != a)
@ -1099,11 +1197,14 @@ static bool context_grow_partitions_phase(
if (p->new_size == UINT64_MAX) {
uint64_t share, rsz, xsz;
uint32_t weight;
bool charge = false;
weight = partition_weight(p);
/* Calculate how much this space this partition needs if everyone would get
* the weight based share */
share = scale_by_weight(*span, p->weight, *weight_sum);
share = scale_by_weight(*span, weight, *weight_sum);
rsz = partition_min_size(context, p);
xsz = partition_max_size(context, p);
@ -1143,15 +1244,18 @@ static bool context_grow_partitions_phase(
if (charge) {
*span = charge_size(context, *span, p->new_size);
*weight_sum = charge_weight(*weight_sum, p->weight);
*weight_sum = charge_weight(*weight_sum, weight);
}
}
if (p->new_padding == UINT64_MAX) {
uint64_t share, rsz, xsz;
uint32_t padding_weight;
bool charge = false;
share = scale_by_weight(*span, p->padding_weight, *weight_sum);
padding_weight = partition_padding_weight(p);
share = scale_by_weight(*span, padding_weight, *weight_sum);
rsz = partition_min_padding(p);
xsz = partition_max_padding(p);
@ -1170,7 +1274,7 @@ static bool context_grow_partitions_phase(
if (charge) {
*span = charge_size(context, *span, p->new_padding);
*weight_sum = charge_weight(*weight_sum, p->padding_weight);
*weight_sum = charge_weight(*weight_sum, padding_weight);
}
}
}
@ -1638,8 +1742,9 @@ static int config_parse_exclude_files(
const char *rvalue,
void *data,
void *userdata) {
_cleanup_free_ char *resolved = NULL;
char ***exclude_files = ASSERT_PTR(data);
const char *p = ASSERT_PTR(rvalue);
int r;
if (isempty(rvalue)) {
@ -1647,10 +1752,23 @@ static int config_parse_exclude_files(
return 0;
}
r = specifier_printf(rvalue, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved);
for (;;) {
_cleanup_free_ char *word = NULL, *resolved = NULL;
r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", p);
return 0;
}
if (r == 0)
return 0;
r = specifier_printf(word, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to expand specifiers in ExcludeFiles= path, ignoring: %s", rvalue);
"Failed to expand specifiers in %s path, ignoring: %s", lvalue, word);
return 0;
}
@ -1660,6 +1778,7 @@ static int config_parse_exclude_files(
if (strv_consume(exclude_files, TAKE_PTR(resolved)) < 0)
return log_oom();
}
return 0;
}
@ -2155,7 +2274,9 @@ static int partition_finalize_fstype(Partition *p, const char *path) {
static bool partition_needs_populate(const Partition *p) {
assert(p);
return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories) || !strv_isempty(p->make_symlinks);
assert(!p->supplement_for || !p->suppressing); /* Avoid infinite recursion */
return !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories) || !strv_isempty(p->make_symlinks) ||
(p->suppressing && partition_needs_populate(p->suppressing));
}
static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
@ -2196,6 +2317,7 @@ static int partition_read_definition(Partition *p, const char *path, const char
{ "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p },
{ "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression },
{ "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level },
{ "Partition", "SupplementFor", config_parse_string, 0, &p->supplement_for_name },
{}
};
_cleanup_free_ char *filename = NULL;
@ -2320,6 +2442,18 @@ static int partition_read_definition(Partition *p, const char *path, const char
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"DefaultSubvolume= must be one of the paths in Subvolumes=.");
if (p->supplement_for_name) {
if (!filename_part_is_valid(p->supplement_for_name))
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"SupplementFor= is an invalid filename: %s",
p->supplement_for_name);
if (p->copy_blocks_path || p->copy_blocks_auto || p->encrypt != ENCRYPT_OFF ||
p->verity != VERITY_OFF)
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"SupplementFor= cannot be combined with CopyBlocks=/Encrypt=/Verity=");
}
/* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
if ((IN_SET(p->type.designator,
PARTITION_ROOT_VERITY,
@ -2626,6 +2760,58 @@ static int context_copy_from(Context *context) {
return 0;
}
static bool check_cross_def_ranges_valid(uint64_t a_min, uint64_t a_max, uint64_t b_min, uint64_t b_max) {
if (a_min == UINT64_MAX && b_min == UINT64_MAX)
return true;
if (a_max == UINT64_MAX && b_max == UINT64_MAX)
return true;
return MAX(a_min != UINT64_MAX ? a_min : 0, b_min != UINT64_MAX ? b_min : 0) <= MIN(a_max, b_max);
}
static int supplement_find_target(const Context *context, const Partition *supplement, Partition **ret) {
int r;
assert(context);
assert(supplement);
assert(ret);
LIST_FOREACH(partitions, p, context->partitions) {
_cleanup_free_ char *filename = NULL;
if (p == supplement)
continue;
r = path_extract_filename(p->definition_path, &filename);
if (r < 0)
return log_error_errno(r,
"Failed to extract filename from path '%s': %m",
p->definition_path);
*ASSERT_PTR(endswith(filename, ".conf")) = 0; /* Remove the file extension */
if (!streq(supplement->supplement_for_name, filename))
continue;
if (p->supplement_for_name)
return log_syntax(NULL, LOG_ERR, supplement->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"SupplementFor= target is itself configured as a supplement.");
if (p->suppressing)
return log_syntax(NULL, LOG_ERR, supplement->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"SupplementFor= target already has a supplement defined: %s",
p->suppressing->definition_path);
*ret = p;
return 0;
}
return log_syntax(NULL, LOG_ERR, supplement->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"Couldn't find target partition for SupplementFor=%s",
supplement->supplement_for_name);
}
static int context_read_definitions(Context *context) {
_cleanup_strv_free_ char **files = NULL;
Partition *last = LIST_FIND_TAIL(partitions, context->partitions);
@ -2717,7 +2903,33 @@ static int context_read_definitions(Context *context) {
if (dp->minimize == MINIMIZE_OFF && !(dp->copy_blocks_path || dp->copy_blocks_auto))
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"Minimize= set for verity hash partition but data partition does not set CopyBlocks= or Minimize=.");
}
LIST_FOREACH(partitions, p, context->partitions) {
Partition *tgt = NULL;
if (!p->supplement_for_name)
continue;
r = supplement_find_target(context, p, &tgt);
if (r < 0)
return r;
if (tgt->copy_blocks_path || tgt->copy_blocks_auto || tgt->encrypt != ENCRYPT_OFF ||
tgt->verity != VERITY_OFF)
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"SupplementFor= target uses CopyBlocks=/Encrypt=/Verity=");
if (!check_cross_def_ranges_valid(p->size_min, p->size_max, tgt->size_min, tgt->size_max))
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"SizeMinBytes= larger than SizeMaxBytes= when merged with SupplementFor= target.");
if (!check_cross_def_ranges_valid(p->padding_min, p->padding_max, tgt->padding_min, tgt->padding_max))
return log_syntax(NULL, LOG_ERR, p->definition_path, 1, SYNTHETIC_ERRNO(EINVAL),
"PaddingMinBytes= larger than PaddingMaxBytes= when merged with SupplementFor= target.");
p->supplement_for = tgt;
tgt->suppressing = tgt->supplement_target_for = p;
}
return 0;
@ -3101,6 +3313,10 @@ static int context_load_partition_table(Context *context) {
}
}
LIST_FOREACH(partitions, p, context->partitions)
if (PARTITION_SUPPRESSED(p) && PARTITION_EXISTS(p))
p->supplement_for->suppressing = NULL;
add_initial_free_area:
nsectors = fdisk_get_nsectors(c);
assert(nsectors <= UINT64_MAX/secsz);
@ -3192,6 +3408,11 @@ static void context_unload_partition_table(Context *context) {
p->current_uuid = SD_ID128_NULL;
p->current_label = mfree(p->current_label);
/* A supplement partition is only ever un-suppressed if the existing partition table prevented
* us from suppressing it. So when unloading the partition table, we must re-suppress. */
if (p->supplement_for)
p->supplement_for->suppressing = p;
}
context->start = UINT64_MAX;
@ -4969,6 +5190,31 @@ static int add_exclude_path(const char *path, Hashmap **denylist, DenyType type)
return 0;
}
static int shallow_join_strv(char ***ret, char **a, char **b) {
_cleanup_free_ char **joined = NULL;
char **iter;
assert(ret);
joined = new(char*, strv_length(a) + strv_length(b) + 1);
if (!joined)
return log_oom();
iter = joined;
STRV_FOREACH(i, a)
*(iter++) = *i;
STRV_FOREACH(i, b)
if (!strv_contains(joined, *i))
*(iter++) = *i;
*iter = NULL;
*ret = TAKE_PTR(joined);
return 0;
}
static int make_copy_files_denylist(
Context *context,
const Partition *p,
@ -4977,6 +5223,7 @@ static int make_copy_files_denylist(
Hashmap **ret) {
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
_cleanup_free_ char **override_exclude_src = NULL, **override_exclude_tgt = NULL;
int r;
assert(context);
@ -4996,13 +5243,26 @@ static int make_copy_files_denylist(
/* Add the user configured excludes. */
STRV_FOREACH(e, p->exclude_files_source) {
if (p->suppressing) {
r = shallow_join_strv(&override_exclude_src,
p->exclude_files_source,
p->suppressing->exclude_files_source);
if (r < 0)
return r;
r = shallow_join_strv(&override_exclude_tgt,
p->exclude_files_target,
p->suppressing->exclude_files_target);
if (r < 0)
return r;
}
STRV_FOREACH(e, override_exclude_src ?: p->exclude_files_source) {
r = add_exclude_path(*e, &denylist, endswith(*e, "/") ? DENY_CONTENTS : DENY_INODE);
if (r < 0)
return r;
}
STRV_FOREACH(e, p->exclude_files_target) {
STRV_FOREACH(e, override_exclude_tgt ?: p->exclude_files_target) {
_cleanup_free_ char *path = NULL;
const char *s = path_startswith(*e, target);
@ -5096,6 +5356,7 @@ static int add_subvolume_path(const char *path, Set **subvolumes) {
static int make_subvolumes_strv(const Partition *p, char ***ret) {
_cleanup_strv_free_ char **subvolumes = NULL;
Subvolume *subvolume;
int r;
assert(p);
assert(ret);
@ -5104,6 +5365,18 @@ static int make_subvolumes_strv(const Partition *p, char ***ret) {
if (strv_extend(&subvolumes, subvolume->path) < 0)
return log_oom();
if (p->suppressing) {
_cleanup_strv_free_ char **suppressing = NULL;
r = make_subvolumes_strv(p->suppressing, &suppressing);
if (r < 0)
return r;
r = strv_extend_strv(&subvolumes, suppressing, /* filter_duplicates= */ true);
if (r < 0)
return log_oom();
}
*ret = TAKE_PTR(subvolumes);
return 0;
}
@ -5114,18 +5387,22 @@ static int make_subvolumes_set(
const char *target,
Set **ret) {
_cleanup_strv_free_ char **paths = NULL;
_cleanup_set_free_ Set *subvolumes = NULL;
Subvolume *subvolume;
int r;
assert(p);
assert(target);
assert(ret);
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes) {
r = make_subvolumes_strv(p, &paths);
if (r < 0)
return r;
STRV_FOREACH(subvolume, paths) {
_cleanup_free_ char *path = NULL;
const char *s = path_startswith(subvolume->path, target);
const char *s = path_startswith(*subvolume, target);
if (!s)
continue;
@ -5168,6 +5445,7 @@ static usec_t epoch_or_infinity(void) {
static int do_copy_files(Context *context, Partition *p, const char *root) {
_cleanup_strv_free_ char **subvolumes = NULL;
_cleanup_free_ char **override_copy_files = NULL;
int r;
assert(p);
@ -5177,11 +5455,17 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
if (r < 0)
return r;
if (p->suppressing) {
r = shallow_join_strv(&override_copy_files, p->copy_files, p->suppressing->copy_files);
if (r < 0)
return r;
}
/* copy_tree_at() automatically copies the permissions of source directories to target directories if
* it created them. However, the root directory is created by us, so we have to manually take care
* that it is initialized. We use the first source directory targeting "/" as the metadata source for
* the root directory. */
STRV_FOREACH_PAIR(source, target, p->copy_files) {
STRV_FOREACH_PAIR(source, target, override_copy_files ?: p->copy_files) {
_cleanup_close_ int rfd = -EBADF, sfd = -EBADF;
if (!path_equal(*target, "/"))
@ -5202,7 +5486,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
break;
}
STRV_FOREACH_PAIR(source, target, p->copy_files) {
STRV_FOREACH_PAIR(source, target, override_copy_files ?: p->copy_files) {
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
_cleanup_set_free_ Set *subvolumes_by_source_inode = NULL;
_cleanup_close_ int sfd = -EBADF, pfd = -EBADF, tfd = -EBADF;
@ -5320,6 +5604,7 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
static int do_make_directories(Partition *p, const char *root) {
_cleanup_strv_free_ char **subvolumes = NULL;
_cleanup_free_ char **override_dirs = NULL;
int r;
assert(p);
@ -5329,7 +5614,13 @@ static int do_make_directories(Partition *p, const char *root) {
if (r < 0)
return r;
STRV_FOREACH(d, p->make_directories) {
if (p->suppressing) {
r = shallow_join_strv(&override_dirs, p->make_directories, p->suppressing->make_directories);
if (r < 0)
return r;
}
STRV_FOREACH(d, override_dirs ?: p->make_directories) {
r = mkdir_p_root_full(root, *d, UID_INVALID, GID_INVALID, 0755, epoch_or_infinity(), subvolumes);
if (r < 0)
return log_error_errno(r, "Failed to create directory '%s' in file system: %m", *d);
@ -5377,6 +5668,12 @@ static int make_subvolumes_read_only(Partition *p, const char *root) {
return log_error_errno(r, "Failed to make subvolume '%s' read-only: %m", subvolume->path);
}
if (p->suppressing) {
r = make_subvolumes_read_only(p->suppressing, root);
if (r < 0)
return r;
}
return 0;
}
@ -5408,7 +5705,7 @@ static int partition_populate_directory(Context *context, Partition *p, char **r
assert(ret);
log_info("Populating %s filesystem.", p->format);
log_info("Preparing to populate %s filesystem.", p->format);
r = var_tmp_dir(&vt);
if (r < 0)
@ -5434,7 +5731,7 @@ static int partition_populate_directory(Context *context, Partition *p, char **r
if (r < 0)
return r;
log_info("Successfully populated %s filesystem.", p->format);
log_info("Ready to populate %s filesystem.", p->format);
*ret = TAKE_PTR(root);
return 0;
@ -5496,6 +5793,38 @@ static int partition_populate_filesystem(Context *context, Partition *p, const c
return 0;
}
static int append_btrfs_subvols(char ***l, OrderedHashmap *subvolumes, const char *default_subvolume) {
Subvolume *subvolume;
int r;
assert(l);
ORDERED_HASHMAP_FOREACH(subvolume, subvolumes) {
_cleanup_free_ char *s = NULL, *f = NULL;
s = strdup(subvolume->path);
if (!s)
return log_oom();
f = subvolume_flags_to_string(subvolume->flags);
if (!f)
return log_oom();
if (streq_ptr(subvolume->path, default_subvolume) &&
!strextend_with_separator(&f, ",", "default"))
return log_oom();
if (!isempty(f) && !strextend_with_separator(&s, ":", f))
return log_oom();
r = strv_extend_many(l, "--subvol", s);
if (r < 0)
return log_oom();
}
return 0;
}
static int finalize_extra_mkfs_options(const Partition *p, const char *root, char ***ret) {
_cleanup_strv_free_ char **sv = NULL;
int r;
@ -5510,28 +5839,14 @@ static int finalize_extra_mkfs_options(const Partition *p, const char *root, cha
p->format);
if (partition_needs_populate(p) && root && streq(p->format, "btrfs")) {
Subvolume *subvolume;
ORDERED_HASHMAP_FOREACH(subvolume, p->subvolumes) {
_cleanup_free_ char *s = NULL, *f = NULL;
s = strdup(subvolume->path);
if (!s)
return log_oom();
f = subvolume_flags_to_string(subvolume->flags);
if (!f)
return log_oom();
if (streq_ptr(subvolume->path, p->default_subvolume) && !strextend_with_separator(&f, ",", "default"))
return log_oom();
if (!isempty(f) && !strextend_with_separator(&s, ":", f))
return log_oom();
r = strv_extend_many(&sv, "--subvol", s);
r = append_btrfs_subvols(&sv, p->subvolumes, p->default_subvolume);
if (r < 0)
return log_oom();
return r;
if (p->suppressing) {
r = append_btrfs_subvols(&sv, p->suppressing->subvolumes, NULL);
if (r < 0)
return r;
}
}
@ -8524,7 +8839,7 @@ static int determine_auto_size(Context *c) {
LIST_FOREACH(partitions, p, c->partitions) {
uint64_t m;
if (p->dropped)
if (p->dropped || PARTITION_SUPPRESSED(p))
continue;
m = partition_min_size_with_padding(c, p);
@ -8756,13 +9071,36 @@ static int run(int argc, char *argv[]) {
if (context_allocate_partitions(context, &largest_free_area))
break; /* Success! */
if (!context_drop_or_foreignize_one_priority(context)) {
if (context_unmerge_and_allocate_partitions(context))
break; /* We had to un-suppress a supplement or few, but still success! */
if (context_drop_or_foreignize_one_priority(context))
continue; /* Still no luck. Let's drop a priority and try again. */
/* No more priorities left to drop. This configuration just doesn't fit on this disk... */
r = log_error_errno(SYNTHETIC_ERRNO(ENOSPC),
"Can't fit requested partitions into available free space (%s), refusing.",
FORMAT_BYTES(largest_free_area));
determine_auto_size(context);
return r;
}
LIST_FOREACH(partitions, p, context->partitions) {
if (!p->supplement_for)
continue;
if (PARTITION_SUPPRESSED(p)) {
assert(!p->allocated_to_area);
p->dropped = true;
log_debug("Partition %s can be merged into %s, suppressing supplement.",
p->definition_path, p->supplement_for->definition_path);
} else if (PARTITION_EXISTS(p))
log_info("Partition %s already exists on disk, using supplement verbatim.",
p->definition_path);
else
log_info("Couldn't allocate partitions with %s merged into %s, using supplement verbatim.",
p->definition_path, p->supplement_for->definition_path);
}
/* Now assign free space according to the weight logic */

View File

@ -27,6 +27,7 @@
#include "format-util.h"
#include "fs-util.h"
#include "glyph-util.h"
#include "inotify-util.h"
#include "io-util.h"
#include "iovec-util.h"
#include "keyring-util.h"
@ -36,6 +37,7 @@
#include "missing_syscall.h"
#include "mkdir-label.h"
#include "nulstr-util.h"
#include "path-lookup.h"
#include "plymouth-util.h"
#include "process-util.h"
#include "random-util.h"
@ -57,7 +59,7 @@ static int lookup_key(const char *keyname, key_serial_t *ret) {
assert(keyname);
assert(ret);
serial = request_key("user", keyname, NULL, 0);
serial = request_key("user", keyname, /* callout_info= */ NULL, /* dest_keyring= */ 0);
if (serial == -1)
return negative_errno();
@ -85,6 +87,28 @@ static int retrieve_key(key_serial_t serial, char ***ret) {
return 0;
}
static int get_ask_password_directory_for_flags(AskPasswordFlags flags, char **ret) {
if (FLAGS_SET(flags, ASK_PASSWORD_USER))
return acquire_user_ask_password_directory(ret);
return strdup_to(ret, "/run/systemd/ask-password/"); /* Returns 1, indicating there's a suitable directory */
}
static int touch_ask_password_directory(AskPasswordFlags flags) {
int r;
_cleanup_free_ char *p = NULL;
r = get_ask_password_directory_for_flags(flags, &p);
if (r <= 0)
return r;
r = touch(p);
if (r < 0)
return r;
return 1; /* did something */
}
static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) {
_cleanup_strv_free_erase_ char **l = NULL;
_cleanup_(erase_and_freep) char *p = NULL;
@ -107,7 +131,7 @@ static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **pa
} else if (r != -ENOKEY)
return r;
r = strv_extend_strv(&l, passwords, true);
r = strv_extend_strv(&l, passwords, /* filter_duplicates= */ true);
if (r <= 0)
return r;
@ -129,7 +153,7 @@ static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **pa
log_debug_errno(errno, "Failed to adjust kernel keyring key timeout: %m");
/* Tell everyone to check the keyring */
(void) touch("/run/systemd/ask-password");
(void) touch_ask_password_directory(flags);
log_debug("Added key to kernel keyring as %" PRIi32 ".", serial);
@ -220,16 +244,16 @@ int ask_password_plymouth(
const char *flag_file,
char ***ret) {
_cleanup_close_ int fd = -EBADF, notify = -EBADF;
_cleanup_close_ int fd = -EBADF, inotify_fd = -EBADF;
_cleanup_free_ char *packet = NULL;
ssize_t k;
int r, n;
struct pollfd pollfd[2] = {};
char buffer[LINE_MAX];
size_t p = 0;
enum {
POLL_SOCKET,
POLL_INOTIFY
POLL_INOTIFY,
_POLL_MAX,
};
assert(ret);
@ -240,11 +264,11 @@ int ask_password_plymouth(
const char *message = req && req->message ? req->message : "Password:";
if (flag_file) {
notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
if (notify < 0)
inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
if (inotify_fd < 0)
return -errno;
if (inotify_add_watch(notify, flag_file, IN_ATTRIB) < 0) /* for the link count */
if (inotify_add_watch(inotify_fd, flag_file, IN_ATTRIB) < 0) /* for the link count */
return -errno;
}
@ -266,10 +290,11 @@ int ask_password_plymouth(
CLEANUP_ERASE(buffer);
pollfd[POLL_SOCKET].fd = fd;
pollfd[POLL_SOCKET].events = POLLIN;
pollfd[POLL_INOTIFY].fd = notify;
pollfd[POLL_INOTIFY].events = POLLIN;
struct pollfd pollfd[_POLL_MAX] = {
[POLL_SOCKET] = { .fd = fd, .events = POLLIN },
[POLL_INOTIFY] = { .fd = inotify_fd, .events = POLLIN },
};
size_t n_pollfd = inotify_fd >= 0 ? _POLL_MAX : _POLL_MAX-1;
for (;;) {
usec_t timeout;
@ -282,7 +307,7 @@ int ask_password_plymouth(
if (flag_file && access(flag_file, F_OK) < 0)
return -errno;
r = ppoll_usec(pollfd, notify >= 0 ? 2 : 1, timeout);
r = ppoll_usec(pollfd, n_pollfd, timeout);
if (r == -EINTR)
continue;
if (r < 0)
@ -290,8 +315,8 @@ int ask_password_plymouth(
if (r == 0)
return -ETIME;
if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
(void) flush_fd(notify);
if (inotify_fd >= 0 && pollfd[POLL_INOTIFY].revents != 0)
(void) flush_fd(inotify_fd);
if (pollfd[POLL_SOCKET].revents == 0)
continue;
@ -382,11 +407,10 @@ int ask_password_tty(
};
bool reset_tty = false, dirty = false, use_color = false, press_tab_visible = false;
_cleanup_close_ int cttyfd = -EBADF, notify = -EBADF;
_cleanup_close_ int cttyfd = -EBADF, inotify_fd = -EBADF;
struct termios old_termios, new_termios;
char passphrase[LINE_MAX + 1] = {}, *x;
_cleanup_strv_free_erase_ char **l = NULL;
struct pollfd pollfd[_POLL_MAX];
size_t p = 0, codepoint = 0;
int r;
@ -405,23 +429,36 @@ int ask_password_tty(
message = strjoina(special_glyph(SPECIAL_GLYPH_LOCK_AND_KEY), " ", message);
if (flag_file || (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && keyring)) {
notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
if (notify < 0)
inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
if (inotify_fd < 0)
return -errno;
}
if (flag_file) {
if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0)
if (inotify_add_watch(inotify_fd, flag_file, IN_ATTRIB /* for the link count */) < 0)
return -errno;
}
if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && req && keyring) {
r = ask_password_keyring(req, flags, ret);
if (r >= 0)
return 0;
else if (r != -ENOKEY)
if (r != -ENOKEY)
return r;
if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_ATTRIB /* for mtime */) < 0)
return -errno;
/* Let's watch the askpw directory for mtime changes, which we issue above whenever the
* keyring changes */
_cleanup_free_ char *watch_path = NULL;
r = get_ask_password_directory_for_flags(flags, &watch_path);
if (r < 0)
return r;
if (r > 0) {
_cleanup_close_ int watch_fd = open_mkdir(watch_path, O_CLOEXEC|O_RDONLY, 0755);
if (watch_fd < 0)
return watch_fd;
r = inotify_add_watch_fd(inotify_fd, watch_fd, IN_ONLYDIR|IN_ATTRIB /* for mtime */);
if (r < 0)
return r;
}
}
CLEANUP_ERASE(passphrase);
@ -466,14 +503,11 @@ int ask_password_tty(
reset_tty = true;
}
pollfd[POLL_TTY] = (struct pollfd) {
.fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO,
.events = POLLIN,
};
pollfd[POLL_INOTIFY] = (struct pollfd) {
.fd = notify,
.events = POLLIN,
struct pollfd pollfd[_POLL_MAX] = {
{ .fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO, .events = POLLIN },
{ .fd = inotify_fd, .events = POLLIN },
};
size_t n_pollfd = inotify_fd >= 0 ? _POLL_MAX : _POLL_MAX-1;
for (;;) {
_cleanup_(erase_char) char c;
@ -491,7 +525,7 @@ int ask_password_tty(
goto finish;
}
r = ppoll_usec(pollfd, notify >= 0 ? 2 : 1, timeout);
r = ppoll_usec(pollfd, n_pollfd, timeout);
if (r == -EINTR)
continue;
if (r < 0)
@ -501,8 +535,8 @@ int ask_password_tty(
goto finish;
}
if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0 && keyring) {
(void) flush_fd(notify);
if (inotify_fd >= 0 && pollfd[POLL_INOTIFY].revents != 0 && keyring) {
(void) flush_fd(inotify_fd);
r = ask_password_keyring(req, flags, ret);
if (r >= 0) {
@ -659,20 +693,21 @@ finish:
return r;
}
static int create_socket(char **ret) {
static int create_socket(const char *askpwdir, char **ret) {
_cleanup_free_ char *path = NULL;
union sockaddr_union sa;
socklen_t sa_len;
_cleanup_close_ int fd = -EBADF;
int r;
assert(askpwdir);
assert(ret);
fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0)
return -errno;
if (asprintf(&path, "/run/systemd/ask-password/sck.%" PRIx64, random_u64()) < 0)
if (asprintf(&path, "%s/sck.%" PRIx64, askpwdir, random_u64()) < 0)
return -ENOMEM;
r = sockaddr_un_set_path(&sa.un, path);
@ -699,19 +734,17 @@ int ask_password_agent(
char ***ret) {
enum {
FD_SOCKET,
FD_SIGNAL,
FD_INOTIFY,
_FD_MAX
POLL_SOCKET,
POLL_SIGNAL,
POLL_INOTIFY,
_POLL_MAX
};
_cleanup_close_ int socket_fd = -EBADF, signal_fd = -EBADF, notify = -EBADF, fd = -EBADF;
char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
char final[sizeof(temp)] = "";
_cleanup_free_ char *socket_name = NULL;
_cleanup_close_ int socket_fd = -EBADF, signal_fd = -EBADF, inotify_fd = -EBADF, dfd = -EBADF;
_cleanup_(unlink_and_freep) char *socket_name = NULL;
_cleanup_free_ char *temp = NULL, *final = NULL;
_cleanup_strv_free_erase_ char **l = NULL;
_cleanup_fclose_ FILE *f = NULL;
struct pollfd pollfd[_FD_MAX];
sigset_t mask, oldmask;
int r;
@ -727,7 +760,20 @@ int ask_password_agent(
assert_se(sigset_add_many(&mask, SIGINT, SIGTERM) >= 0);
assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0);
(void) mkdir_p_label("/run/systemd/ask-password", 0755);
_cleanup_free_ char *askpwdir = NULL;
r = get_ask_password_directory_for_flags(flags, &askpwdir);
if (r < 0)
goto finish;
if (r == 0) {
r = -ENXIO;
goto finish;
}
dfd = open_mkdir(askpwdir, O_RDONLY|O_CLOEXEC, 0755);
if (dfd < 0) {
r = log_debug_errno(dfd, "Failed to open directory '%s': %m", askpwdir);
goto finish;
}
if (FLAGS_SET(flags, ASK_PASSWORD_ACCEPT_CACHED) && req && req->keyring) {
r = ask_password_keyring(req, flags, ret);
@ -737,30 +783,25 @@ int ask_password_agent(
} else if (r != -ENOKEY)
goto finish;
notify = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
if (notify < 0) {
inotify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
if (inotify_fd < 0) {
r = -errno;
goto finish;
}
r = RET_NERRNO(inotify_add_watch(notify, "/run/systemd/ask-password", IN_ATTRIB /* for mtime */));
r = inotify_add_watch_fd(inotify_fd, dfd, IN_ONLYDIR|IN_ATTRIB /* for mtime */);
if (r < 0)
goto finish;
}
fd = mkostemp_safe(temp);
if (fd < 0) {
r = fd;
if (asprintf(&final, "ask.%" PRIu64, random_u64()) < 0) {
r = -ENOMEM;
goto finish;
}
(void) fchmod(fd, 0644);
f = take_fdopen(&fd, "w");
if (!f) {
r = -errno;
r = fopen_temporary_at(dfd, final, &f, &temp);
if (r < 0)
goto finish;
}
signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (signal_fd < 0) {
@ -768,7 +809,7 @@ int ask_password_agent(
goto finish;
}
socket_fd = create_socket(&socket_name);
socket_fd = create_socket(askpwdir, &socket_name);
if (socket_fd < 0) {
r = socket_fd;
goto finish;
@ -800,27 +841,28 @@ int ask_password_agent(
fprintf(f, "Id=%s\n", req->id);
}
if (fchmod(fileno(f), 0644) < 0) {
r = -errno;
goto finish;
}
r = fflush_and_check(f);
if (r < 0)
goto finish;
memcpy(final, temp, sizeof(temp));
final[sizeof(final)-11] = 'a';
final[sizeof(final)-10] = 's';
final[sizeof(final)-9] = 'k';
r = RET_NERRNO(rename(temp, final));
if (r < 0)
if (renameat(dfd, temp, dfd, final) < 0) {
r = -errno;
goto finish;
}
zero(pollfd);
pollfd[FD_SOCKET].fd = socket_fd;
pollfd[FD_SOCKET].events = POLLIN;
pollfd[FD_SIGNAL].fd = signal_fd;
pollfd[FD_SIGNAL].events = POLLIN;
pollfd[FD_INOTIFY].fd = notify;
pollfd[FD_INOTIFY].events = POLLIN;
temp = mfree(temp);
struct pollfd pollfd[_POLL_MAX] = {
[POLL_SOCKET] = { .fd = socket_fd, .events = POLLIN },
[POLL_SIGNAL] = { .fd = signal_fd, .events = POLLIN },
[POLL_INOTIFY] = { .fd = inotify_fd, .events = POLLIN },
};
size_t n_pollfd = inotify_fd >= 0 ? _POLL_MAX : _POLL_MAX - 1;
for (;;) {
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
@ -835,7 +877,7 @@ int ask_password_agent(
else
timeout = USEC_INFINITY;
r = ppoll_usec(pollfd, notify >= 0 ? _FD_MAX : _FD_MAX - 1, timeout);
r = ppoll_usec(pollfd, n_pollfd, timeout);
if (r == -EINTR)
continue;
if (r < 0)
@ -845,13 +887,13 @@ int ask_password_agent(
goto finish;
}
if (pollfd[FD_SIGNAL].revents & POLLIN) {
if (pollfd[POLL_SIGNAL].revents & POLLIN) {
r = -EINTR;
goto finish;
}
if (notify >= 0 && pollfd[FD_INOTIFY].revents != 0) {
(void) flush_fd(notify);
if (inotify_fd >= 0 && pollfd[POLL_INOTIFY].revents != 0) {
(void) flush_fd(inotify_fd);
if (req && req->keyring) {
r = ask_password_keyring(req, flags, ret);
@ -863,10 +905,10 @@ int ask_password_agent(
}
}
if (pollfd[FD_SOCKET].revents == 0)
if (pollfd[POLL_SOCKET].revents == 0)
continue;
if (pollfd[FD_SOCKET].revents != POLLIN) {
if (pollfd[POLL_SOCKET].revents != POLLIN) {
r = -EIO;
goto finish;
}
@ -911,8 +953,8 @@ int ask_password_agent(
continue;
}
if (ucred->uid != 0) {
log_debug("Got request from unprivileged user. Ignoring.");
if (ucred->uid != getuid() && ucred->uid != 0) {
log_debug("Got response from bad user. Ignoring.");
continue;
}
@ -951,13 +993,13 @@ int ask_password_agent(
r = 0;
finish:
if (socket_name)
(void) unlink(socket_name);
(void) unlink(temp);
if (final[0])
(void) unlink(final);
if (temp) {
assert(dfd >= 0);
(void) unlinkat(dfd, temp, 0);
} else if (final) {
assert(dfd >= 0);
(void) unlinkat(dfd, final, 0);
}
assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0);
return r;
@ -1021,3 +1063,18 @@ int ask_password_auto(
return -EUNATCH;
}
int acquire_user_ask_password_directory(char **ret) {
int r;
r = xdg_user_runtime_dir(ret, "systemd/ask-password");
if (r == -ENXIO) {
if (ret)
*ret = NULL;
return 0;
}
if (r < 0)
return r;
return 1;
}

View File

@ -16,6 +16,7 @@ typedef enum AskPasswordFlags {
ASK_PASSWORD_NO_CREDENTIAL = 1 << 7, /* never use $CREDENTIALS_DIRECTORY data */
ASK_PASSWORD_HIDE_EMOJI = 1 << 8, /* hide the lock and key emoji */
ASK_PASSWORD_HEADLESS = 1 << 9, /* headless mode: never query interactively */
ASK_PASSWORD_USER = 1 << 10, /* query only our own agents, not any system password agents */
} AskPasswordFlags;
/* Encapsulates the mostly static fields of a password query */
@ -31,3 +32,5 @@ int ask_password_tty(int tty_fd, const AskPasswordRequest *req, usec_t until, As
int ask_password_plymouth(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flags, const char *flag_file, char ***ret);
int ask_password_agent(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flag, char ***ret);
int ask_password_auto(const AskPasswordRequest *req, usec_t until, AskPasswordFlags flag, char ***ret);
int acquire_user_ask_password_directory(char **ret);

View File

@ -31,7 +31,7 @@ int bpf_serialize_link(FILE *f, FDSet *fds, const char *key, struct bpf_link *li
return serialize_fd(f, fds, key, sym_bpf_link__fd(link));
}
struct bpf_link *bpf_link_free(struct bpf_link *link) {
struct bpf_link* bpf_link_free(struct bpf_link *link) {
/* If libbpf wasn't dlopen()ed, sym_bpf_link__destroy might be unresolved (NULL), so let's not try to
* call it if link is NULL. link might also be a non-null "error pointer", but such a value can only
* originate from a call to libbpf, but that means that libbpf is available, and we can let
@ -41,3 +41,10 @@ struct bpf_link *bpf_link_free(struct bpf_link *link) {
return NULL;
}
struct ring_buffer* bpf_ring_buffer_free(struct ring_buffer *rb) {
if (rb) /* See the comment in bpf_link_free(). */
sym_ring_buffer__free(rb);
return NULL;
}

View File

@ -12,5 +12,8 @@ bool bpf_can_link_program(struct bpf_program *prog);
int bpf_serialize_link(FILE *f, FDSet *fds, const char *key, struct bpf_link *link);
struct bpf_link *bpf_link_free(struct bpf_link *p);
struct bpf_link* bpf_link_free(struct bpf_link *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct bpf_link *, bpf_link_free);
struct ring_buffer* bpf_ring_buffer_free(struct ring_buffer *rb);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct ring_buffer *, bpf_ring_buffer_free);

View File

@ -465,10 +465,6 @@ int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const s
assert(path);
assert(st);
r = hashmap_ensure_allocated(stats_by_path, &path_hash_ops_free_free);
if (r < 0)
return r;
st_copy = newdup(struct stat, st, 1);
if (!st_copy)
return -ENOMEM;
@ -477,7 +473,7 @@ int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const s
if (!path_copy)
return -ENOMEM;
r = hashmap_put(*stats_by_path, path_copy, st_copy);
r = hashmap_ensure_put(stats_by_path, &path_hash_ops_free_free, path_copy, st_copy);
if (r < 0)
return r;
@ -502,12 +498,12 @@ static int config_parse_many_files(
_cleanup_ordered_hashmap_free_ OrderedHashmap *dropins = NULL;
_cleanup_set_free_ Set *inodes = NULL;
struct stat st;
int r;
int r, level = FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG;
if (ret_stats_by_path) {
stats_by_path = hashmap_new(&path_hash_ops_free_free);
if (!stats_by_path)
return -ENOMEM;
return log_oom_full(level);
}
STRV_FOREACH(fn, files) {
@ -518,14 +514,14 @@ static int config_parse_many_files(
if (r == -ENOENT)
continue;
if (r < 0)
return r;
return log_full_errno(level, r, "Failed to open %s: %m", *fn);
int fd = fileno(f);
r = ordered_hashmap_ensure_put(&dropins, &config_file_hash_ops_fclose, *fn, f);
if (r < 0) {
assert(r != -EEXIST);
return r;
assert(r == -ENOMEM);
return log_oom_full(level);
}
assert(r > 0);
TAKE_PTR(f);
@ -535,14 +531,14 @@ static int config_parse_many_files(
_cleanup_free_ struct stat *st_dropin = new(struct stat, 1);
if (!st_dropin)
return -ENOMEM;
return log_oom_full(level);
if (fstat(fd, st_dropin) < 0)
return -errno;
return log_full_errno(level, errno, "Failed to stat %s: %m", *fn);
r = set_ensure_consume(&inodes, &inode_hash_ops, TAKE_PTR(st_dropin));
if (r < 0)
return r;
return log_oom_full(level);
}
/* First read the first found main config file. */
@ -553,11 +549,11 @@ static int config_parse_many_files(
if (r == -ENOENT)
continue;
if (r < 0)
return r;
return log_full_errno(level, r, "Failed to open %s: %m", *fn);
if (inodes) {
if (fstat(fileno(f), &st) < 0)
return -errno;
return log_full_errno(level, errno, "Failed to stat %s: %m", *fn);
if (set_contains(inodes, &st)) {
log_debug("%s: symlink to/symlinked as drop-in, will be read later.", *fn);
@ -567,13 +563,13 @@ static int config_parse_many_files(
r = config_parse(/* unit= */ NULL, *fn, f, sections, lookup, table, flags, userdata, &st);
if (r < 0)
return r;
return r; /* config_parse() logs internally. */
assert(r > 0);
if (ret_stats_by_path) {
r = hashmap_put_stats_by_path(&stats_by_path, *fn, &st);
if (r < 0)
return r;
return log_full_errno(level, r, "Failed to save stats of %s: %m", *fn);
}
break;
@ -586,13 +582,13 @@ static int config_parse_many_files(
ORDERED_HASHMAP_FOREACH_KEY(f_dropin, path_dropin, dropins) {
r = config_parse(/* unit= */ NULL, path_dropin, f_dropin, sections, lookup, table, flags, userdata, &st);
if (r < 0)
return r;
return r; /* config_parse() logs internally. */
assert(r > 0);
if (ret_stats_by_path) {
r = hashmap_put_stats_by_path(&stats_by_path, path_dropin, &st);
if (r < 0)
return r;
return log_full_errno(level, r, "Failed to save stats of %s: %m", path_dropin);
}
}
@ -625,11 +621,12 @@ int config_parse_many(
r = conf_files_list_dropins(&files, dropin_dirname, root, conf_file_dirs);
if (r < 0)
return r;
return log_full_errno(FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG, r,
"Failed to list up drop-in configs in %s: %m", dropin_dirname);
r = config_parse_many_files(root, conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path);
if (r < 0)
return r;
return r; /* config_parse_many_files() logs internally. */
if (ret_dropin_files)
*ret_dropin_files = TAKE_PTR(files);
@ -650,22 +647,16 @@ int config_parse_standard_file_with_dropins_full(
const char* const *conf_paths = (const char* const*) CONF_PATHS_STRV("");
_cleanup_strv_free_ char **configs = NULL;
int r;
int r, level = FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_WARNING : LOG_DEBUG;
/* Build the list of main config files */
r = strv_extend_strv_biconcat(&configs, root, conf_paths, main_file);
if (r < 0) {
if (flags & CONFIG_PARSE_WARN)
log_oom();
return r;
}
if (r < 0)
return log_oom_full(level);
_cleanup_free_ char *dropin_dirname = strjoin(main_file, ".d");
if (!dropin_dirname) {
if (flags & CONFIG_PARSE_WARN)
log_oom();
return -ENOMEM;
}
if (!dropin_dirname)
return log_oom_full(level);
return config_parse_many(
(const char* const*) configs,

View File

@ -66,9 +66,5 @@ 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,30 +61,19 @@ int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader) {
return 0;
}
int efi_loader_get_device_part_uuid(sd_id128_t *ret) {
_cleanup_free_ char *p = NULL;
int r;
unsigned parsed[16];
static int get_device_part_uuid(const char *variable, sd_id128_t *ret) {
if (!is_efi_boot())
return -EOPNOTSUPP;
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderDevicePartUUID), &p);
if (r < 0)
return r;
return efi_get_variable_id128(variable, 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_loader_get_device_part_uuid(sd_id128_t *ret) {
return get_device_part_uuid(EFI_LOADER_VARIABLE(LoaderDevicePartUUID), ret);
}
if (ret)
for (unsigned i = 0; i < ELEMENTSOF(parsed); i++)
ret->bytes[i] = parsed[i];
return 0;
int efi_stub_get_device_part_uuid(sd_id128_t *ret) {
return get_device_part_uuid(EFI_LOADER_VARIABLE(StubDevicePartUUID), ret);
}
int efi_loader_get_entries(char ***ret) {
@ -353,6 +342,22 @@ 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,6 +11,7 @@
#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);
@ -23,6 +24,8 @@ 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) {
@ -58,6 +61,10 @@ 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

@ -968,7 +968,9 @@ void log_setup_generator(void) {
/* This effectively means: journal for per-user generators, kmsg otherwise */
log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
}
} else
log_set_target(LOG_TARGET_AUTO);
log_setup();
log_parse_environment();
log_open();
}

View File

@ -14,6 +14,7 @@
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",
};
@ -159,6 +160,16 @@ 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,6 +8,7 @@
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

@ -28,17 +28,16 @@ static int output_waiting_jobs(sd_bus *bus, Table *table, uint32_t id, const cha
while ((r = sd_bus_message_read(reply, "(usssoo)", &other_id, &name, &type, NULL, NULL, NULL)) > 0) {
_cleanup_free_ char *row = NULL;
int rc;
if (asprintf(&row, "%s %u (%s/%s)", prefix, other_id, name, type) < 0)
return log_oom();
rc = table_add_many(table,
r = table_add_many(table,
TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_RIGHT),
TABLE_STRING, row,
TABLE_EMPTY,
TABLE_EMPTY);
if (rc < 0)
if (r < 0)
return table_log_add_error(r);
}

View File

@ -197,7 +197,7 @@ _unused_ static void test_compress_stream(const char *compression,
ASSERT_OK(compress(src, dst, -1, &uncompressed_size));
if (cat) {
assert_se(asprintf(&cmd, "%s %s | diff %s -", cat, pattern, srcfile) > 0);
assert_se(asprintf(&cmd, "%s %s | diff '%s' -", cat, pattern, srcfile) > 0);
assert_se(system(cmd) == 0);
}
@ -212,7 +212,7 @@ _unused_ static void test_compress_stream(const char *compression,
r = decompress(dst, dst2, st.st_size);
assert_se(r == 0);
assert_se(asprintf(&cmd2, "diff %s %s", srcfile, pattern2) > 0);
assert_se(asprintf(&cmd2, "diff '%s' %s", srcfile, pattern2) > 0);
assert_se(system(cmd2) == 0);
log_debug("/* test faulty decompression */");

View File

@ -170,6 +170,8 @@ typedef struct Item {
bool try_replace:1;
bool purge:1;
OperationMask done;
} Item;
@ -3046,6 +3048,9 @@ 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))
@ -3602,7 +3607,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;
unbase64 = false, from_cred = false, missing_user_or_group = false, purge = false;
int r;
assert(fname);
@ -3668,6 +3673,8 @@ 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),
@ -3684,6 +3691,7 @@ 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))
@ -3838,6 +3846,12 @@ 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

@ -55,40 +55,35 @@ static bool arg_console = false;
static const char *arg_device = NULL;
static int send_passwords(const char *socket_name, char **passwords) {
_cleanup_(erase_and_freep) char *packet = NULL;
_cleanup_close_ int socket_fd = -EBADF;
union sockaddr_union sa;
socklen_t sa_len;
size_t packet_length = 1;
char *d;
ssize_t n;
int r;
assert(socket_name);
union sockaddr_union sa;
r = sockaddr_un_set_path(&sa.un, socket_name);
if (r < 0)
return r;
sa_len = r;
socklen_t sa_len = r;
size_t packet_length = 1;
STRV_FOREACH(p, passwords)
packet_length += strlen(*p) + 1;
packet = new(char, packet_length);
_cleanup_(erase_and_freep) char *packet = new(char, packet_length);
if (!packet)
return -ENOMEM;
packet[0] = '+';
d = packet + 1;
char *d = packet + 1;
STRV_FOREACH(p, passwords)
d = stpcpy(d, *p) + 1;
socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
_cleanup_close_ int socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
if (socket_fd < 0)
return log_debug_errno(errno, "socket(): %m");
n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, sa_len);
ssize_t n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, sa_len);
if (n < 0)
return log_debug_errno(errno, "sendto(): %m");
@ -96,12 +91,9 @@ static int send_passwords(const char *socket_name, char **passwords) {
}
static bool wall_tty_match(const char *path, bool is_local, void *userdata) {
_cleanup_free_ char *p = NULL;
_cleanup_close_ int fd = -EBADF;
struct stat st;
assert(path_is_absolute(path));
struct stat st;
if (lstat(path, &st) < 0) {
log_debug_errno(errno, "Failed to stat %s: %m", path);
return true;
@ -120,12 +112,13 @@ static bool wall_tty_match(const char *path, bool is_local, void *userdata) {
* advantage that the block will automatically go away if the
* process dies. */
_cleanup_free_ char *p = NULL;
if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) {
log_oom();
log_oom_debug();
return true;
}
fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
_cleanup_close_ int fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
if (fd < 0) {
log_debug_errno(errno, "Failed to open the wall pipe: %m");
return 1;
@ -162,6 +155,7 @@ static int agent_ask_password_tty(
r = ask_password_tty(tty_fd, &req, until, flags, flag_file, ret);
if (arg_console) {
assert(tty_fd >= 0);
tty_fd = safe_close(tty_fd);
release_terminal();
@ -172,7 +166,7 @@ static int agent_ask_password_tty(
return r;
}
static int process_one_password_file(const char *filename) {
static int process_one_password_file(const char *filename, FILE *f) {
_cleanup_free_ char *socket_name = NULL, *message = NULL;
bool accept_cached = false, echo = false, silent = false;
uint64_t not_after = 0;
@ -192,13 +186,17 @@ static int process_one_password_file(const char *filename) {
int r;
assert(filename);
assert(f);
r = config_parse(NULL, filename, NULL,
NULL,
config_item_table_lookup, items,
r = config_parse(/* unit= */ NULL,
filename,
f,
/* sections= */ "Ask\0",
config_item_table_lookup,
items,
CONFIG_PARSE_RELAXED|CONFIG_PARSE_WARN,
NULL,
NULL);
/* userdata= */ NULL,
/* ret_stat= */ NULL);
if (r < 0)
return r;
@ -297,41 +295,44 @@ static int wall_tty_block(void) {
return fd;
}
static int process_password_files(void) {
static int process_password_files(const char *path) {
_cleanup_closedir_ DIR *d = NULL;
int r = 0;
int ret = 0, r;
d = opendir("/run/systemd/ask-password");
assert(path);
d = opendir(path);
if (!d) {
if (errno == ENOENT)
return 0;
return log_error_errno(errno, "Failed to open /run/systemd/ask-password: %m");
return log_error_errno(errno, "Failed to open '%s': %m", path);
}
FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read directory: %m")) {
FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read directory '%s': %m", path)) {
_cleanup_free_ char *p = NULL;
int q;
/* We only support /run on tmpfs, hence we can rely on
* d_type to be reliable */
if (de->d_type != DT_REG)
if (!IN_SET(de->d_type, DT_REG, DT_UNKNOWN))
continue;
if (!startswith(de->d_name, "ask."))
continue;
p = path_join("/run/systemd/ask-password", de->d_name);
p = path_join(path, de->d_name);
if (!p)
return log_oom();
q = process_one_password_file(p);
if (q < 0 && r == 0)
r = q;
_cleanup_fclose_ FILE *f = NULL;
r = xfopenat(dirfd(d), de->d_name, "re", O_NOFOLLOW, &f);
if (r < 0) {
log_warning_errno(r, "Failed to open '%s', ignoring: %m", p);
continue;
}
return r;
RET_GATHER(ret, process_one_password_file(p, f));
}
return ret;
}
static int process_and_watch_password_files(bool watch) {
@ -341,6 +342,7 @@ static int process_and_watch_password_files(bool watch) {
_FD_MAX
};
_cleanup_free_ char *user_ask_password_directory = NULL;
_unused_ _cleanup_close_ int tty_block_fd = -EBADF;
_cleanup_close_ int notify = -EBADF, signal_fd = -EBADF;
struct pollfd pollfd[_FD_MAX];
@ -351,6 +353,12 @@ static int process_and_watch_password_files(bool watch) {
(void) mkdir_p_label("/run/systemd/ask-password", 0755);
r = acquire_user_ask_password_directory(&user_ask_password_directory);
if (r < 0)
return log_error_errno(r, "Failed to determine per-user password directory: %m");
if (r > 0)
(void) mkdir_p_label(user_ask_password_directory, 0755);
assert_se(sigemptyset(&mask) >= 0);
assert_se(sigset_add_many(&mask, SIGTERM) >= 0);
assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) >= 0);
@ -366,29 +374,34 @@ static int process_and_watch_password_files(bool watch) {
if (notify < 0)
return log_error_errno(errno, "Failed to allocate directory watch: %m");
r = inotify_add_watch_and_warn(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO);
r = inotify_add_watch_and_warn(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO|IN_ONLYDIR);
if (r < 0)
return r;
if (user_ask_password_directory) {
r = inotify_add_watch_and_warn(notify, user_ask_password_directory, IN_CLOSE_WRITE|IN_MOVED_TO|IN_ONLYDIR);
if (r < 0)
return r;
}
pollfd[FD_INOTIFY] = (struct pollfd) { .fd = notify, .events = POLLIN };
}
for (;;) {
usec_t timeout = USEC_INFINITY;
r = process_password_files();
if (r < 0) {
r = process_password_files("/run/systemd/ask-password");
if (user_ask_password_directory)
RET_GATHER(r, process_password_files(user_ask_password_directory));
if (r == -ECANCELED)
/* Disable poll() timeout since at least one password has
* been skipped and therefore one file remains and is
* unlikely to trigger any events. */
/* Disable poll() timeout since at least one password has been skipped and therefore
* one file remains and is unlikely to trigger any events. */
timeout = 0;
else
else if (r < 0)
/* FIXME: we should do something here since otherwise the service
* requesting the password won't notice the error and will wait
* indefinitely. */
log_error_errno(r, "Failed to process password: %m");
}
log_warning_errno(r, "Failed to process password, ignoring: %m");
if (!watch)
break;

View File

@ -11,6 +11,7 @@ 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();
@ -18,7 +19,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);
r = udev_rule_parse_value(str, &value, &endpos, &is_case_sensitive);
if (r < 0) {
/* not modified on failure */
assert_se(value == UINT_TO_PTR(0x12345678U));

View File

@ -4,15 +4,16 @@
#include "tests.h"
#include "udev-rules.h"
static void test_udev_rule_parse_value_one(const char *in, const char *expected_value, int expected_retval) {
static void test_udev_rule_parse_value_one(const char *in, const char *expected_value, bool expected_case_insensitive, 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) == expected_retval);
assert_se(udev_rule_parse_value(str, &value, &endpos, &i) == expected_retval);
if (expected_retval < 0) {
/* not modified on failure */
assert_se(value == UINT_TO_PTR(0x12345678U));
@ -25,6 +26,7 @@ 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);
}
}
@ -33,45 +35,61 @@ 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", 0);
test_udev_rule_parse_value_one("\"valid operand\"", "valid operand", /* case_insensitive = */ false, 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", 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);
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);
/* input: e"" */
test_udev_rule_parse_value_one("e\"\"", "", 0);
test_udev_rule_parse_value_one("e\"\"", "", /* case_insensitive = */ false, 0);
/* input: e"1234" */
test_udev_rule_parse_value_one("e\"1234\"", "1234", 0);
test_udev_rule_parse_value_one("e\"1234\"", "1234", /* case_insensitive = */ false, 0);
/* input: e"\"" */
test_udev_rule_parse_value_one("e\"\\\"\"", "\"", 0);
test_udev_rule_parse_value_one("e\"\\\"\"", "\"", /* case_insensitive = */ false, 0);
/* input: e"\ */
test_udev_rule_parse_value_one("e\"\\", NULL, -EINVAL);
test_udev_rule_parse_value_one("e\"\\", NULL, /* case_insensitive = */ false, -EINVAL);
/* input: e"\" */
test_udev_rule_parse_value_one("e\"\\\"", NULL, -EINVAL);
test_udev_rule_parse_value_one("e\"\\\"", NULL, /* case_insensitive = */ false, -EINVAL);
/* input: e"\\" */
test_udev_rule_parse_value_one("e\"\\\\\"", "\\", 0);
test_udev_rule_parse_value_one("e\"\\\\\"", "\\", /* case_insensitive = */ false, 0);
/* input: e"\\\" */
test_udev_rule_parse_value_one("e\"\\\\\\\"", NULL, -EINVAL);
test_udev_rule_parse_value_one("e\"\\\\\\\"", NULL, /* case_insensitive = */ false, -EINVAL);
/* input: e"\\\"" */
test_udev_rule_parse_value_one("e\"\\\\\\\"\"", "\\\"", 0);
test_udev_rule_parse_value_one("e\"\\\\\\\"\"", "\\\"", /* case_insensitive = */ false, 0);
/* input: e"\\\\" */
test_udev_rule_parse_value_one("e\"\\\\\\\\\"", "\\\\", 0);
test_udev_rule_parse_value_one("e\"\\\\\\\\\"", "\\\\", /* case_insensitive = */ false, 0);
/* input: e"operand with newline\n" */
test_udev_rule_parse_value_one("e\"operand with newline\\n\"", "operand with newline\n", 0);
test_udev_rule_parse_value_one("e\"operand with newline\\n\"", "operand with newline\n", /* case_insensitive = */ false, 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", 0);
"e\"single\\rcharacter\\t\\aescape\\bsequence\"", "single\rcharacter\t\aescape\bsequence", /* case_insensitive = */ false, 0);
/* input: e"reject\invalid escape sequence" */
test_udev_rule_parse_value_one("e\"reject\\invalid escape sequence", NULL, -EINVAL);
test_udev_rule_parse_value_one("e\"reject\\invalid escape sequence", NULL, /* case_insensitive = */ false, -EINVAL);
/* input: e"\ */
test_udev_rule_parse_value_one("e\"\\", NULL, -EINVAL);
test_udev_rule_parse_value_one("e\"\\", NULL, /* case_insensitive = */ false, -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",
0);
/* 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);
}
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -52,7 +52,8 @@ static void test_event_spawn_self(const char *self, const char *arg, bool with_p
log_debug("/* %s(%s, %s) */", __func__, arg, yes_no(with_pidfd));
assert_se(cmd = strjoin(self, " ", arg));
/* 'self' may contain spaces, hence needs to be quoted. */
assert_se(cmd = strjoin("'", self, "' ", arg));
test_event_spawn_core(with_pidfd, cmd, result_buf, BUF_SIZE);

View File

@ -63,9 +63,15 @@ 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,
} UdevRuleMatchType;
assert_cc(_MATCH_TYPE_MAX <= _MATCH_TYPE_MASK);
typedef enum {
SUBST_TYPE_PLAIN, /* no substitution */
SUBST_TYPE_FORMAT, /* % or $ */
@ -155,8 +161,7 @@ struct UdevRuleToken {
UdevRuleTokenType type:8;
UdevRuleOperatorType op:8;
UdevRuleMatchType match_type:8;
UdevRuleSubstituteType attr_subst_type:7;
bool attr_match_remove_trailing_whitespace:1;
UdevRuleSubstituteType attr_subst_type:8;
const char *value;
void *data;
@ -295,6 +300,7 @@ 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), \
@ -488,12 +494,10 @@ 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) {
static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type, UdevRuleOperatorType op, char *value, void *data, bool is_case_insensitive) {
_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);
@ -552,16 +556,21 @@ 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]))
remove_trailing_whitespace = true;
match_type |= MATCH_REMOVE_TRAILING_WHITESPACE;
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;
@ -573,7 +582,6 @@ 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,
};
@ -621,7 +629,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) {
static int parse_token(UdevRuleLine *rule_line, const char *key, char *attr, UdevRuleOperatorType op, char *value, bool is_case_insensitive) {
ResolveNameTiming resolve_name_timing = LINE_GET_RULES(rule_line)->resolve_name_timing;
bool is_match = IN_SET(op, OP_MATCH, OP_NOMATCH);
int r;
@ -629,35 +637,39 @@ 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);
r = rule_line_add_token(rule_line, TK_M_ACTION, op, value, NULL, is_case_insensitive);
} 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);
r = rule_line_add_token(rule_line, TK_M_DEVPATH, op, value, NULL, is_case_insensitive);
} 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);
r = rule_line_add_token(rule_line, TK_M_KERNEL, op, value, NULL, is_case_insensitive);
} 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);
r = rule_line_add_token(rule_line, TK_A_DEVLINK, op, value, NULL, /* is_case_insensitive = */ false);
} else
r = rule_line_add_token(rule_line, TK_M_DEVLINK, op, value, NULL);
r = rule_line_add_token(rule_line, TK_M_DEVLINK, op, value, NULL, is_case_insensitive);
} else if (streq(key, "NAME")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
@ -677,9 +689,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);
r = rule_line_add_token(rule_line, TK_A_NAME, op, value, NULL, /* is_case_insensitive = */ false);
} else
r = rule_line_add_token(rule_line, TK_M_NAME, op, value, NULL);
r = rule_line_add_token(rule_line, TK_M_NAME, op, value, NULL, is_case_insensitive);
} else if (streq(key, "ENV")) {
if (isempty(attr))
return log_line_invalid_attr(rule_line, key);
@ -697,15 +709,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);
r = rule_line_add_token(rule_line, TK_A_ENV, op, value, attr, /* is_case_insensitive = */ false);
} else
r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr);
r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr, is_case_insensitive);
} 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);
r = rule_line_add_token(rule_line, TK_M_CONST, op, value, attr, is_case_insensitive);
} else if (streq(key, "TAG")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
@ -717,9 +729,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);
r = rule_line_add_token(rule_line, TK_A_TAG, op, value, NULL, /* is_case_insensitive = */ false);
} else
r = rule_line_add_token(rule_line, TK_M_TAG, op, value, NULL);
r = rule_line_add_token(rule_line, TK_M_TAG, op, value, NULL, is_case_insensitive);
} else if (streq(key, "SUBSYSTEM")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
@ -729,14 +741,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);
r = rule_line_add_token(rule_line, TK_M_SUBSYSTEM, op, value, NULL, is_case_insensitive);
} 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);
r = rule_line_add_token(rule_line, TK_M_DRIVER, op, value, NULL, is_case_insensitive);
} else if (streq(key, "ATTR")) {
r = check_attr_format_and_warn(rule_line, key, attr);
if (r < 0)
@ -750,9 +762,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);
r = rule_line_add_token(rule_line, TK_A_ATTR, op, value, attr, /* is_case_insensitive = */ false);
} else
r = rule_line_add_token(rule_line, TK_M_ATTR, op, value, attr);
r = rule_line_add_token(rule_line, TK_M_ATTR, op, value, attr, is_case_insensitive);
} else if (streq(key, "SYSCTL")) {
r = check_attr_format_and_warn(rule_line, key, attr);
if (r < 0)
@ -766,30 +778,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);
r = rule_line_add_token(rule_line, TK_A_SYSCTL, op, value, attr, /* is_case_insensitive = */ false);
} else
r = rule_line_add_token(rule_line, TK_M_SYSCTL, op, value, attr);
r = rule_line_add_token(rule_line, TK_M_SYSCTL, op, value, attr, is_case_insensitive);
} 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);
r = rule_line_add_token(rule_line, TK_M_PARENTS_KERNEL, op, value, NULL, is_case_insensitive);
} 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);
r = rule_line_add_token(rule_line, TK_M_PARENTS_SUBSYSTEM, op, value, NULL, is_case_insensitive);
} 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);
r = rule_line_add_token(rule_line, TK_M_PARENTS_DRIVER, op, value, NULL, is_case_insensitive);
} else if (streq(key, "ATTRS")) {
r = check_attr_format_and_warn(rule_line, key, attr);
if (r < 0)
@ -802,14 +814,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);
r = rule_line_add_token(rule_line, TK_M_PARENTS_ATTR, op, value, attr, is_case_insensitive);
} 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);
r = rule_line_add_token(rule_line, TK_M_PARENTS_TAG, op, value, NULL, is_case_insensitive);
} else if (streq(key, "TEST")) {
mode_t mode = MODE_INVALID;
@ -821,8 +833,10 @@ 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));
r = rule_line_add_token(rule_line, TK_M_TEST, op, value, MODE_TO_PTR(mode), is_case_insensitive);
} else if (streq(key, "PROGRAM")) {
if (attr)
return log_line_invalid_attr(rule_line, key);
@ -831,8 +845,10 @@ 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);
r = rule_line_add_token(rule_line, TK_M_PROGRAM, op, value, NULL, /* is_case_insensitive */ false);
} else if (streq(key, "IMPORT")) {
if (isempty(attr))
return log_line_invalid_attr(rule_line, key);
@ -841,18 +857,20 @@ 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);
r = rule_line_add_token(rule_line, TK_M_IMPORT_FILE, op, value, NULL, /* is_case_insensitive = */ false);
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));
r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false);
} else
r = rule_line_add_token(rule_line, TK_M_IMPORT_PROGRAM, op, value, NULL);
r = rule_line_add_token(rule_line, TK_M_IMPORT_PROGRAM, op, value, NULL, /* is_case_insensitive = */ false);
} else if (streq(attr, "builtin")) {
UdevBuiltinCommand cmd;
@ -860,13 +878,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));
r = rule_line_add_token(rule_line, TK_M_IMPORT_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false);
} else if (streq(attr, "db"))
r = rule_line_add_token(rule_line, TK_M_IMPORT_DB, op, value, NULL);
r = rule_line_add_token(rule_line, TK_M_IMPORT_DB, op, value, NULL, /* is_case_insensitive = */ false);
else if (streq(attr, "cmdline"))
r = rule_line_add_token(rule_line, TK_M_IMPORT_CMDLINE, op, value, NULL);
r = rule_line_add_token(rule_line, TK_M_IMPORT_CMDLINE, op, value, NULL, /* is_case_insensitive = */ false);
else if (streq(attr, "parent"))
r = rule_line_add_token(rule_line, TK_M_IMPORT_PARENT, op, value, NULL);
r = rule_line_add_token(rule_line, TK_M_IMPORT_PARENT, op, value, NULL, /* is_case_insensitive = */ false);
else
return log_line_invalid_attr(rule_line, key);
} else if (streq(key, "RESULT")) {
@ -875,7 +893,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);
r = rule_line_add_token(rule_line, TK_M_RESULT, op, value, NULL, is_case_insensitive);
} else if (streq(key, "OPTIONS")) {
char *tmp;
@ -887,24 +905,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);
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_NONE, op, NULL, NULL, /* is_case_insensitive = */ false);
else if (streq(value, "string_escape=replace"))
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_REPLACE, op, NULL, NULL);
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STRING_ESCAPE_REPLACE, op, NULL, NULL, /* is_case_insensitive = */ false);
else if (streq(value, "db_persist"))
r = rule_line_add_token(rule_line, TK_A_OPTIONS_DB_PERSIST, op, NULL, NULL);
r = rule_line_add_token(rule_line, TK_A_OPTIONS_DB_PERSIST, op, NULL, NULL, /* is_case_insensitive = */ false);
else if (streq(value, "watch"))
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(1));
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(1), /* is_case_insensitive = */ false);
else if (streq(value, "nowatch"))
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(0));
r = rule_line_add_token(rule_line, TK_A_OPTIONS_INOTIFY_WATCH, op, NULL, INT_TO_PTR(0), /* is_case_insensitive = */ false);
else if ((tmp = startswith(value, "static_node=")))
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STATIC_NODE, op, tmp, NULL);
r = rule_line_add_token(rule_line, TK_A_OPTIONS_STATIC_NODE, op, tmp, NULL, /* is_case_insensitive = */ false);
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));
r = rule_line_add_token(rule_line, TK_A_OPTIONS_DEVLINK_PRIORITY, op, NULL, INT_TO_PTR(prio), /* is_case_insensitive = */ false);
} else if ((tmp = startswith(value, "log_level="))) {
int level;
@ -915,7 +933,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));
r = rule_line_add_token(rule_line, TK_A_OPTIONS_LOG_LEVEL, op, NULL, INT_TO_PTR(level), /* is_case_insensitive = */ false);
} else {
log_line_warning(rule_line, "Invalid value for OPTIONS key, ignoring: '%s'", value);
return 0;
@ -933,17 +951,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));
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid), /* is_case_insensitive = */ false);
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));
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid), /* is_case_insensitive = */ false);
} 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);
r = rule_line_add_token(rule_line, TK_A_OWNER, op, value, NULL, /* is_case_insensitive = */ false);
} else {
log_line_debug(rule_line, "User name resolution is disabled, ignoring %s=\"%s\".", key, value);
return 0;
@ -961,17 +979,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));
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid), /* is_case_insensitive = */ false);
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));
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid), /* is_case_insensitive = */ false);
} 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);
r = rule_line_add_token(rule_line, TK_A_GROUP, op, value, NULL, /* is_case_insensitive = */ false);
} else {
log_line_debug(rule_line, "Resolving group name is disabled, ignoring GROUP=\"%s\".", value);
return 0;
@ -989,10 +1007,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));
r = rule_line_add_token(rule_line, TK_A_MODE_ID, op, NULL, MODE_TO_PTR(mode), /* is_case_insensitive = */ false);
else {
check_value_format_and_warn(rule_line, key, value, true);
r = rule_line_add_token(rule_line, TK_A_MODE, op, value, NULL);
r = rule_line_add_token(rule_line, TK_A_MODE, op, value, NULL, /* is_case_insensitive = */ false);
}
} else if (streq(key, "SECLABEL")) {
if (isempty(attr))
@ -1005,13 +1023,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);
r = rule_line_add_token(rule_line, TK_A_SECLABEL, op, value, attr, /* is_case_insensitive = */ false);
} 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);
r = rule_line_add_token(rule_line, TK_A_RUN_PROGRAM, op, value, NULL, /* is_case_insensitive = */ false);
else if (streq(attr, "builtin")) {
UdevBuiltinCommand cmd;
@ -1019,7 +1037,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));
r = rule_line_add_token(rule_line, TK_A_RUN_BUILTIN, op, value, UDEV_BUILTIN_CMD_TO_PTR(cmd), /* is_case_insensitive = */ false);
} else
return log_line_invalid_attr(rule_line, key);
} else if (streq(key, "GOTO")) {
@ -1119,13 +1137,30 @@ static void check_token_delimiters(UdevRuleLine *rule_line, const char *line) {
}
}
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) {
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos, bool *ret_is_case_insensitive) {
char *i, *j;
bool is_escaped;
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;
/* value must be double quotated */
is_escaped = str[0] == 'e';
str += is_escaped;
str += is_escaped + is_case_insensitive;
if (str[0] != '"')
return -EINVAL;
@ -1172,10 +1207,11 @@ int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) {
*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) {
static int parse_line(char **line, char **ret_key, char **ret_attr, UdevRuleOperatorType *ret_op, char **ret_value, bool *ret_is_case_insensitive) {
char *key_begin, *key_end, *attr, *tmp;
UdevRuleOperatorType op;
int r;
@ -1185,6 +1221,7 @@ 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 ",");
@ -1219,7 +1256,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);
r = udev_rule_parse_value(tmp, ret_value, line, ret_is_case_insensitive);
if (r < 0)
return r;
@ -1291,17 +1328,18 @@ 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);
r = parse_line(&p, &key, &attr, &op, &value, &is_case_insensitive);
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);
r = parse_token(rule_line, key, attr, op, value, is_case_insensitive);
if (r < 0)
return r;
}
@ -1412,7 +1450,6 @@ 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);
}
@ -1427,7 +1464,6 @@ 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;
@ -1696,7 +1732,7 @@ bool udev_rules_should_reload(UdevRules *rules) {
static bool token_match_string(UdevRuleToken *token, const char *str) {
const char *value;
bool match = false;
bool match = false, case_insensitive;
assert(token);
assert(token->value);
@ -1704,12 +1740,16 @@ 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) {
switch (token->match_type & _MATCH_TYPE_MASK) {
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");
break;
case MATCH_TYPE_PLAIN_WITH_EMPTY:
@ -1720,7 +1760,7 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
_fallthrough_;
case MATCH_TYPE_PLAIN:
NULSTR_FOREACH(i, value)
if (streq(i, str)) {
if (case_insensitive ? strcaseeq(i, str) : streq(i, str)) {
match = true;
break;
}
@ -1733,7 +1773,7 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
_fallthrough_;
case MATCH_TYPE_GLOB:
NULSTR_FOREACH(i, value)
if ((fnmatch(i, str, 0) == 0)) {
if ((fnmatch(i, str, case_insensitive ? FNM_CASEFOLD : 0) == 0)) {
match = true;
break;
}
@ -1773,7 +1813,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 (token->attr_match_remove_trailing_whitespace) {
if (FLAGS_SET(token->match_type, MATCH_REMOVE_TRAILING_WHITESPACE)) {
strscpy(vbuf, sizeof(vbuf), value);
value = delete_trailing_chars(vbuf, NULL);
}
@ -1785,7 +1825,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 (token->attr_match_remove_trailing_whitespace)
if (FLAGS_SET(token->match_type, 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);
int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos, bool *ret_is_case_insensitive);
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

@ -526,9 +526,6 @@ def call_systemd_measure(uki, linux, opts):
# First, pick up the sections we shall measure now */
for s in uki.sections:
print(s)
if not s.measure:
continue

View File

@ -0,0 +1,6 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
all setup run clean clean-again:
true
.PHONY: all setup run clean clean-again

View File

@ -0,0 +1,10 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
integration_tests += [
integration_test_template + {
'name' : fs.name(meson.current_source_dir()),
'storage' : 'persistent',
'vm' : true,
'firmware' : 'auto',
},
]

View File

@ -376,6 +376,7 @@ foreach dirname : [
'TEST-83-BTRFS',
'TEST-84-STORAGETM',
'TEST-85-NETWORK',
'TEST-86-MULTI-PROFILE-UKI',
]
subdir(dirname)
endforeach

View File

@ -4,32 +4,32 @@ set -e
ANALYZE="${1:-systemd-analyze}"
$ANALYZE compare-versions 1 lt 2
$ANALYZE compare-versions 1 '<' 2
$ANALYZE compare-versions 1 le 2
$ANALYZE compare-versions 1 '<=' 2
$ANALYZE compare-versions 1 ne 2
$ANALYZE compare-versions 1 '!=' 2
( ! $ANALYZE compare-versions 1 ge 2 )
( ! $ANALYZE compare-versions 1 '>=' 2 )
( ! $ANALYZE compare-versions 1 eq 2 )
( ! $ANALYZE compare-versions 1 '==' 2 )
( ! $ANALYZE compare-versions 1 gt 2 )
( ! $ANALYZE compare-versions 1 '>' 2 )
"$ANALYZE" compare-versions 1 lt 2
"$ANALYZE" compare-versions 1 '<' 2
"$ANALYZE" compare-versions 1 le 2
"$ANALYZE" compare-versions 1 '<=' 2
"$ANALYZE" compare-versions 1 ne 2
"$ANALYZE" compare-versions 1 '!=' 2
( ! "$ANALYZE" compare-versions 1 ge 2 )
( ! "$ANALYZE" compare-versions 1 '>=' 2 )
( ! "$ANALYZE" compare-versions 1 eq 2 )
( ! "$ANALYZE" compare-versions 1 '==' 2 )
( ! "$ANALYZE" compare-versions 1 gt 2 )
( ! "$ANALYZE" compare-versions 1 '>' 2 )
test "$($ANALYZE compare-versions 1 2)" = '1 < 2'
test "$($ANALYZE compare-versions 2 2)" = '2 == 2'
test "$($ANALYZE compare-versions 2 1)" = '2 > 1'
test "$($ANALYZE compare-versions '' '')" = "'' == ''"
test "$("$ANALYZE" compare-versions 1 2)" = '1 < 2'
test "$("$ANALYZE" compare-versions 2 2)" = '2 == 2'
test "$("$ANALYZE" compare-versions 2 1)" = '2 > 1'
test "$("$ANALYZE" compare-versions '' '')" = "'' == ''"
set +e
$ANALYZE compare-versions 1 2; ret1=$?
$ANALYZE compare-versions 2 2; ret2=$?
$ANALYZE compare-versions 2 1; ret3=$?
"$ANALYZE" compare-versions 1 2; ret1=$?
"$ANALYZE" compare-versions 2 2; ret2=$?
"$ANALYZE" compare-versions 2 1; ret3=$?
set -e
test $ret1 == 12
test $ret2 == 0
test $ret3 == 11
test "$ret1" == 12
test "$ret2" == 0
test "$ret3" == 11

View File

@ -44,9 +44,9 @@ test_one() (
fi
if [[ "${input##*/}" =~ \.fstab\.input ]]; then
SYSTEMD_LOG_LEVEL=debug SYSTEMD_IN_INITRD="$initrd" SYSTEMD_SYSFS_CHECK=no SYSTEMD_PROC_CMDLINE="fstab=yes root=fstab" SYSTEMD_FSTAB="$input" SYSTEMD_SYSROOT_FSTAB="/dev/null" $generator "$out" "$out" "$out"
SYSTEMD_LOG_LEVEL=debug SYSTEMD_IN_INITRD="$initrd" SYSTEMD_SYSFS_CHECK=no SYSTEMD_PROC_CMDLINE="fstab=yes root=fstab" SYSTEMD_FSTAB="$input" SYSTEMD_SYSROOT_FSTAB="/dev/null" "$generator" "$out" "$out" "$out"
else
SYSTEMD_LOG_LEVEL=debug SYSTEMD_IN_INITRD="$initrd" SYSTEMD_SYSFS_CHECK=no SYSTEMD_PROC_CMDLINE="fstab=no $(cat "$input")" $generator "$out" "$out" "$out"
SYSTEMD_LOG_LEVEL=debug SYSTEMD_IN_INITRD="$initrd" SYSTEMD_SYSFS_CHECK=no SYSTEMD_PROC_CMDLINE="fstab=no $(cat "$input")" "$generator" "$out" "$out" "$out"
fi
# The option x-systemd.growfs creates symlink to system's systemd-growfs@.service in .mount.wants directory.

View File

@ -53,7 +53,7 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do
echo "*** Running $f"
prepare_testdir "${f%.input}"
cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf"
$SYSUSERS --root="$TESTDIR"
"$SYSUSERS" --root="$TESTDIR"
compare "${f%.*}" ""
done
@ -62,7 +62,7 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do
echo "*** Running $f on stdin"
prepare_testdir "${f%.input}"
touch "$TESTDIR/etc/sysusers.d/test.conf"
$SYSUSERS --root="$TESTDIR" - <"$f"
"$SYSUSERS" --root="$TESTDIR" - <"$f"
compare "${f%.*}" "on stdin"
done
@ -72,9 +72,9 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do
prepare_testdir "${f%.input}"
touch "$TESTDIR/etc/sysusers.d/test.conf"
# this overrides test.conf which is masked on disk
$SYSUSERS --root="$TESTDIR" --replace=/etc/sysusers.d/test.conf - <"$f"
"$SYSUSERS" --root="$TESTDIR" --replace=/etc/sysusers.d/test.conf - <"$f"
# this should be ignored
$SYSUSERS --root="$TESTDIR" --replace=/usr/lib/sysusers.d/test.conf - <"$SOURCE/test-1.input"
"$SYSUSERS" --root="$TESTDIR" --replace=/usr/lib/sysusers.d/test.conf - <"$SOURCE/test-1.input"
compare "${f%.*}" "on stdin with --replace"
done
@ -84,7 +84,7 @@ echo "*** Testing --inline"
prepare_testdir "$SOURCE/inline"
# copy a random file to make sure it is ignored
cp "$f" "$TESTDIR/etc/sysusers.d/confuse.conf"
$SYSUSERS --root="$TESTDIR" --inline \
"$SYSUSERS" --root="$TESTDIR" --inline \
"u u1 222 - - /bin/zsh" \
"g g1 111"
@ -95,7 +95,7 @@ echo "*** Testing --inline with --replace"
prepare_testdir "$SOURCE/inline"
# copy a random file to make sure it is ignored
cp "$f" "$TESTDIR/etc/sysusers.d/confuse.conf"
$SYSUSERS --root="$TESTDIR" \
"$SYSUSERS" --root="$TESTDIR" \
--inline \
--replace=/etc/sysusers.d/confuse.conf \
"u u1 222 - - /bin/zsh" \
@ -105,7 +105,7 @@ compare "$SOURCE/inline" "(--inline --replace=…)"
echo "*** Testing --inline with no /etc"
rm -rf "${TESTDIR:?}/etc"
$SYSUSERS --root="$TESTDIR" --inline \
"$SYSUSERS" --root="$TESTDIR" --inline \
"u u1 222 - - /bin/zsh" \
"g g1 111"
@ -136,7 +136,7 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do
echo "*** Running $f (with login.defs)"
prepare_testdir "${f%.input}"
cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf"
$SYSUSERS --root="$TESTDIR"
"$SYSUSERS" --root="$TESTDIR"
# shellcheck disable=SC2050
[ @ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES@ = 1 ] && bound=555 || bound=$system_guid_max
@ -152,7 +152,7 @@ for f in $(find "$SOURCE"/test-*.input | sort -V); do
echo "*** Running $f (with login.defs symlinked)"
prepare_testdir "${f%.input}"
cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf"
$SYSUSERS --root="$TESTDIR"
"$SYSUSERS" --root="$TESTDIR"
# shellcheck disable=SC2050
[ @ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES@ = 1 ] && bound=555 || bound=$system_guid_max
@ -166,7 +166,7 @@ for f in $(find "$SOURCE"/unhappy-*.input | sort -V); do
echo "*** Running test $f"
prepare_testdir "${f%.input}"
cp "$f" "$TESTDIR/usr/lib/sysusers.d/test.conf"
SYSTEMD_LOG_LEVEL=info $SYSUSERS --root="$TESTDIR" 2>&1 | tail -n1 | sed -r 's/^[^:]+:[^:]+://' >"$TESTDIR/err"
SYSTEMD_LOG_LEVEL=info "$SYSUSERS" --root="$TESTDIR" 2>&1 | tail -n1 | sed -r 's/^[^:]+:[^:]+://' >"$TESTDIR/err"
if ! diff -u "$TESTDIR/err" "${f%.*}.expected-err"; then
echo >&2 "**** Unexpected error output for $f"
cat >&2 "$TESTDIR/err"

View File

@ -2313,6 +2313,17 @@ 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,6 +237,8 @@ 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.'
@ -275,10 +277,12 @@ 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,26 +9,39 @@ set -o pipefail
export SYSTEMD_LOG_LEVEL=debug
c='
d /tmp/somedir
f /tmp/somedir/somefile - - - - baz
d$ /tmp/somedir
f$ /tmp/somedir/somefile - - - - baz
f /tmp/someotherfile - - - - qux
'
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

@ -9,6 +9,14 @@ 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

View File

@ -9,6 +9,14 @@ 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

View File

@ -29,7 +29,6 @@ EOF
systemctl daemon-reexec
export SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
udevadm control --log-level debug
@ -42,9 +41,9 @@ 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 ARGS
export STATE_DIRECTORY
export SYSTEMD_LOG_LEVEL=debug
export SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC=30
# Quick smoke tests

View File

@ -29,6 +29,9 @@ if ! systemd-detect-virt --quiet --container; then
udevadm control --log-level debug
fi
esp_guid=C12A7328-F81F-11D2-BA4B-00A0C93EC93B
xbootldr_guid=BC13C2FF-59E6-4262-A352-B275FD6F7172
machine="$(uname -m)"
if [ "${machine}" = "x86_64" ]; then
root_guid=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
@ -1325,9 +1328,12 @@ testcase_compression() {
# TODO: add btrfs once btrfs-progs v6.11 is available in distributions.
for format in squashfs erofs; do
if ! command -v "mkfs.$format" && ! command -v mksquashfs >/dev/null; then
continue
fi
case "$format" in
squashfs)
command -v mksquashfs >/dev/null || continue ;;
*)
command -v "mkfs.$format" || continue ;;
esac
[[ "$format" == "squashfs" ]] && compression=zstd
[[ "$format" == "erofs" ]] && compression=lz4hc
@ -1429,6 +1435,82 @@ EOF
systemd-dissect -U "$imgs/mnt"
}
testcase_fallback_partitions() {
local workdir image defs
workdir="$(mktemp --directory "/tmp/test-repart.fallback.XXXXXXXXXX")"
# shellcheck disable=SC2064
trap "rm -rf '${workdir:?}'" RETURN
image="$workdir/image.img"
defs="$workdir/defs"
mkdir "$defs"
tee "$defs/10-esp.conf" <<EOF
[Partition]
Type=esp
Format=vfat
SizeMinBytes=10M
EOF
tee "$defs/20-xbootldr.conf" <<EOF
[Partition]
Type=xbootldr
Format=vfat
SizeMinBytes=100M
SupplementFor=10-esp
EOF
# Blank disk => big ESP should be created
systemd-repart --empty=create --size=auto --dry-run=no --definitions="$defs" "$image"
output=$(sfdisk -d "$image")
assert_in "${image}1 : start= 2048, size= 204800, type=${esp_guid}" "$output"
assert_not_in "${image}2" "$output"
# Disk with small ESP => ESP grows
sfdisk "$image" <<EOF
label: gpt
size=10M, type=${esp_guid}
EOF
systemd-repart --dry-run=no --definitions="$defs" "$image"
output=$(sfdisk -d "$image")
assert_in "${image}1 : start= 2048, size= 204800, type=${esp_guid}" "$output"
assert_not_in "${image}2" "$output"
# Disk with small ESP that can't grow => XBOOTLDR created
truncate -s 150M "$image"
sfdisk "$image" <<EOF
label: gpt
size=10M, type=${esp_guid},
size=10M, type=${root_guid},
EOF
systemd-repart --dry-run=no --definitions="$defs" "$image"
output=$(sfdisk -d "$image")
assert_in "${image}1 : start= 2048, size= 20480, type=${esp_guid}" "$output"
assert_in "${image}3 : start= 43008, size= 264152, type=${xbootldr_guid}" "$output"
# Disk with existing XBOOTLDR partition => XBOOTLDR grows, small ESP created
sfdisk "$image" <<EOF
label: gpt
size=10M, type=${xbootldr_guid},
EOF
systemd-repart --dry-run=no --definitions="$defs" "$image"
output=$(sfdisk -d "$image")
assert_in "${image}1 : start= 2048, size= 204800, type=${xbootldr_guid}" "$output"
assert_in "${image}2 : start= 206848, size= 100312, type=${esp_guid}" "$output"
}
OFFLINE="yes"
run_testcases

View File

@ -0,0 +1,81 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
export SYSTEMD_LOG_LEVEL=debug
bootctl
CURRENT_UKI=$(bootctl --print-stub-path)
echo "CURRENT UKI ($CURRENT_UKI):"
ukify inspect "$CURRENT_UKI"
if test -f /run/systemd/stub/profile; then
echo "CURRENT PROFILE:"
cat /run/systemd/stub/profile
fi
echo "CURRENT MEASUREMENT:"
/usr/lib/systemd/systemd-measure --current
if test -f /run/systemd/tpm2-pcr-signature.json; then
echo "CURRENT SIGNATURE:"
jq </run/systemd/tpm2-pcr-signature.json
fi
echo "CURRENT EVENT LOG + PCRS:"
/usr/lib/systemd/systemd-pcrlock
if test ! -f /run/systemd/stub/profile; then
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out /root/pcrsign.private.pem
openssl rsa -pubout -in /root/pcrsign.private.pem -out /root/pcrsign.public.pem
ukify build --extend="$CURRENT_UKI" --output=/tmp/extended0.efi --profile='ID=profile0
TITLE="Profile Zero"' --measure-base="$CURRENT_UKI" --pcr-private-key=/root/pcrsign.private.pem --pcr-public-key=/root/pcrsign.public.pem --pcr-banks=sha256,sha384,sha512
ukify build --extend=/tmp/extended0.efi --output=/tmp/extended1.efi --profile='ID=profile1
TITLE="Profile One"' --measure-base=/tmp/extended0.efi --cmdline="testprofile1=1 $(cat /proc/cmdline)" --pcr-private-key=/root/pcrsign.private.pem --pcr-public-key=/root/pcrsign.public.pem --pcr-banks=sha256,sha384,sha512
ukify build --extend=/tmp/extended1.efi --output=/tmp/extended2.efi --profile='ID=profile2
TITLE="Profile Two"' --measure-base=/tmp/extended1.efi --cmdline="testprofile2=1 $(cat /proc/cmdline)" --pcr-private-key=/root/pcrsign.private.pem --pcr-public-key=/root/pcrsign.public.pem --pcr-banks=sha256,sha384,sha512
echo "EXTENDED UKI:"
ukify inspect /tmp/extended2.efi
rm /tmp/extended0.efi /tmp/extended1.efi
mv /tmp/extended2.efi "$CURRENT_UKI"
# Prepare a disk image, locked to the PCR measurements of the UKI we just generated
truncate -s 32M /root/encrypted.raw
echo -n "geheim" >/root/encrypted.secret
cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom /root/encrypted.raw --key-file=/root/encrypted.secret
systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs= --tpm2-public-key=/root/pcrsign.public.pem --unlock-key-file=/root/encrypted.secret /root/encrypted.raw
rm -f /root/encrypted.secret
reboot
exit 0
else
# shellcheck source=/dev/null
. /run/systemd/stub/profile
# Validate that with the current profile we can fulfill the PCR 11 policy
systemd-cryptsetup attach multiprof /root/encrypted.raw - tpm2-device=auto,headless=1
systemd-cryptsetup detach multiprof
if [ "$ID" = "profile0" ]; then
grep -v testprofile /proc/cmdline
echo "default $(basename "$CURRENT_UKI")@profile1" >"$(bootctl -p)/loader/loader.conf"
reboot
exit 0
elif [ "$ID" = "profile1" ]; then
grep testprofile1=1 /proc/cmdline
echo "default $(basename "$CURRENT_UKI")@profile2" >"$(bootctl -p)/loader/loader.conf"
reboot
exit 0
elif [ "$ID" = "profile2" ]; then
grep testprofile2=1 /proc/cmdline
rm /root/encrypted.raw
else
exit 1
fi
fi
touch /testok

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

@ -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 -