1
0
mirror of https://github.com/systemd/systemd synced 2026-03-13 00:24:48 +01:00

Compare commits

..

53 Commits

Author SHA1 Message Date
nikstur
14519d7dfa meson: guard symlinks in sysconfdir behind install_sysconfidr
Symlinks to files inside sysconfdir are now only installed if
ìnstall_sysconfdir=true (which is the default).

If sshconfdir,sshdconfdir,shellprofiledir are not inside sysconfdir and
install_sysconfidr=false, these symlinks are still installed to the
configured directory.
2026-02-09 16:52:53 +01:00
Philip Withnall
779ed358f2 test: Add basic tests for path_split_prefix_filename()
These aren’t anything comprehensive, but provide some basic assurances
that it’s working correctly. In particular, they test its behaviour when
*both* the prefix and filename components are requested.

Split out from the original version of this function which was part
of #40236.

Signed-off-by: Philip Withnall <pwithnall@gnome.org>
2026-02-09 16:26:40 +01:00
Luca Boccassi
e67b008fa3 journald: set a lower size limit for FDs from unpriv processes
Unprivileged processes can send 768M in a FD-based message to journald,
which will be malloc'ed in one go, likely causing memory issues.
Set the limit for unprivileged users to 24M.

Allow coredumps as an exception, since we always allowed storing
up to the 768M max core files in the journal.

Reported on yeswehack.com as #YWH-PGM9780-48
2026-02-09 13:51:59 +01:00
Lennart Poettering
775f04293d
kernel-install refactorings (#40610)
This contains the first two commits from #38764. While @daandemeyer
convinced me to base systemd-sysinstall on a new "bootctl link" rather
than "kernel-install", I think the refactorings I prepped as part of the
original work still make a lot of sense on their own, and I hope I
didn't do them for /dev/null.
2026-02-09 11:54:36 +01:00
Lennart Poettering
7ae2329198
tree-wide: symlink well-known Varlink service entry point sockets into /run/varlink/registry/ (#40590)
This is generally useful, but is particularly useful in context of
https://github.com/mvo5/varlink-proxy-rs which can expose a set of local
Varlink services via a HTTP bridge. The idea is that the sockets linked
into /run/varlink/registry/ are candidates for being exposed like that.

/cc @mvo5
2026-02-09 11:54:15 +01:00
Mike Yuan
b026cb94ae
path-util: unify path_extract_filename/directory into path_split_prefix_filename() (#40608) 2026-02-09 11:03:14 +01:00
Zbigniew Jędrzejewski-Szmek
df8747806b
Two cleanups (#40587) 2026-02-09 11:02:41 +01:00
Zbigniew Jędrzejewski-Szmek
8a70fa901e hwdb: quote invalid patterns in error messages
If the pattern is invalid, we don't quite know how it looks, so it's
safer to quote it.

Also simplify the call to sorted().
2026-02-09 11:01:31 +01:00
Zbigniew Jędrzejewski-Szmek
25860000b6 Fix wording in two places
Noticed this while going through the stable series…
Also update location after 97318131fd06a5bc35454da81dcbbc84f16d9940.
2026-02-09 11:01:15 +01:00
Lennart Poettering
71e651ba82 update TODO 2026-02-09 10:54:18 +01:00
Lennart Poettering
a1d3fd1202 test: add superficial test for list-registry 2026-02-09 10:54:18 +01:00
Lennart Poettering
e2f23b6deb man: document new varlinkctl feature 2026-02-09 10:54:18 +01:00
Lennart Poettering
607152c742 varlinkctl: add 'list-registry' command 2026-02-09 10:54:17 +01:00
Lennart Poettering
3e4fca7489 chase: add new flag CHASE_MUST_BE_SOCKET
Just like CHASE_MUST_BE_DIRECTORY and CHASE_MUST_BE_REGULAR, but test if
the inode is a socket.
2026-02-09 10:44:47 +01:00
Lennart Poettering
c0ee33f48f stat-util: add stat_verify_socket() helper 2026-02-09 10:44:47 +01:00
Lennart Poettering
382f141fea units: symlink well-known Varlink services into /run/varlink/registry/
So far we didn't provide any concept to enumerate local Varlink
services. Let's change that.

Let's define very light-weight scheme for this: provide a well-known dir
/run/varlink/registry/ where services that implement public interfaces
can link their sockets into. When enumerating services it's thus
sufficient to enumerate inodes in that directory.

The usecase for this is twofold:

1. It's simply very useful to be able to see which public services are
   bound on the local system, for debugging/admin/development purposes.

2. At Amutable we'd like to optionally provide a HTTP-to-Varlink bridge
   on individual nodes, that allows remote peers (after authentication)
   to access local Varlink services. For that it's essential we know the
   list of services and their entrypoints to expose, it would be
   security-wise highly problematic for clients to provide AF_UNIX
   entrypoint paths when connecting. hence: let's instead just have a
   dir with the public stuff, and let's ensure the HTTP-to-Varlink
   bridge simply exposes that stuff, and nothing else.

Non-public interfaces (such as the oomd interfaces between PID 1 and
oomd), and interfaces with multiple implementors (such as the resolved
hook interface, or the metrics collection stuff) should not be linked
in.

This is inspired by the Varlink.org "registry" concept, briefly
explained here:

https://varlink.org/FAQ#how-do-i-find-the-service-which-implements-a-local-interface

Note however that the described Varlink interface is not actually
implemented here, the directory is introduced however in a fashion that
conceptually matches the registry defined there, and would allow us to
implement the registry interface on top of it. (One of the reason the
registry Varlink API is not implemented right now is that the URI format
it relies on is entirely unspecified in the Varlink docs right now. Some
research needs to be done to extract what's implemented in the reference
implementation and to determine how it maps to the Varlink entrypoint
address format systemd's own tooling currently uses)

This primarily installs the symlinks via Symlinks= in unit files and via
a new tmpfiles.d/ drop-in. But since we touch all .socket units relating
to Varlink this also sets the FileDescriptorName= to varlink for each,
just to minimize diffrences and make things work more alike (the
services in questin don't care about the name, so this doesn't change).
In one case we replace a pair of separate sockets for two closely
related varlink services by a socket and a symlink, so that we can
safely use Symlinks= to also install the registry symlinks.
2026-02-09 10:44:47 +01:00
Lennart Poettering
4a54381d07
mountfsd: don't cross mount boundaries when looking for owner of foreign UID owned tree (#40578) 2026-02-09 10:36:22 +01:00
Lennart Poettering
05bbe4247b update TODO 2026-02-09 10:33:29 +01:00
Lennart Poettering
2557f78c07 mountfsd: do not cross mount boundaries when looking for parent of foreign UID range owned dirs
This is primarily paranoia: it might be possible for unpriv users to set
up mount hierarchies in unexpected ways when using userns. Hence let's
make protections more rigid: when looking for a parent dir of a foreign
UID owned dir tree, refuse to cross mount boundaries.
2026-02-09 10:33:01 +01:00
Lennart Poettering
f5a7247afb stat-util: add statx_verify_directory() helper, similar to stat_verify_directory() but for statx 2026-02-09 10:33:01 +01:00
Lennart Poettering
780f15ee7d mountpoint-util: use xstatx() a bit more 2026-02-09 10:33:01 +01:00
Lennart Poettering
2c756c6964 dirent-util: use xstatx_full() some more 2026-02-09 10:33:01 +01:00
Zbigniew Jędrzejewski-Szmek
15777d6d5c
report: many smaller clean-ups/tweaks to systemd-report (#40598)
/cc @keszybz
2026-02-09 10:28:29 +01:00
Lennart Poettering
cfee2c1900 kernel-install: allocate "Context" object only in verb_xyz() functions, not already in run()
We soon want to add a Varlink interface to this, but that means that the
various paramaters for the Context object will be sourced from a Varlink
message not from the command line. Hence split apart the parsing logic
so that we alway parse the command line into arg_xyz first, and then,
inside the verb_abc() calls copy the data from there into the Context
object.

This matches a similar pattern in bootctl.
2026-02-09 10:21:16 +01:00
Lennart Poettering
533afe86c3 kernel-install: rework in preparation for varlink
This reworks things a bit, so that the "Context" object can later be
allocated for each Varlink call separately. For example we define a
more precise CONTEXT_NULL that invalidates truly all fields, so that we
can discern "defaults" from "unspecified" later on.

Other minor rearrangements too
2026-02-09 10:21:16 +01:00
Daan De Meyer
7d4ea41bd1
bootctl parts of installer PR (#40447)
This contains the "bootctl install" related work from the #38764 split
out, but also includes the preparatory work already split out into
#40446.

I'll rebase this PR once the prep work is merged.

This has a simple CI test already, and has docs
2026-02-09 09:46:39 +01:00
Mike Yuan
8b7e98f183 journal-send: check if $LOG_NAMESPACE denotes our /run/systemd/journal/ is already namespaced properly 2026-02-09 09:41:47 +01:00
Luca Boccassi
a2e55fceee docs: note step to update obs workflow file on release 2026-02-09 09:36:53 +01:00
Luca Boccassi
4834014018 README: note that we now have packages built from stable branch too 2026-02-09 09:36:53 +01:00
Mike Gilbert
a304f6c9db meson: use printf instead of echo
The echo builtin provided by some shells (mksh) will interpret \x2d as
an escape sequence. This causes meson to fail:

```
test/fuzz/meson.build:93:52: ERROR: File fuzz-unit-file/dm-back-slash.swap does not exist.
```

Bug: https://bugs.gentoo.org/969789
2026-02-09 09:36:26 +01:00
Lennart Poettering
7e8c35ce80 update TODO 2026-02-09 09:33:07 +01:00
Mike Yuan
cc9a9d6a15
tree-wide: use path_split_prefix_filename() where appropriate 2026-02-09 09:07:15 +01:00
Mike Yuan
18a8ea3377
path-util: unify path_extract_filename/directory into path_split_prefix_filename() 2026-02-09 09:05:45 +01:00
Mike Yuan
294855953c
path-util: drop redundant condition in path_find_last_component()
Follow-up for 3a7ba9f6b9d9a80c7f909bfbf24b5fc8c99a3176
2026-02-09 09:05:44 +01:00
Mike Yuan
3788ec1b4a
snapshot-util: include shared-forward.h 2026-02-09 09:05:44 +01:00
Lennart Poettering
2d34ff822b TODO: add some items about report/metrics 2026-02-07 23:55:23 +01:00
Lennart Poettering
e180087038 man: add super basic man page 2026-02-07 23:36:48 +01:00
Lennart Poettering
9b09b11d21 report: rework limits logic
Let's count how many sources we skip, and output that as a summary, and
return an error exit code in this case.

Let's also put a limit on the metrics collected, not just the sources.

Also, our macros that put limits on things are usually called XYZ_MAX,
follow the same scheme here.
2026-02-07 23:30:00 +01:00
Lennart Poettering
a7aebb2250 report: don't claim no reporting sockets were found, when it's no metrics that were found
Let's add a proper message about missing sources, and call them
"sources", i.e. take a more high-level view on things.
2026-02-07 23:28:51 +01:00
Lennart Poettering
1b9506601f report: keep track of varlink connections inside of Context object
Let's also move the Varlink connection management into the Context
object. Let's also switch to Set* for it, so that we get get
auto-expanding behaviour.
2026-02-07 23:28:51 +01:00
Lennart Poettering
6c051e84c7 report: move event loop object into Context
It's one of the primary objects that make up the program "context"
conceptually, hence it also should be part of the Context object. This
allows us to just have it available if the Context object is seen.
2026-02-07 23:13:16 +01:00
Lennart Poettering
3317ea84bc report: pass Context as first argument to metrics_call()
Typically the context object should be the first one. And the return
parameters should be the last ones.
2026-02-07 23:13:16 +01:00
Lennart Poettering
9fa9f10088 report: -p is not defined 2026-02-07 23:13:16 +01:00
Lennart Poettering
0ec663a41f report: do not treat an empty report dir as an issue
We should permit that the report varlink dir is created on the fly when
the first socket is bound there. Hence, let's treat a non-existant dir
equivalent to an empty one.

We usually do this in our tree like this, do it here too.
2026-02-07 23:13:16 +01:00
Lennart Poettering
1f8b09dfc6 report: use sd_json_variant_unref_many() 2026-02-07 23:13:16 +01:00
Lennart Poettering
75e1963df1 report: add the usual --json= argument 2026-02-07 23:13:16 +01:00
Lennart Poettering
a537c5e4b8 bootctl: add comments emphasizing that certain functions do not touch the file read pointer 2026-02-06 23:17:02 +01:00
Lennart Poettering
1dad3b6762 bootctl: add test case for bootctl install via varlink 2026-02-06 23:17:02 +01:00
Lennart Poettering
9b29d38c33 bootctl: return recognizable Varlink error when we cannot determine the boot entry token
When running "bootctl install" on an empty --root= dir, we don't know
which token to use, and the operation will fail. Make sure to return an
explicit error about this.

This introduces a recognizable low-level error for this (EUNATCH), and
then turns this into a recognizable Varlink error.

(I made sure that the old low-level error EINVAL wasn't load-bearing,
and it is safe to change this.)
2026-02-06 23:16:50 +01:00
Lennart Poettering
91b3620b07 bootctl: optionally include backing disk name in efi boot option description 2026-02-06 23:16:49 +01:00
Lennart Poettering
7ee2f6657f bootctl: parse install source via our usual string table helpers 2026-02-06 23:16:00 +01:00
Lennart Poettering
aea76373b2 bootctl: add Install() varlink API
Fixes: #11221
2026-02-06 23:15:49 +01:00
Lennart Poettering
38433a6d06 bootctl: rework bootctl-install.c in preparation of varlinkification
This primarily introduces a context object for each operation, so that
we later can instantiate one for each varlink op we execute, and can
safely lifecycle all operation parameters for each subequent call.

This also reworks the root dir handling to be fd based.

This drops explicit CHASE_TRIGGER_AUTOFS from a bunch of chase() calls
that operate within the ESP/XBOOTLDR, while it keeps them in place for the
chase() calls that find the top-level ESP/XBOOTLDR inode. This reflects
the fact that we explicitly support autofs for the ESP/XBOOTLDR itself,
but below it expect no further mounts, just plain VFAT.

This changes behaviour of the interaction of $KERNEL_INSTALL_CONF_ROOT
and --root=: the former will now be taken relative to the host root, and
will no longer be affected by --root=. This follows similar behaviour in
kernel-install, where it is very explicitly documented in the man page
(the bootclt man page does not document this). This is strictly speaking
a compat breakage, but i think a very minor, niche one, and I think the
pain afflicted by this change is probably neglible compare to the
unsystematic behaviour comapred to kernel-install.
2026-02-06 23:15:47 +01:00
80 changed files with 2248 additions and 844 deletions

View File

@ -38,4 +38,5 @@ Stable branches with backported patches are available in the [stable repo](https
We have a security bug bounty program sponsored by the [Sovereign Tech Fund](https://www.sovereigntechfund.de/) hosted on [YesWeHack](https://yeswehack.com/programs/systemd-bug-bounty-program) We have a security bug bounty program sponsored by the [Sovereign Tech Fund](https://www.sovereigntechfund.de/) hosted on [YesWeHack](https://yeswehack.com/programs/systemd-bug-bounty-program)
Repositories with distribution packages built from git main are [available on OBS](https://software.opensuse.org//download.html?project=system%3Asystemd&package=systemd) Repositories with distribution packages built from git main are [available on OBS](https://software.opensuse.org//download.html?project=system%3Asystemd&package=systemd),
and also repositories with [packages built from the latest stable release](https://software.opensuse.org//download.html?project=system%3Asystemd%3Astable&package=systemd)

19
TODO
View File

@ -116,10 +116,25 @@ Deprecations and removals:
* Consider removing root=gpt-auto, and push people to use root=dissect instead. * Consider removing root=gpt-auto, and push people to use root=dissect instead.
* remove any trace of "cpuacct" cgroup controller, it's a cgroupv1 thing.
similar "devices"
Features: Features:
* mountfsd: when looking for non-foreign uid owned inodes up the tree, stop at * report:
mounts - should the list of metrics use JSON-SEQ? or maybe be wrapped in a json
array (the latter might be necessary, once we sign the combination)
- "io.systemd.Manager.unit_active_state" is a weird mix of CamelCase and snake_case
- metrics from pid1: suppress metrics form units that are inactive and have nothing to report
- how to plug facts into this? i.e. hostname, ssh keys, and so on
- switch to daan's suggested hierarchy?
* implement a varlink registry service, similar to the one of the reference
implementation, backed by /run/varlink/registry/. Then, also implement
connect-via-registry-resolution in sd-varlink and varlinkctl. Care needs to
be taken to do the resolution asynchronousy. Also, note that the Varlink
reference implementation uses a different address syntax, which needs to be
taken into account.
* downgrade the uid/gid disposition enforcement in udev * downgrade the uid/gid disposition enforcement in udev

View File

@ -42,7 +42,7 @@ Thus code that is used by "higher-level" components (e.g. our binaries which are
would go to a subdirectory specific to that component if it is only used there. would go to a subdirectory specific to that component if it is only used there.
If the code is to be shared between components, it'd go to `src/shared/`. If the code is to be shared between components, it'd go to `src/shared/`.
Shared code that is used by multiple components that do not link to `libsystemd-shared-<nnn>.so` may live either in `src/libsystemd/`, `src/basic/`, or `src/fundamental/`. Shared code that is used by multiple components that do not link to `libsystemd-shared-<nnn>.so` may live either in `src/libsystemd/`, `src/basic/`, or `src/fundamental/`.
Any code that is used only for EFI goes under `src/boot/efi/`, and in `src/fundamental/` if it is shared with non-EFI components. Code used only for EFI goes under `src/boot/`, and under `src/fundamental/` if it is shared with non-EFI components.
To summarize: To summarize:

View File

@ -671,6 +671,10 @@ SYSTEMD_HOME_DEBUG_SUFFIX=foo \
specified algorithm takes an effect immediately, you need to explicitly run specified algorithm takes an effect immediately, you need to explicitly run
`journalctl --rotate`. `journalctl --rotate`.
* `$SYSTEMD_JOURNAL_FD_SIZE_MAX` Takes a size with the usual suffixes (K, M, ...) in
string format. Overrides the default maximum allowed size for a file-descriptor
based input record to be stored in the journal.
* `$SYSTEMD_CATALOG` path to the compiled catalog database file to use for * `$SYSTEMD_CATALOG` path to the compiled catalog database file to use for
`journalctl -x`, `journalctl --update-catalog`, `journalctl --list-catalog` `journalctl -x`, `journalctl --update-catalog`, `journalctl --list-catalog`
and related calls. and related calls.

View File

@ -26,14 +26,15 @@ SPDX-License-Identifier: LGPL-2.1-or-later
17. Check that announcement to systemd-devel, with a copy&paste from NEWS, was sent. This should happen automatically. 17. Check that announcement to systemd-devel, with a copy&paste from NEWS, was sent. This should happen automatically.
18. Update IRC topic (`/msg chanserv TOPIC #systemd Version NNN released | Online resources https://systemd.io/`) 18. Update IRC topic (`/msg chanserv TOPIC #systemd Version NNN released | Online resources https://systemd.io/`)
19. [FINAL] Create an empty -stable branch: `git push systemd origin/main:refs/heads/v${version}-stable`. 19. [FINAL] Create an empty -stable branch: `git push systemd origin/main:refs/heads/v${version}-stable`.
20. [FINAL] Build and upload the documentation (on the -stable branch): `ninja -C build doc-sync` 20. [FINAL] Edit in the new -stable branch `.obs/workflows.yml`, changing `project` from `system:systemd` to `system:systemd:stable` and the branch from `main` to `v${version}-stable`. This should be backported to older active stable branches too.
21. [FINAL] Create a new `ci/v${version}-stable` branch for deb package builds on https://salsa.debian.org/systemd-team/systemd 21. [FINAL] Build and upload the documentation (on the -stable branch): `ninja -C build doc-sync`
22. [FINAL] Switch `.semaphore/semaphore-runner.sh` and `mkosi/mkosi.pkgenv/mkosi.conf.d/debian-ubuntu.conf` 22. [FINAL] Create a new `ci/v${version}-stable` branch for deb package builds on https://salsa.debian.org/systemd-team/systemd
23. [FINAL] Switch `.semaphore/semaphore-runner.sh` and `mkosi/mkosi.pkgenv/mkosi.conf.d/debian-ubuntu.conf`
to the new `ci/v${version}-stable` branch on the -stable branch to the new `ci/v${version}-stable` branch on the -stable branch
23. [FINAL] Switch `versionrewrite-pattern` and `versionrewrite-replacement` to release mode in https://build.opensuse.org/projects/system:systemd/packages/systemd/files/_service?expand=1 24. [FINAL] Switch `versionrewrite-pattern` and `versionrewrite-replacement` to release mode in https://build.opensuse.org/projects/system:systemd/packages/systemd/files/_service?expand=1
24. [FINAL] Change the Github Pages branch to the newly created branch (https://github.com/systemd/systemd/settings/pages) and set the 'Custom domain' to 'systemd.io' 25. [FINAL] Change the Github Pages branch to the newly created branch (https://github.com/systemd/systemd/settings/pages) and set the 'Custom domain' to 'systemd.io'
25. [FINAL] Update version number in `meson.version` to the devel version of the next release (e.g. from `256` to `257~devel`) 26. [FINAL] Update version number in `meson.version` to the devel version of the next release (e.g. from `256` to `257~devel`)
26. [FINAL] Build and upload the documentation (on the main branch): `ninja -C build doc-sync` 27. [FINAL] Build and upload the documentation (on the main branch): `ninja -C build doc-sync`
# Steps to a Successful Stable Release # Steps to a Successful Stable Release

View File

@ -266,12 +266,12 @@ def check_matches(groups):
if gr: if gr:
# we check this first to provide an easy error message # we check this first to provide an easy error message
if rest[-1] not in '*:': if rest[-1] not in '*:':
error('Pattern {} does not end with "*" or ":"', match) error('Pattern {!r} does not end with "*" or ":"', match)
try: try:
gr.parseString(rest) gr.parseString(rest)
except ParseBaseException as e: except ParseBaseException as e:
error('Pattern {} is invalid: {}', match, e) error('Pattern {!r} is invalid: {}', match, e)
continue continue
matches.sort() matches.sort()
@ -353,12 +353,11 @@ def print_summary(fname, groups):
error(f'{fname}: no matches or props') error(f'{fname}: no matches or props')
if __name__ == '__main__': if __name__ == '__main__':
args = sys.argv[1:] or sorted( args = sys.argv[1:] or sorted([
[
os.path.dirname(sys.argv[0]) + '/20-dmi-id.hwdb', os.path.dirname(sys.argv[0]) + '/20-dmi-id.hwdb',
os.path.dirname(sys.argv[0]) + '/20-net-ifname.hwdb', os.path.dirname(sys.argv[0]) + '/20-net-ifname.hwdb',
] + glob.glob(os.path.dirname(sys.argv[0]) + '/[678][0-9]-*.hwdb') *glob.glob(os.path.dirname(sys.argv[0]) + '/[678][0-9]-*.hwdb'),
) ])
for fname in args: for fname in args:
groups = parse(fname) groups = parse(fname)

View File

@ -524,6 +524,18 @@
<xi:include href="version-info.xml" xpointer="v252"/></listitem> <xi:include href="version-info.xml" xpointer="v252"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--efi-boot-option-description-with-device=</option></term>
<listitem><para>Takes a boolean, defaults to false. Controls whether to append disk model information
to the firmware boot option item description (as configured with
<option>--efi-boot-option-description=</option> above). This is useful when installing multiple
operating systems on separate disks on the same system, as it ensures the firmware boot options are discernable
and give a hint which disk is booted into. Note that this uses hardware model information, and hence
might not be too useful in case multiple disks of an identical model are used.</para>
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--dry-run</option></term> <term><option>--dry-run</option></term>
<listitem><para>Dry run for <option>unlink</option> and <option>cleanup</option>.</para> <listitem><para>Dry run for <option>unlink</option> and <option>cleanup</option>.</para>

View File

@ -1132,6 +1132,7 @@ manpages = [
'ENABLE_RANDOMSEED'], 'ENABLE_RANDOMSEED'],
['systemd-remount-fs.service', '8', ['systemd-remount-fs'], ''], ['systemd-remount-fs.service', '8', ['systemd-remount-fs'], ''],
['systemd-repart', '8', ['systemd-repart.service'], 'ENABLE_REPART'], ['systemd-repart', '8', ['systemd-repart.service'], 'ENABLE_REPART'],
['systemd-report', '1', [], ''],
['systemd-resolved.service', '8', ['systemd-resolved'], 'ENABLE_RESOLVE'], ['systemd-resolved.service', '8', ['systemd-resolved'], 'ENABLE_RESOLVE'],
['systemd-rfkill.service', ['systemd-rfkill.service',
'8', '8',

74
man/systemd-report.xml Normal file
View File

@ -0,0 +1,74 @@
<?xml version='1.0'?> <!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="systemd-report"
xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>systemd-report</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>systemd-report</refentrytitle>
<manvolnum>1</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd-report</refname>
<refpurpose>Generate report of system facts and metrics</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>systemd-report</command> <arg choice="opt" rep="repeat">OPTIONS</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><command>systemd-report</command> requests facts and metrics from the system and writes them to
standard output.</para>
</refsect1>
<refsect1>
<title>Options</title>
<para>The following options are understood:</para>
<variablelist>
<varlistentry>
<term><option>--system</option></term>
<term><option>--user</option></term>
<listitem>
<para>Query per-system metrics sources (the default), or the per-user metrics sources.</para>
<xi:include href="version-info.xml" xpointer="v260"/>
</listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="json" />
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
</refsect1>
<refsect1>
<title>Exit status</title>
<para>On success, 0 is returned, a non-zero failure code
otherwise.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>

View File

@ -181,6 +181,16 @@
<xi:include href="version-info.xml" xpointer="v255"/></listitem> <xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><command>list-registry</command></term>
<listitem><para>Shows a list of Varlink services currently registered in the service registry, plus
their entrypoint sockets. (Currently, this simply enumerates the sockets and symlinked sockets in
<filename>/run/varlink/registry/</filename>, see below.)</para>
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><command>validate-idl</command> [<replaceable>FILE</replaceable>]</term> <term><command>validate-idl</command> [<replaceable>FILE</replaceable>]</term>
@ -354,6 +364,18 @@
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--system</option></term>
<term><option>--user</option></term>
<listitem>
<para>Determines whether to query to the per-system or per-user registry when using the
<command>list-registry</command> command. By default, the per-system registry is queried.</para>
</listitem>
<xi:include href="version-info.xml" xpointer="v260"/>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="no-ask-password" /> <xi:include href="standard-options.xml" xpointer="no-ask-password" />
<xi:include href="standard-options.xml" xpointer="no-pager" /> <xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="help" /> <xi:include href="standard-options.xml" xpointer="help" />
@ -361,6 +383,31 @@
</variablelist> </variablelist>
</refsect1> </refsect1>
<refsect1>
<title>Files &amp; Directories</title>
<variablelist>
<varlistentry>
<term><filename>/run/varlink/registry/</filename></term>
<listitem>
<para>Directory containing <constant>AF_UNIX</constant> entrypoint socket inodes (or symlinks to
them) of well-known, public Varlink interfaces on the local system. They are named after the
Varlink interface they implement.</para>
<para>Use <command>varlinkctl list-registry</command> to show the contents of this
directory.</para>
<para>(Inodes that neither qualify as socket inodes nor as symlinks to them shall be ignored. A
future extension might introduce regular files and directories to enhance the registry
functionality.)</para>
<xi:include href="version-info.xml" xpointer="v260"/>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1> <refsect1>
<title>Examples</title> <title>Examples</title>

View File

@ -3,7 +3,7 @@
install_data('70-systemd-shell-extra.sh', install_dir : shellprofiledir.startswith('/usr/') ? shellprofiledir : libexecdir / 'profile.d') install_data('70-systemd-shell-extra.sh', install_dir : shellprofiledir.startswith('/usr/') ? shellprofiledir : libexecdir / 'profile.d')
install_data('80-systemd-osc-context.sh', install_dir : shellprofiledir.startswith('/usr/') ? shellprofiledir : libexecdir / 'profile.d') install_data('80-systemd-osc-context.sh', install_dir : shellprofiledir.startswith('/usr/') ? shellprofiledir : libexecdir / 'profile.d')
if conf.get('LINK_SHELL_EXTRA_DROPIN') == 1 if (not shellprofiledir.startswith(sysconfdir) or install_sysconfdir) and conf.get('LINK_SHELL_EXTRA_DROPIN') == 1
if meson.version().version_compare('>=1.3.0') if meson.version().version_compare('>=1.3.0')
install_symlink( install_symlink(
'70-systemd-shell-extra.sh', '70-systemd-shell-extra.sh',
@ -19,7 +19,7 @@ if conf.get('LINK_SHELL_EXTRA_DROPIN') == 1
endif endif
endif endif
if conf.get('LINK_OSC_CONTEXT_DROPIN') == 1 if (not shellprofiledir.startswith(sysconfdir) or install_sysconfdir) and conf.get('LINK_OSC_CONTEXT_DROPIN') == 1
if meson.version().version_compare('>=1.3.0') if meson.version().version_compare('>=1.3.0')
install_symlink( install_symlink(
'80-systemd-osc-context.sh', '80-systemd-osc-context.sh',

View File

@ -63,7 +63,7 @@ _varlinkctl() {
fi fi
local -A VERBS=( local -A VERBS=(
[STANDALONE]='help' [STANDALONE]='help list-registry'
[CALL]='call' [CALL]='call'
[FILE]='info list-interfaces validate-idl' [FILE]='info list-interfaces validate-idl'
[ADDRESS_INTERFACES]='list-methods introspect' [ADDRESS_INTERFACES]='list-methods introspect'

View File

@ -26,7 +26,10 @@
CHASE_STEP | \ CHASE_STEP | \
CHASE_PROHIBIT_SYMLINKS | \ CHASE_PROHIBIT_SYMLINKS | \
CHASE_PARENT | \ CHASE_PARENT | \
CHASE_MKDIR_0755) CHASE_MKDIR_0755 | \
CHASE_MUST_BE_DIRECTORY | \
CHASE_MUST_BE_REGULAR | \
CHASE_MUST_BE_SOCKET)
bool unsafe_transition(const struct stat *a, const struct stat *b) { bool unsafe_transition(const struct stat *a, const struct stat *b) {
/* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
@ -133,7 +136,6 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
int r; int r;
assert(!FLAGS_SET(flags, CHASE_PREFIX_ROOT)); assert(!FLAGS_SET(flags, CHASE_PREFIX_ROOT));
assert(!FLAGS_SET(flags, CHASE_MUST_BE_DIRECTORY|CHASE_MUST_BE_REGULAR));
assert(!FLAGS_SET(flags, CHASE_STEP|CHASE_EXTRACT_FILENAME)); assert(!FLAGS_SET(flags, CHASE_STEP|CHASE_EXTRACT_FILENAME));
assert(!FLAGS_SET(flags, CHASE_NO_AUTOFS|CHASE_TRIGGER_AUTOFS)); assert(!FLAGS_SET(flags, CHASE_NO_AUTOFS|CHASE_TRIGGER_AUTOFS));
assert(dir_fd >= 0 || IN_SET(dir_fd, AT_FDCWD, XAT_FDROOT)); assert(dir_fd >= 0 || IN_SET(dir_fd, AT_FDCWD, XAT_FDROOT));
@ -328,6 +330,10 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
if (FLAGS_SET(flags, CHASE_PARENT)) if (FLAGS_SET(flags, CHASE_PARENT))
flags |= CHASE_MUST_BE_DIRECTORY; flags |= CHASE_MUST_BE_DIRECTORY;
/* If multiple flags are set now, fail immediately */
if (FLAGS_SET(flags, CHASE_MUST_BE_DIRECTORY) + FLAGS_SET(flags, CHASE_MUST_BE_REGULAR) + FLAGS_SET(flags, CHASE_MUST_BE_SOCKET) > 1)
return -EBADSLT;
for (todo = buffer;;) { for (todo = buffer;;) {
_cleanup_free_ char *first = NULL; _cleanup_free_ char *first = NULL;
_cleanup_close_ int child = -EBADF; _cleanup_close_ int child = -EBADF;
@ -566,6 +572,12 @@ success:
if (r < 0) if (r < 0)
return r; return r;
} }
if (FLAGS_SET(flags, CHASE_MUST_BE_SOCKET)) {
r = stat_verify_socket(&st);
if (r < 0)
return r;
}
} }
if (ret_path) { if (ret_path) {
@ -872,7 +884,7 @@ int chase_and_opendir(const char *path, const char *root, ChaseFlags chase_flags
DIR *d; DIR *d;
int r; int r;
assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP|CHASE_MUST_BE_REGULAR))); assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP|CHASE_MUST_BE_REGULAR|CHASE_MUST_BE_SOCKET)));
assert(ret_dir); assert(ret_dir);
if (empty_or_root(root) && !ret_path && (chase_flags & CHASE_NO_SHORTCUT_MASK) == 0) { if (empty_or_root(root) && !ret_path && (chase_flags & CHASE_NO_SHORTCUT_MASK) == 0) {
@ -970,7 +982,7 @@ int chase_and_fopen_unlocked(
int mode_flags, r; int mode_flags, r;
assert(path); assert(path);
assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP|CHASE_PARENT|CHASE_MUST_BE_DIRECTORY))); assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP|CHASE_PARENT|CHASE_MUST_BE_DIRECTORY|CHASE_MUST_BE_SOCKET)));
assert(open_flags); assert(open_flags);
assert(ret_file); assert(ret_file);
@ -1040,7 +1052,7 @@ int chase_and_openat(
_cleanup_free_ char *p = NULL, *fname = NULL; _cleanup_free_ char *p = NULL, *fname = NULL;
int r; int r;
assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP))); assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP|CHASE_MUST_BE_SOCKET)));
XOpenFlags xopen_flags = 0; XOpenFlags xopen_flags = 0;
if (FLAGS_SET(chase_flags, CHASE_MUST_BE_DIRECTORY)) if (FLAGS_SET(chase_flags, CHASE_MUST_BE_DIRECTORY))
@ -1086,7 +1098,7 @@ int chase_and_opendirat(int dir_fd, const char *path, ChaseFlags chase_flags, ch
DIR *d; DIR *d;
int r; int r;
assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP|CHASE_MUST_BE_REGULAR))); assert(!(chase_flags & (CHASE_NONEXISTENT|CHASE_STEP|CHASE_MUST_BE_REGULAR|CHASE_MUST_BE_SOCKET)));
assert(ret_dir); assert(ret_dir);
if (dir_fd == AT_FDCWD && !ret_path && (chase_flags & CHASE_NO_SHORTCUT_MASK) == 0) { if (dir_fd == AT_FDCWD && !ret_path && (chase_flags & CHASE_NO_SHORTCUT_MASK) == 0) {

View File

@ -29,6 +29,7 @@ typedef enum ChaseFlags {
CHASE_EXTRACT_FILENAME = 1 << 13, /* Only return the last component of the resolved path */ CHASE_EXTRACT_FILENAME = 1 << 13, /* Only return the last component of the resolved path */
CHASE_MUST_BE_DIRECTORY = 1 << 14, /* Fail if returned inode fd is not a dir */ CHASE_MUST_BE_DIRECTORY = 1 << 14, /* Fail if returned inode fd is not a dir */
CHASE_MUST_BE_REGULAR = 1 << 15, /* Fail if returned inode fd is not a regular file */ CHASE_MUST_BE_REGULAR = 1 << 15, /* Fail if returned inode fd is not a regular file */
CHASE_MUST_BE_SOCKET = 1 << 16, /* Fail if returned inode fd is not a socket */
} ChaseFlags; } ChaseFlags;
bool unsafe_transition(const struct stat *a, const struct stat *b); bool unsafe_transition(const struct stat *a, const struct stat *b);

View File

@ -5,10 +5,11 @@
#include "dirent-util.h" #include "dirent-util.h"
#include "path-util.h" #include "path-util.h"
#include "stat-util.h"
#include "string-util.h" #include "string-util.h"
int dirent_ensure_type(int dir_fd, struct dirent *de) { int dirent_ensure_type(int dir_fd, struct dirent *de) {
struct statx sx; int r;
assert(dir_fd >= 0); assert(dir_fd >= 0);
assert(de); assert(de);
@ -22,8 +23,16 @@ int dirent_ensure_type(int dir_fd, struct dirent *de) {
} }
/* Let's ask only for the type, nothing else. */ /* Let's ask only for the type, nothing else. */
if (statx(dir_fd, de->d_name, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_TYPE, &sx) < 0) struct statx sx;
return -errno; r = xstatx_full(dir_fd,
de->d_name,
AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT,
/* mandatory_mask= */ STATX_TYPE,
/* optional_mask= */ STATX_INO,
/* mandatory_attributes= */ 0,
&sx);
if (r < 0)
return r;
assert(FLAGS_SET(sx.stx_mask, STATX_TYPE)); assert(FLAGS_SET(sx.stx_mask, STATX_TYPE));
de->d_type = IFTODT(sx.stx_mode); de->d_type = IFTODT(sx.stx_mode);

View File

@ -69,15 +69,14 @@ int make_lock_file_for(const char *p, int operation, LockFile *ret) {
assert(p); assert(p);
assert(ret); assert(ret);
r = path_extract_filename(p, &fn); r = path_split_prefix_filename(p, &dn, &fn);
if (r < 0)
return r;
r = path_extract_directory(p, &dn);
if (r < 0) if (r < 0)
return r; return r;
if (dn)
t = strjoin(dn, "/.#", fn, ".lck"); t = strjoin(dn, "/.#", fn, ".lck");
else
t = strjoin(".#", fn, ".lck");
if (!t) if (!t)
return -ENOMEM; return -ENOMEM;

View File

@ -263,9 +263,14 @@ int is_mount_point_at(int dir_fd, const char *path, int flags) {
/* When running on chroot environment, the root may not be a mount point, but we unconditionally /* When running on chroot environment, the root may not be a mount point, but we unconditionally
* return true when the input is "/" in the above, but the shortcut may not work e.g. when the path * return true when the input is "/" in the above, but the shortcut may not work e.g. when the path
* is relative. */ * is relative. */
struct statx sx2 = {}; /* explicitly initialize the struct to make msan silent. */ struct statx sx2;
if (statx(AT_FDCWD, "/", AT_STATX_DONT_SYNC, STATX_TYPE|STATX_INO, &sx2) < 0) r = xstatx(AT_FDCWD,
return -errno; "/",
AT_STATX_DONT_SYNC,
STATX_TYPE|STATX_INO,
&sx2);
if (r < 0)
return r;
return statx_inode_same(&sx, &sx2); return statx_inode_same(&sx, &sx2);
} }

View File

@ -1027,8 +1027,7 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char *
q = path + strlen(path) - 1; q = path + strlen(path) - 1;
q = skip_slash_or_dot_backward(path, q); q = skip_slash_or_dot_backward(path, q);
if (!q || /* the root directory */ if (!q) { /* the root directory, "." or "./" */
(q == path && *q == '.')) { /* path is "." or "./" */
if (next) if (next)
*next = path; *next = path;
if (ret) if (ret)
@ -1102,18 +1101,17 @@ const char* last_path_component(const char *path) {
return path + k; return path + k;
} }
int path_extract_filename(const char *path, char **ret) { int path_split_prefix_filename(const char *path, char **ret_dir, char **ret_filename) {
_cleanup_free_ char *a = NULL; _cleanup_free_ char *d = NULL;
const char *c, *next = NULL; const char *c, *next = NULL;
int r; int r;
/* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes /* Split the path into dir prefix/filename pair. Returns:
* filename_is_valid(). A wrapper around last_path_component(), but eats up trailing
* slashes. Returns:
* *
* -EINVAL if the path is not valid * -EINVAL if the path is not valid
* -EADDRNOTAVAIL if only a directory was specified, but no filename, i.e. the root dir * -EADDRNOTAVAIL if the path refers to the uppermost directory in hierarchy (i.e. has neither
* itself or "." is specified * dir prefix nor filename - the root dir itself or ".")
* -EDESTADDRREQ if only a filename was passed, and caller only specifies ret_dir
* -ENOMEM no memory * -ENOMEM no memory
* *
* Returns >= 0 on success. If the input path has a trailing slash, returns O_DIRECTORY, to * Returns >= 0 on success. If the input path has a trailing slash, returns O_DIRECTORY, to
@ -1122,63 +1120,52 @@ int path_extract_filename(const char *path, char **ret) {
* This function guarantees to return a fully valid filename, i.e. one that passes * This function guarantees to return a fully valid filename, i.e. one that passes
* filename_is_valid() this means "." and ".." are not accepted. */ * filename_is_valid() this means "." and ".." are not accepted. */
if (!path_is_valid(path)) if (isempty(path))
return -EINVAL; return -EINVAL;
r = path_find_last_component(path, false, &next, &c); r = path_find_last_component(path, /* accept_dot_dot = */ false, &next, &c);
if (r < 0) if (r < 0)
return r; return r;
if (r == 0) /* root directory */ if (r == 0) /* root directory or "." */
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
a = strndup(c, r); if (ret_dir) {
if (!a)
return -ENOMEM;
*ret = TAKE_PTR(a);
return strlen(c) > (size_t) r ? O_DIRECTORY : 0;
}
int path_extract_directory(const char *path, char **ret) {
const char *c, *next = NULL;
int r;
/* The inverse of path_extract_filename(), i.e. returns the directory path prefix. Returns:
*
* -EINVAL if the path is not valid
* -EDESTADDRREQ if no directory was specified in the passed in path, i.e. only a filename was passed
* -EADDRNOTAVAIL if the passed in parameter had no filename but did have a directory, i.e.
* the root dir itself or "." was specified
* -ENOMEM no memory (surprise!)
*
* This function guarantees to return a fully valid path, i.e. one that passes path_is_valid().
*/
r = path_find_last_component(path, false, &next, &c);
if (r < 0)
return r;
if (r == 0) /* empty or root */
return isempty(path) ? -EINVAL : -EADDRNOTAVAIL;
if (next == path) { if (next == path) {
if (*path != '/') /* filename only */ if (*path != '/') { /* filename only */
if (!ret_filename)
return -EDESTADDRREQ; return -EDESTADDRREQ;
} else {
return strdup_to(ret, "/"); d = strdup("/");
if (!d)
return -ENOMEM;
} }
} else {
_cleanup_free_ char *a = strndup(path, next - path); d = strndup(path, next - path);
if (!a) if (!d)
return -ENOMEM; return -ENOMEM;
path_simplify(a); path_simplify(d);
if (!path_is_valid(a)) if (!path_is_valid(d))
return -EINVAL;
}
} else if (!path_is_valid(path))
/* We didn't validate the dir prefix, hence check if the whole path is valid now */
return -EINVAL; return -EINVAL;
if (ret) if (ret_filename) {
*ret = TAKE_PTR(a); char *fn = strndup(c, r);
if (!fn)
return -ENOMEM;
return 0; *ret_filename = fn;
}
if (ret_dir)
*ret_dir = TAKE_PTR(d);
return strlen(c) > (size_t) r ? O_DIRECTORY : 0;
} }
bool filename_part_is_valid(const char *p) { bool filename_part_is_valid(const char *p) {

View File

@ -135,8 +135,15 @@ int fsck_exists_for_fstype(const char *fstype);
int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret); int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret);
int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret); int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret);
const char* last_path_component(const char *path); const char* last_path_component(const char *path);
int path_extract_filename(const char *path, char **ret);
int path_extract_directory(const char *path, char **ret); int path_split_prefix_filename(const char *path, char **ret_dir, char **ret_filename);
static inline int path_extract_filename(const char *path, char **ret) {
return path_split_prefix_filename(path, NULL, ret);
}
static inline int path_extract_directory(const char *path, char **ret) {
int r = path_split_prefix_filename(path, ret, NULL);
return r < 0 ? r : 0; /* suppress O_DIRECTORY */
}
bool filename_part_is_valid(const char *p) _pure_; bool filename_part_is_valid(const char *p) _pure_;
bool filename_is_valid(const char *p) _pure_; bool filename_is_valid(const char *p) _pure_;

View File

@ -90,6 +90,21 @@ int stat_verify_directory(const struct stat *st) {
return 0; return 0;
} }
int statx_verify_directory(const struct statx *stx) {
assert(stx);
if (!FLAGS_SET(stx->stx_mask, STATX_TYPE))
return -ENODATA;
if (S_ISLNK(stx->stx_mode))
return -ELOOP;
if (!S_ISDIR(stx->stx_mode))
return -ENOTDIR;
return 0;
}
int fd_verify_directory(int fd) { int fd_verify_directory(int fd) {
if (IN_SET(fd, AT_FDCWD, XAT_FDROOT)) if (IN_SET(fd, AT_FDCWD, XAT_FDROOT))
return 0; return 0;
@ -127,6 +142,21 @@ int is_symlink(const char *path) {
return verify_stat_at(AT_FDCWD, path, false, stat_verify_symlink, false); return verify_stat_at(AT_FDCWD, path, false, stat_verify_symlink, false);
} }
int stat_verify_socket(const struct stat *st) {
assert(st);
if (S_ISLNK(st->st_mode))
return -ELOOP;
if (S_ISDIR(st->st_mode))
return -EISDIR;
if (!S_ISSOCK(st->st_mode))
return -ENOTSOCK;
return 0;
}
int stat_verify_linked(const struct stat *st) { int stat_verify_linked(const struct stat *st) {
assert(st); assert(st);

View File

@ -11,6 +11,7 @@ int verify_regular_at(int fd, const char *path, bool follow);
int fd_verify_regular(int fd); int fd_verify_regular(int fd);
int stat_verify_directory(const struct stat *st); int stat_verify_directory(const struct stat *st);
int statx_verify_directory(const struct statx *stx);
int fd_verify_directory(int fd); int fd_verify_directory(int fd);
int is_dir_at(int fd, const char *path, bool follow); int is_dir_at(int fd, const char *path, bool follow);
int is_dir(const char *path, bool follow); int is_dir(const char *path, bool follow);
@ -19,6 +20,8 @@ int stat_verify_symlink(const struct stat *st);
int fd_verify_symlink(int fd); int fd_verify_symlink(int fd);
int is_symlink(const char *path); int is_symlink(const char *path);
int stat_verify_socket(const struct stat *st);
int stat_verify_linked(const struct stat *st); int stat_verify_linked(const struct stat *st);
int fd_verify_linked(int fd); int fd_verify_linked(int fd);

View File

@ -161,17 +161,12 @@ static int tempfn_build(const char *p, const char *pre, const char *post, bool c
if (!d) if (!d)
return -ENOMEM; return -ENOMEM;
} else { } else {
r = path_extract_directory(p, &d); r = path_split_prefix_filename(p, &d, &fn);
if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → No directory specified, just a filename */
return r;
r = path_extract_filename(p, &fn);
if (r < 0) if (r < 0)
return r; return r;
if (strlen(fn) > NAME_MAX - len_add) /* Truncate the filename if it would become too long after mangling. */
/* We cannot simply prepend and append strings to the filename. Let's truncate the filename. */ strshorten(fn, NAME_MAX - len_add);
fn[NAME_MAX - len_add] = '\0';
} }
nf = strjoin(".#", strempty(pre), strempty(fn), strempty(post)); nf = strjoin(".#", strempty(pre), strempty(fn), strempty(post));

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once #pragma once
#include "shared-forward.h"
int verb_install(int argc, char *argv[], void *userdata); int verb_install(int argc, char *argv[], void *userdata);
int verb_remove(int argc, char *argv[], void *userdata); int verb_remove(int argc, char *argv[], void *userdata);
int verb_is_installed(int argc, char *argv[], void *userdata); int verb_is_installed(int argc, char *argv[], void *userdata);
int vl_method_install(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);

View File

@ -75,6 +75,8 @@ int get_file_version(int fd, char **ret) {
assert(fd >= 0); assert(fd >= 0);
assert(ret); assert(ret);
/* Does not reposition file offset (as it uses mmap()) */
if (fstat(fd, &st) < 0) if (fstat(fd, &st) < 0)
return log_error_errno(errno, "Failed to stat EFI binary: %m"); return log_error_errno(errno, "Failed to stat EFI binary: %m");

View File

@ -31,6 +31,7 @@
#include "parse-argument.h" #include "parse-argument.h"
#include "path-util.h" #include "path-util.h"
#include "pretty-print.h" #include "pretty-print.h"
#include "string-table.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
#include "utf8.h" #include "utf8.h"
@ -39,12 +40,6 @@
#include "verbs.h" #include "verbs.h"
#include "virt.h" #include "virt.h"
/* EFI_BOOT_OPTION_DESCRIPTION_MAX sets the maximum length for the boot option description
* stored in NVRAM. The UEFI spec does not specify a minimum or maximum length for this
* string, but we limit the length to something reasonable to prevent from the firmware
* having to deal with a potentially too long string. */
#define EFI_BOOT_OPTION_DESCRIPTION_MAX ((size_t) 255)
static GracefulMode _arg_graceful = ARG_GRACEFUL_NO; static GracefulMode _arg_graceful = ARG_GRACEFUL_NO;
char *arg_esp_path = NULL; char *arg_esp_path = NULL;
@ -69,6 +64,7 @@ char *arg_root = NULL;
char *arg_image = NULL; char *arg_image = NULL;
InstallSource arg_install_source = INSTALL_SOURCE_AUTO; InstallSource arg_install_source = INSTALL_SOURCE_AUTO;
char *arg_efi_boot_option_description = NULL; char *arg_efi_boot_option_description = NULL;
bool arg_efi_boot_option_description_with_device = false;
bool arg_dry_run = false; bool arg_dry_run = false;
ImagePolicy *arg_image_policy = NULL; ImagePolicy *arg_image_policy = NULL;
bool arg_varlink = false; bool arg_varlink = false;
@ -93,6 +89,14 @@ STATIC_DESTRUCTOR_REGISTER(arg_certificate_source, freep);
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep); STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
static const char* const install_source_table[_INSTALL_SOURCE_MAX] = {
[INSTALL_SOURCE_IMAGE] = "image",
[INSTALL_SOURCE_HOST] = "host",
[INSTALL_SOURCE_AUTO] = "auto",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(install_source, InstallSource);
int acquire_esp( int acquire_esp(
int unprivileged_mode, int unprivileged_mode,
bool graceful, bool graceful,
@ -340,6 +344,8 @@ static int help(int argc, char *argv[], void *userdata) {
" Install all supported EFI architectures\n" " Install all supported EFI architectures\n"
" --efi-boot-option-description=DESCRIPTION\n" " --efi-boot-option-description=DESCRIPTION\n"
" Description of the entry in the boot option list\n" " Description of the entry in the boot option list\n"
" --efi-boot-option-description-with-device=yes\n"
" Suffix description with disk vendor/model/serial\n"
" --dry-run Dry run (unlink and cleanup)\n" " --dry-run Dry run (unlink and cleanup)\n"
" --secure-boot-auto-enroll=yes|no\n" " --secure-boot-auto-enroll=yes|no\n"
" Set up secure boot auto-enrollment\n" " Set up secure boot auto-enrollment\n"
@ -389,6 +395,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_JSON, ARG_JSON,
ARG_ARCH_ALL, ARG_ARCH_ALL,
ARG_EFI_BOOT_OPTION_DESCRIPTION, ARG_EFI_BOOT_OPTION_DESCRIPTION,
ARG_EFI_BOOT_OPTION_DESCRIPTION_WITH_DEVICE,
ARG_DRY_RUN, ARG_DRY_RUN,
ARG_PRINT_LOADER_PATH, ARG_PRINT_LOADER_PATH,
ARG_PRINT_STUB_PATH, ARG_PRINT_STUB_PATH,
@ -427,6 +434,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "json", required_argument, NULL, ARG_JSON }, { "json", required_argument, NULL, ARG_JSON },
{ "all-architectures", no_argument, NULL, ARG_ARCH_ALL }, { "all-architectures", no_argument, NULL, ARG_ARCH_ALL },
{ "efi-boot-option-description", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION }, { "efi-boot-option-description", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION },
{ "efi-boot-option-description-with-device", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION_WITH_DEVICE },
{ "dry-run", no_argument, NULL, ARG_DRY_RUN }, { "dry-run", no_argument, NULL, ARG_DRY_RUN },
{ "secure-boot-auto-enroll", required_argument, NULL, ARG_SECURE_BOOT_AUTO_ENROLL }, { "secure-boot-auto-enroll", required_argument, NULL, ARG_SECURE_BOOT_AUTO_ENROLL },
{ "certificate", required_argument, NULL, ARG_CERTIFICATE }, { "certificate", required_argument, NULL, ARG_CERTIFICATE },
@ -481,18 +489,14 @@ static int parse_argv(int argc, char *argv[]) {
return r; return r;
break; break;
case ARG_INSTALL_SOURCE: case ARG_INSTALL_SOURCE: {
if (streq(optarg, "auto")) InstallSource is = install_source_from_string(optarg);
arg_install_source = INSTALL_SOURCE_AUTO; if (is < 0)
else if (streq(optarg, "image")) return log_error_errno(is, "Unexpected parameter for --install-source=: %s", optarg);
arg_install_source = INSTALL_SOURCE_IMAGE;
else if (streq(optarg, "host"))
arg_install_source = INSTALL_SOURCE_HOST;
else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Unexpected parameter for --install-source=: %s", optarg);
arg_install_source = is;
break; break;
}
case 'p': case 'p':
arg_print_esp_path = true; arg_print_esp_path = true;
@ -572,9 +576,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_EFI_BOOT_OPTION_DESCRIPTION: case ARG_EFI_BOOT_OPTION_DESCRIPTION:
if (isempty(optarg) || !(string_is_safe(optarg) && utf8_is_valid(optarg))) { if (isempty(optarg) || !(string_is_safe(optarg) && utf8_is_valid(optarg))) {
_cleanup_free_ char *escaped = NULL; _cleanup_free_ char *escaped = cescape(optarg);
escaped = cescape(optarg);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid --efi-boot-option-description=: %s", strna(escaped)); "Invalid --efi-boot-option-description=: %s", strna(escaped));
} }
@ -587,6 +589,13 @@ static int parse_argv(int argc, char *argv[]) {
return r; return r;
break; break;
case ARG_EFI_BOOT_OPTION_DESCRIPTION_WITH_DEVICE:
r = parse_boolean_argument("--efi-boot-option-description-with-device=", optarg, &arg_efi_boot_option_description_with_device);
if (r < 0)
return r;
break;
case ARG_DRY_RUN: case ARG_DRY_RUN:
arg_dry_run = true; arg_dry_run = true;
break; break;
@ -711,7 +720,7 @@ static int vl_server(void) {
r = varlink_server_new( r = varlink_server_new(
&varlink_server, &varlink_server,
SD_VARLINK_SERVER_ROOT_ONLY, SD_VARLINK_SERVER_ROOT_ONLY|SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT,
/* userdata= */ NULL); /* userdata= */ NULL);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to allocate Varlink server: %m"); return log_error_errno(r, "Failed to allocate Varlink server: %m");
@ -724,7 +733,8 @@ static int vl_server(void) {
varlink_server, varlink_server,
"io.systemd.BootControl.ListBootEntries", vl_method_list_boot_entries, "io.systemd.BootControl.ListBootEntries", vl_method_list_boot_entries,
"io.systemd.BootControl.SetRebootToFirmware", vl_method_set_reboot_to_firmware, "io.systemd.BootControl.SetRebootToFirmware", vl_method_set_reboot_to_firmware,
"io.systemd.BootControl.GetRebootToFirmware", vl_method_get_reboot_to_firmware); "io.systemd.BootControl.GetRebootToFirmware", vl_method_get_reboot_to_firmware,
"io.systemd.BootControl.Install", vl_method_install);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to bind Varlink methods: %m"); return log_error_errno(r, "Failed to bind Varlink methods: %m");

View File

@ -37,6 +37,7 @@ extern char *arg_root;
extern char *arg_image; extern char *arg_image;
extern InstallSource arg_install_source; extern InstallSource arg_install_source;
extern char *arg_efi_boot_option_description; extern char *arg_efi_boot_option_description;
extern bool arg_efi_boot_option_description_with_device;
extern bool arg_dry_run; extern bool arg_dry_run;
extern ImagePolicy *arg_image_policy; extern ImagePolicy *arg_image_policy;
extern bool arg_varlink; extern bool arg_varlink;
@ -59,3 +60,9 @@ int acquire_esp(int unprivileged_mode, bool graceful, uint32_t *ret_part, uint64
int acquire_xbootldr(int unprivileged_mode, sd_id128_t *ret_uuid, dev_t *ret_devid); int acquire_xbootldr(int unprivileged_mode, sd_id128_t *ret_uuid, dev_t *ret_devid);
bool touch_variables(void); bool touch_variables(void);
/* EFI_BOOT_OPTION_DESCRIPTION_MAX sets the maximum length for the boot option description
* stored in NVRAM. The UEFI spec does not specify a minimum or maximum length for this
* string, but we limit the length to something reasonable to prevent from the firmware
* having to deal with a potentially too long string. */
#define EFI_BOOT_OPTION_DESCRIPTION_MAX ((size_t) 255)

View File

@ -328,6 +328,52 @@ void manager_process_native_message(
} while (r == 0); } while (r == 0);
} }
static size_t entry_size_max_by_ucred(Manager *m, const struct ucred *ucred, const char *label) {
static uint64_t entry_size_max = UINT64_MAX;
static bool entry_size_max_checked = false;
int r;
if (entry_size_max != UINT64_MAX)
return entry_size_max;
if (!entry_size_max_checked) {
const char *p;
entry_size_max_checked = true;
p = secure_getenv("SYSTEMD_JOURNAL_FD_SIZE_MAX");
if (p) {
r = parse_size(p, 1024, &entry_size_max);
if (r < 0)
log_warning_errno(r, "Failed to parse $SYSTEMD_JOURNAL_FD_SIZE_MAX, ignoring: %m");
else
return entry_size_max;
}
}
/* Check for unprivileged senders, as the default limit of 768M is quite high and the socket is
* unprivileged, to avoid abuses. */
if (!ucred)
return ENTRY_SIZE_UNPRIV_MAX;
if (ucred->uid == 0) /* Shortcut for root senders */
return ENTRY_SIZE_MAX;
/* As an exception, allow coredumps to use the old max size for backward compatibility */
if (pid_is_valid(ucred->pid)) {
ClientContext *context = NULL;
r = client_context_get(m, ucred->pid, ucred, label, /* unit_id= */ NULL, &context);
if (r < 0)
log_ratelimit_warning_errno(r, JOURNAL_LOG_RATELIMIT,
"Failed to retrieve credentials for PID " PID_FMT ", ignoring: %m",
ucred->pid);
else if (context->unit && startswith(context->unit, "systemd-coredump@"))
return ENTRY_SIZE_MAX;
}
return ENTRY_SIZE_UNPRIV_MAX;
}
int manager_process_native_file( int manager_process_native_file(
Manager *m, Manager *m,
int fd, int fd,
@ -392,7 +438,7 @@ int manager_process_native_file(
/* When !sealed, set a lower memory limit. We have to read the file, effectively doubling memory /* When !sealed, set a lower memory limit. We have to read the file, effectively doubling memory
* use. */ * use. */
if (st.st_size > ENTRY_SIZE_MAX / (sealed ? 1 : 2)) if ((size_t) st.st_size > entry_size_max_by_ucred(m, ucred, label) / (sealed ? 1 : 2))
return log_ratelimit_error_errno(SYNTHETIC_ERRNO(EFBIG), JOURNAL_LOG_RATELIMIT, return log_ratelimit_error_errno(SYNTHETIC_ERRNO(EFBIG), JOURNAL_LOG_RATELIMIT,
"File passed too large (%"PRIu64" bytes), refusing.", "File passed too large (%"PRIu64" bytes), refusing.",
(uint64_t) st.st_size); (uint64_t) st.st_size);

View File

@ -51,12 +51,16 @@ static char *arg_root = NULL;
static char *arg_image = NULL; static char *arg_image = NULL;
static ImagePolicy *arg_image_policy = NULL; static ImagePolicy *arg_image_policy = NULL;
static bool arg_legend = true; static bool arg_legend = true;
static BootEntryTokenType arg_entry_token_type = BOOT_ENTRY_TOKEN_AUTO;
static char *arg_entry_token = NULL;
static BootEntryType arg_boot_entry_type = _BOOT_ENTRY_TYPE_INVALID;
STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep); STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep); STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep);
typedef enum Action { typedef enum Action {
ACTION_ADD, ACTION_ADD,
@ -109,7 +113,15 @@ typedef struct Context {
char **envp; char **envp;
} Context; } Context;
#define CONTEXT_NULL (Context) { .rfd = -EBADF } #define CONTEXT_NULL \
(Context) { \
.rfd = XAT_FDROOT, \
.action = _ACTION_INVALID, \
.kernel_image_type = _KERNEL_IMAGE_TYPE_INVALID, \
.layout = _LAYOUT_INVALID, \
.entry_type = _BOOT_ENTRY_TYPE_INVALID, \
.entry_token_type = _BOOT_ENTRY_TOKEN_TYPE_INVALID, \
}
static void context_done(Context *c) { static void context_done(Context *c) {
assert(c); assert(c);
@ -429,20 +441,6 @@ static int context_set_initrds(Context *c, char* const* strv) {
return context_set_path_strv(c, strv, "command line", "initrds", &c->initrds); return context_set_path_strv(c, strv, "command line", "initrds", &c->initrds);
} }
static int context_set_entry_type(Context *c, const char *s) {
assert(c);
BootEntryType e;
if (isempty(s) || streq(s, "all")) {
c->entry_type = _BOOT_ENTRY_TYPE_INVALID;
return 0;
}
e = boot_entry_type_from_string(s);
if (e < 0)
return log_error_errno(e, "Invalid entry type: %s", s);
c->entry_type = e;
return 1;
}
static int context_load_environment(Context *c) { static int context_load_environment(Context *c) {
assert(c); assert(c);
@ -695,11 +693,16 @@ static int context_load_plugins(Context *c) {
return 0; return 0;
} }
static int context_init(Context *c) { static int context_setup(Context *c) {
int r; int r;
assert(c); assert(c);
if (c->kernel_image_type < 0)
c->kernel_image_type = KERNEL_IMAGE_TYPE_UNKNOWN;
if (c->entry_token_type < 0)
c->entry_token_type = BOOT_ENTRY_TOKEN_AUTO;
r = context_open_root(c); r = context_open_root(c);
if (r < 0) if (r < 0)
return r; return r;
@ -735,6 +738,24 @@ static int context_init(Context *c) {
return 0; return 0;
} }
static int context_from_cmdline(Context *c, Action action) {
int r;
assert(c);
c->action = action;
c->entry_type = arg_boot_entry_type;
r = free_and_strdup_warn(&c->entry_token, arg_entry_token);
if (r < 0)
return r;
c->entry_token_type = arg_entry_token_type;
return context_setup(c);
}
static int context_inspect_kernel(Context *c) { static int context_inspect_kernel(Context *c) {
assert(c); assert(c);
@ -768,9 +789,18 @@ static int context_ensure_layout(Context *c) {
if (!srel_path) if (!srel_path)
return log_oom(); return log_oom();
_cleanup_fclose_ FILE *f = NULL;
r = chase_and_fopenat_unlocked(c->rfd, srel_path, CHASE_AT_RESOLVE_IN_ROOT|CHASE_MUST_BE_REGULAR, "re", /* ret_path= */ NULL, &f);
if (r < 0) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to open '%s': %m", srel_path);
} else {
_cleanup_free_ char *srel = NULL; _cleanup_free_ char *srel = NULL;
r = read_one_line_file_at(c->rfd, srel_path, &srel);
if (r >= 0) { r = read_line(f, LONG_LINE_MAX, &srel);
if (r < 0)
return log_error_errno(r, "Failed to read %s: %m", srel_path);
if (streq(srel, "type1")) if (streq(srel, "type1"))
/* The loader/entries.srel file clearly indicates that the installed boot loader /* The loader/entries.srel file clearly indicates that the installed boot loader
* implements the proper standard upstream boot loader spec for Type #1 entries. * implements the proper standard upstream boot loader spec for Type #1 entries.
@ -784,17 +814,17 @@ static int context_ensure_layout(Context *c) {
log_debug("%s with '%s' found, using layout=%s.", srel_path, srel, layout_to_string(c->layout)); log_debug("%s with '%s' found, using layout=%s.", srel_path, srel, layout_to_string(c->layout));
return 0; return 0;
} else if (r != -ENOENT) }
return log_error_errno(r, "Failed to read %s: %m", srel_path);
_cleanup_free_ char *entry_token_path = path_join(c->boot_root, c->entry_token); _cleanup_free_ char *entry_token_path = path_join(c->boot_root, c->entry_token);
if (!entry_token_path) if (!entry_token_path)
return log_oom(); return log_oom();
r = is_dir_at(c->rfd, entry_token_path, /* follow= */ false); r = chaseat(c->rfd, entry_token_path, CHASE_AT_RESOLVE_IN_ROOT|CHASE_MUST_BE_DIRECTORY, /* ret_path= */ NULL, /* ret_fd= */ NULL);
if (r < 0 && r != -ENOENT) if (r < 0) {
return log_error_errno(r, "Failed to check if '%s' is a directory: %m", entry_token_path); if (!IN_SET(r, -ENOENT, -ENOTDIR))
if (r > 0) { return log_error_errno(r, "Failed to check if '%s' exists and is a directory: %m", entry_token_path);
} else {
/* If the metadata in $BOOT_ROOT doesn't tell us anything, then check if the entry token /* If the metadata in $BOOT_ROOT doesn't tell us anything, then check if the entry token
* directory already exists. If so, let's assume it's the standard boot loader spec, too. */ * directory already exists. If so, let's assume it's the standard boot loader spec, too. */
c->layout = LAYOUT_BLS; c->layout = LAYOUT_BLS;
@ -1091,6 +1121,26 @@ static bool bypass(void) {
return should_bypass("KERNEL_INSTALL"); return should_bypass("KERNEL_INSTALL");
} }
static int kernel_from_version(const char *version, char **ret_kernel) {
_cleanup_free_ char *vmlinuz = NULL;
int r;
assert(version);
vmlinuz = path_join("/usr/lib/modules/", version, "/vmlinuz");
if (!vmlinuz)
return log_oom();
r = access_nofollow(vmlinuz, F_OK);
if (r == -ENOENT)
return log_error_errno(r, "Kernel image not installed to '%s', specify kernel kernel image path explicitly.", vmlinuz);
if (r < 0)
return log_error_errno(r, "Failed to determine if kernel image is installed to '%s': %m", vmlinuz);
*ret_kernel = TAKE_PTR(vmlinuz);
return 0;
}
static int do_add( static int do_add(
Context *c, Context *c,
const char *version, const char *version,
@ -1100,8 +1150,21 @@ static int do_add(
int r; int r;
assert(c); assert(c);
assert(version);
assert(kernel); struct utsname un;
if (!version) {
assert_se(uname(&un) >= 0);
version = un.release;
}
_cleanup_free_ char *vmlinuz = NULL;
if (!kernel) {
r = kernel_from_version(version, &vmlinuz);
if (r < 0)
return r;
kernel = vmlinuz;
}
r = context_set_version(c, version); r = context_set_version(c, version);
if (r < 0) if (r < 0)
@ -1122,32 +1185,9 @@ static int do_add(
return context_execute(c); return context_execute(c);
} }
static int kernel_from_version(const char *version, char **ret_kernel) {
_cleanup_free_ char *vmlinuz = NULL;
int r;
assert(version);
vmlinuz = path_join("/usr/lib/modules/", version, "/vmlinuz");
if (!vmlinuz)
return log_oom();
r = access_nofollow(vmlinuz, F_OK);
if (r == -ENOENT)
return log_error_errno(r, "Kernel image not installed to '%s', requiring manual kernel image path specification.", vmlinuz);
if (r < 0)
return log_error_errno(r, "Failed to determine if kernel image is installed to '%s': %m", vmlinuz);
*ret_kernel = TAKE_PTR(vmlinuz);
return 0;
}
static int verb_add(int argc, char *argv[], void *userdata) { static int verb_add(int argc, char *argv[], void *userdata) {
Context *c = ASSERT_PTR(userdata);
_cleanup_free_ char *vmlinuz = NULL;
const char *version, *kernel; const char *version, *kernel;
char **initrds; char **initrds;
struct utsname un;
int r; int r;
assert(argv); assert(argv);
@ -1158,7 +1198,10 @@ static int verb_add(int argc, char *argv[], void *userdata) {
if (bypass()) if (bypass())
return 0; return 0;
c->action = ACTION_ADD; _cleanup_(context_done) Context c = CONTEXT_NULL;
r = context_from_cmdline(&c, ACTION_ADD);
if (r < 0)
return r;
/* We use the same order of arguments that "inspect" introduced, i.e. if only on argument is /* We use the same order of arguments that "inspect" introduced, i.e. if only on argument is
* specified we take it as the kernel path, not the version, i.e. it's the first argument that is * specified we take it as the kernel path, not the version, i.e. it's the first argument that is
@ -1168,24 +1211,10 @@ static int verb_add(int argc, char *argv[], void *userdata) {
(argc > 1 ? empty_or_dash_to_null(argv[1]) : NULL); (argc > 1 ? empty_or_dash_to_null(argv[1]) : NULL);
initrds = strv_skip(argv, 3); initrds = strv_skip(argv, 3);
if (!version) { return do_add(&c, version, kernel, initrds);
assert_se(uname(&un) >= 0);
version = un.release;
}
if (!kernel) {
r = kernel_from_version(version, &vmlinuz);
if (r < 0)
return r;
kernel = vmlinuz;
}
return do_add(c, version, kernel, initrds);
} }
static int verb_add_all(int argc, char *argv[], void *userdata) { static int verb_add_all(int argc, char *argv[], void *userdata) {
Context *c = ASSERT_PTR(userdata);
_cleanup_close_ int fd = -EBADF; _cleanup_close_ int fd = -EBADF;
size_t n = 0; size_t n = 0;
int ret = 0, r; int ret = 0, r;
@ -1198,9 +1227,12 @@ static int verb_add_all(int argc, char *argv[], void *userdata) {
if (bypass()) if (bypass())
return 0; return 0;
c->action = ACTION_ADD; _cleanup_(context_done) Context c = CONTEXT_NULL;
r = context_from_cmdline(&c, ACTION_ADD);
if (r < 0)
return r;
fd = chase_and_openat(c->rfd, "/usr/lib/modules", CHASE_AT_RESOLVE_IN_ROOT, O_DIRECTORY|O_RDONLY|O_CLOEXEC, NULL); fd = chase_and_openat(c.rfd, "/usr/lib/modules", CHASE_AT_RESOLVE_IN_ROOT, O_DIRECTORY|O_RDONLY|O_CLOEXEC, NULL);
if (fd < 0) if (fd < 0)
return log_error_errno(fd, "Failed to open %s/usr/lib/modules/: %m", strempty(arg_root)); return log_error_errno(fd, "Failed to open %s/usr/lib/modules/: %m", strempty(arg_root));
@ -1234,7 +1266,7 @@ static int verb_add_all(int argc, char *argv[], void *userdata) {
_cleanup_(context_done) Context copy = CONTEXT_NULL; _cleanup_(context_done) Context copy = CONTEXT_NULL;
r = context_copy(c, &copy); r = context_copy(&c, &copy);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to copy execution context: %m"); return log_error_errno(r, "Failed to copy execution context: %m");
@ -1262,18 +1294,17 @@ static int verb_add_all(int argc, char *argv[], void *userdata) {
return ret; return ret;
} }
static int run_as_installkernel(int argc, char *argv[], Context *c) { static int run_as_installkernel(int argc, char *argv[]) {
/* kernel's install.sh invokes us as /* kernel's install.sh invokes us as
* /sbin/installkernel <version> <vmlinuz> <map> <installation-dir> * /sbin/installkernel <version> <vmlinuz> <map> <installation-dir>
* We ignore the last two arguments. */ * We ignore the last two arguments. */
if (optind + 2 > argc) if (optind + 2 > argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "'installkernel' command requires at least two arguments."); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "'installkernel' command requires at least two arguments.");
return verb_add(3, STRV_MAKE("add", argv[optind], argv[optind+1]), c); return verb_add(3, STRV_MAKE("add", argv[optind], argv[optind+1]), /* userdata= */ NULL);
} }
static int verb_remove(int argc, char *argv[], void *userdata) { static int verb_remove(int argc, char *argv[], void *userdata) {
Context *c = ASSERT_PTR(userdata);
int r; int r;
assert(argc >= 2); assert(argc >= 2);
@ -1289,25 +1320,27 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
if (bypass()) if (bypass())
return 0; return 0;
c->action = ACTION_REMOVE; _cleanup_(context_done) Context c = CONTEXT_NULL;
r = context_from_cmdline(&c, ACTION_REMOVE);
if (r < 0)
return r;
/* Note, we do not automatically derive the kernel version to remove from uname() here (unlike we do /* Note, we do not automatically derive the kernel version to remove from uname() here (unlike we do
* it for the "add" verb), since we don't want to make it too easy to uninstall your running * it for the "add" verb), since we don't want to make it too easy to uninstall your running
* kernel, as a safety precaution */ * kernel, as a safety precaution */
r = context_set_version(c, argv[1]); r = context_set_version(&c, argv[1]);
if (r < 0) if (r < 0)
return r; return r;
r = context_prepare_execution(c); r = context_prepare_execution(&c);
if (r < 0) if (r < 0)
return r; return r;
return context_execute(c); return context_execute(&c);
} }
static int verb_inspect(int argc, char *argv[], void *userdata) { static int verb_inspect(int argc, char *argv[], void *userdata) {
Context *c = ASSERT_PTR(userdata);
_cleanup_(table_unrefp) Table *t = NULL; _cleanup_(table_unrefp) Table *t = NULL;
_cleanup_free_ char *vmlinuz = NULL; _cleanup_free_ char *vmlinuz = NULL;
const char *version, *kernel; const char *version, *kernel;
@ -1315,7 +1348,10 @@ static int verb_inspect(int argc, char *argv[], void *userdata) {
struct utsname un; struct utsname un;
int r; int r;
c->action = ACTION_INSPECT; _cleanup_(context_done) Context c = CONTEXT_NULL;
r = context_from_cmdline(&c, ACTION_INSPECT);
if (r < 0)
return r;
/* When only a single parameter is specified 'inspect' it's the kernel image path, and not the kernel /* When only a single parameter is specified 'inspect' it's the kernel image path, and not the kernel
* version. i.e. it's the first argument that is optional, not the 2nd. That's a bit unfortunate, but * version. i.e. it's the first argument that is optional, not the 2nd. That's a bit unfortunate, but
@ -1340,19 +1376,19 @@ static int verb_inspect(int argc, char *argv[], void *userdata) {
kernel = vmlinuz; kernel = vmlinuz;
} }
r = context_set_version(c, version); r = context_set_version(&c, version);
if (r < 0) if (r < 0)
return r; return r;
r = context_set_kernel(c, kernel); r = context_set_kernel(&c, kernel);
if (r < 0) if (r < 0)
return r; return r;
r = context_set_initrds(c, initrds); r = context_set_initrds(&c, initrds);
if (r < 0) if (r < 0)
return r; return r;
r = context_prepare_execution(c); r = context_prepare_execution(&c);
if (r < 0) if (r < 0)
return r; return r;
@ -1362,40 +1398,40 @@ static int verb_inspect(int argc, char *argv[], void *userdata) {
r = table_add_many(t, r = table_add_many(t,
TABLE_FIELD, "Machine ID", TABLE_FIELD, "Machine ID",
TABLE_ID128, c->machine_id, TABLE_ID128, c.machine_id,
TABLE_FIELD, "Kernel Image Type", TABLE_FIELD, "Kernel Image Type",
TABLE_STRING, kernel_image_type_to_string(c->kernel_image_type), TABLE_STRING, kernel_image_type_to_string(c.kernel_image_type),
TABLE_FIELD, "Layout", TABLE_FIELD, "Layout",
TABLE_STRING, context_get_layout(c), TABLE_STRING, context_get_layout(&c),
TABLE_FIELD, "Boot Root", TABLE_FIELD, "Boot Root",
TABLE_STRING, c->boot_root, TABLE_STRING, c.boot_root,
TABLE_FIELD, "Entry Token Type", TABLE_FIELD, "Entry Token Type",
TABLE_STRING, boot_entry_token_type_to_string(c->entry_token_type), TABLE_STRING, boot_entry_token_type_to_string(c.entry_token_type),
TABLE_FIELD, "Entry Token", TABLE_FIELD, "Entry Token",
TABLE_STRING, c->entry_token, TABLE_STRING, c.entry_token,
TABLE_FIELD, "Entry Directory", TABLE_FIELD, "Entry Directory",
TABLE_STRING, c->entry_dir, TABLE_STRING, c.entry_dir,
TABLE_FIELD, "Kernel Version", TABLE_FIELD, "Kernel Version",
TABLE_VERSION, c->version, TABLE_VERSION, c.version,
TABLE_FIELD, "Kernel", TABLE_FIELD, "Kernel",
TABLE_STRING, c->kernel, TABLE_STRING, c.kernel,
TABLE_FIELD, "Initrds", TABLE_FIELD, "Initrds",
TABLE_STRV, c->initrds, TABLE_STRV, c.initrds,
TABLE_FIELD, "Initrd Generator", TABLE_FIELD, "Initrd Generator",
TABLE_STRING, c->initrd_generator, TABLE_STRING, c.initrd_generator,
TABLE_FIELD, "UKI Generator", TABLE_FIELD, "UKI Generator",
TABLE_STRING, c->uki_generator, TABLE_STRING, c.uki_generator,
TABLE_FIELD, "Plugins", TABLE_FIELD, "Plugins",
TABLE_STRV, c->plugins, TABLE_STRV, c.plugins,
TABLE_FIELD, "Plugin Environment", TABLE_FIELD, "Plugin Environment",
TABLE_STRV, c->envp); TABLE_STRV, c.envp);
if (r < 0) if (r < 0)
return table_log_add_error(r); return table_log_add_error(r);
if (!sd_json_format_enabled(arg_json_format_flags)) { if (!sd_json_format_enabled(arg_json_format_flags)) {
r = table_add_many(t, r = table_add_many(t,
TABLE_FIELD, "Plugin Arguments", TABLE_FIELD, "Plugin Arguments",
TABLE_STRV, strv_skip(c->argv, 1)); TABLE_STRV, strv_skip(c.argv, 1));
if (r < 0) if (r < 0)
return table_log_add_error(r); return table_log_add_error(r);
} }
@ -1418,11 +1454,15 @@ static int verb_inspect(int argc, char *argv[], void *userdata) {
} }
static int verb_list(int argc, char *argv[], void *userdata) { static int verb_list(int argc, char *argv[], void *userdata) {
Context *c = ASSERT_PTR(userdata);
_cleanup_close_ int fd = -EBADF; _cleanup_close_ int fd = -EBADF;
int r; int r;
fd = chase_and_openat(c->rfd, "/usr/lib/modules", CHASE_AT_RESOLVE_IN_ROOT, O_DIRECTORY|O_RDONLY|O_CLOEXEC, NULL); _cleanup_(context_done) Context c = CONTEXT_NULL;
r = context_from_cmdline(&c, ACTION_INSPECT);
if (r < 0)
return r;
fd = chase_and_openat(c.rfd, "/usr/lib/modules", CHASE_AT_RESOLVE_IN_ROOT, O_DIRECTORY|O_RDONLY|O_CLOEXEC, NULL);
if (fd < 0) if (fd < 0)
return log_error_errno(fd, "Failed to open %s/usr/lib/modules/: %m", strempty(arg_root)); return log_error_errno(fd, "Failed to open %s/usr/lib/modules/: %m", strempty(arg_root));
@ -1532,7 +1572,7 @@ static int help(void) {
return 0; return 0;
} }
static int parse_argv(int argc, char *argv[], Context *c) { static int parse_argv(int argc, char *argv[]) {
enum { enum {
ARG_VERSION = 0x100, ARG_VERSION = 0x100,
ARG_NO_LEGEND, ARG_NO_LEGEND,
@ -1568,7 +1608,6 @@ static int parse_argv(int argc, char *argv[], Context *c) {
assert(argc >= 0); assert(argc >= 0);
assert(argv); assert(argv);
assert(c);
while ((t = getopt_long(argc, argv, "hv", options, NULL)) >= 0) while ((t = getopt_long(argc, argv, "hv", options, NULL)) >= 0)
switch (t) { switch (t) {
@ -1612,7 +1651,7 @@ static int parse_argv(int argc, char *argv[], Context *c) {
break; break;
case ARG_ENTRY_TOKEN: case ARG_ENTRY_TOKEN:
r = parse_boot_entry_token_type(optarg, &c->entry_token_type, &c->entry_token); r = parse_boot_entry_token_type(optarg, &arg_entry_token_type, &arg_entry_token);
if (r < 0) if (r < 0)
return r; return r;
break; break;
@ -1645,11 +1684,18 @@ static int parse_argv(int argc, char *argv[], Context *c) {
return r; return r;
break; break;
case ARG_BOOT_ENTRY_TYPE: case ARG_BOOT_ENTRY_TYPE: {
r = context_set_entry_type(c, optarg); if (isempty(optarg) || streq(optarg, "all")) {
if (r < 0) arg_boot_entry_type = _BOOT_ENTRY_TYPE_INVALID;
return r;
break; break;
}
BootEntryType e = boot_entry_type_from_string(optarg);
if (e < 0)
return log_error_errno(e, "Invalid entry type: %s", optarg);
arg_boot_entry_type = e;
break;
}
case '?': case '?':
return -EINVAL; return -EINVAL;
@ -1664,7 +1710,7 @@ static int parse_argv(int argc, char *argv[], Context *c) {
return 1; return 1;
} }
static int run(int argc, char* argv[]) { static int kernel_install_main(int argc, char *argv[]) {
static const Verb verbs[] = { static const Verb verbs[] = {
{ "add", 1, VERB_ANY, 0, verb_add }, { "add", 1, VERB_ANY, 0, verb_add },
{ "add-all", 1, 1, 0, verb_add_all }, { "add-all", 1, 1, 0, verb_add_all },
@ -1673,24 +1719,21 @@ static int run(int argc, char* argv[]) {
{ "list", 1, 1, 0, verb_list }, { "list", 1, 1, 0, verb_list },
{} {}
}; };
_cleanup_(context_done) Context c = {
.rfd = AT_FDCWD, return dispatch_verb(argc, argv, verbs, /* userdata= */ NULL);
.action = _ACTION_INVALID, }
.kernel_image_type = KERNEL_IMAGE_TYPE_UNKNOWN,
.layout = _LAYOUT_INVALID, static int run(int argc, char* argv[]) {
.entry_type = _BOOT_ENTRY_TYPE_INVALID,
.entry_token_type = BOOT_ENTRY_TOKEN_AUTO,
};
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(umount_and_freep) char *mounted_dir = NULL;
int r; int r;
log_setup(); log_setup();
r = parse_argv(argc, argv, &c); r = parse_argv(argc, argv);
if (r <= 0) if (r <= 0)
return r; return r;
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(umount_and_freep) char *mounted_dir = NULL;
if (arg_image) { if (arg_image) {
assert(!arg_root); assert(!arg_root);
@ -1713,14 +1756,10 @@ static int run(int argc, char* argv[]) {
return log_oom(); return log_oom();
} }
r = context_init(&c);
if (r < 0)
return r;
if (invoked_as(argv, "installkernel")) if (invoked_as(argv, "installkernel"))
return run_as_installkernel(argc, argv, &c); return run_as_installkernel(argc, argv);
return dispatch_verb(argc, argv, verbs, &c); return kernel_install_main(argc, argv);
} }
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run); DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);

View File

@ -387,6 +387,19 @@ _public_ int sd_journal_stream_fd_with_namespace(
assert_return(priority >= 0, -EINVAL); assert_return(priority >= 0, -EINVAL);
assert_return(priority <= 7, -EINVAL); assert_return(priority <= 7, -EINVAL);
if (name_space) {
/* If $LOG_NAMESPACE is set, we're already placed in a mountns with /run/systemd/journal/
* being a bind mount for the journald namespace instance, in which case we shall go by
* the standard journal socket path. */
const char *env = secure_getenv("LOG_NAMESPACE");
if (env) {
if (!streq(name_space, env))
return -EREMOTE;
name_space = NULL;
}
}
path = journal_stream_path(name_space); path = journal_stream_path(name_space);
if (!path) if (!path)
return -EINVAL; return -EINVAL;

View File

@ -801,11 +801,18 @@ static DirectoryOwnership validate_directory_fd(
* check if the directory is owned by the peer UID or by the foreign UID range (in the latter case * check if the directory is owned by the peer UID or by the foreign UID range (in the latter case
* one of the parent directories must be owned by the peer though). */ * one of the parent directories must be owned by the peer though). */
struct stat st; struct statx stx;
if (fstat(fd, &st) < 0) r = xstatx_full(fd,
return log_debug_errno(errno, "Failed to stat() directory fd: %m"); /* path= */ NULL,
AT_EMPTY_PATH,
/* mandatory_mask= */ STATX_TYPE|STATX_UID|STATX_MNT_ID|STATX_INO,
/* optional_mask= */ 0,
/* mandatory_attributes= */ STATX_ATTR_MOUNT_ROOT,
&stx);
if (r < 0)
return log_debug_errno(r, "Failed to statx() directory fd: %m");
r = stat_verify_directory(&st); r = statx_verify_directory(&stx);
if (r < 0) if (r < 0)
return r; return r;
@ -813,8 +820,8 @@ static DirectoryOwnership validate_directory_fd(
if (fl < 0) if (fl < 0)
return log_debug_errno(fl, "Directory file descriptor has unsafe flags set: %m"); return log_debug_errno(fl, "Directory file descriptor has unsafe flags set: %m");
if (st.st_uid == 0) { if (stx.stx_uid == 0) {
*ret_current_owner_uid = st.st_uid; *ret_current_owner_uid = stx.stx_uid;
if (peer_uid == 0) { if (peer_uid == 0) {
log_debug("Directory file descriptor points to root owned directory (%s), who is also the peer.", strna(path)); log_debug("Directory file descriptor points to root owned directory (%s), who is also the peer.", strna(path));
return DIRECTORY_IS_ROOT_PEER_OWNED; return DIRECTORY_IS_ROOT_PEER_OWNED;
@ -822,9 +829,9 @@ static DirectoryOwnership validate_directory_fd(
log_debug("Directory file descriptor points to root owned directory (%s).", strna(path)); log_debug("Directory file descriptor points to root owned directory (%s).", strna(path));
return DIRECTORY_IS_ROOT_OWNED; return DIRECTORY_IS_ROOT_OWNED;
} }
if (st.st_uid == peer_uid) { if (stx.stx_uid == peer_uid) {
log_debug("Directory file descriptor points to peer owned directory (%s).", strna(path)); log_debug("Directory file descriptor points to peer owned directory (%s).", strna(path));
*ret_current_owner_uid = st.st_uid; *ret_current_owner_uid = stx.stx_uid;
return DIRECTORY_IS_PEER_OWNED; return DIRECTORY_IS_PEER_OWNED;
} }
@ -835,17 +842,24 @@ static DirectoryOwnership validate_directory_fd(
_cleanup_close_ int parent_fd = -EBADF; _cleanup_close_ int parent_fd = -EBADF;
unsigned n_level; unsigned n_level;
for (n_level = 0; n_level < 16; n_level++) { for (n_level = 0; n_level < 16; n_level++) {
/* Do not go above bind mounts */
if (FLAGS_SET(stx.stx_attributes, STATX_ATTR_MOUNT_ROOT)) {
log_debug("Directory is a mount point, not checking for parent's ownership.");
*ret_current_owner_uid = stx.stx_uid;
return DIRECTORY_IS_OTHERWISE_OWNED;
}
/* Stop iteration if we find a directory up the tree that is neither owned by the user, nor is from the foreign UID range */ /* Stop iteration if we find a directory up the tree that is neither owned by the user, nor is from the foreign UID range */
if (!uid_is_foreign(st.st_uid) || !gid_is_foreign(st.st_gid)) { if (!uid_is_foreign(stx.stx_uid) || !gid_is_foreign(stx.stx_gid)) {
log_debug("Directory file descriptor points to directory which itself or its parents is neither owned by foreign UID range nor by the user."); log_debug("Directory file descriptor points to directory which itself or its parents is neither owned by foreign UID range nor by the user.");
*ret_current_owner_uid = st.st_uid; *ret_current_owner_uid = stx.stx_uid;
return DIRECTORY_IS_OTHERWISE_OWNED; return DIRECTORY_IS_OTHERWISE_OWNED;
} }
/* If the peer is root, then it doesn't matter if we find a parent owned by root, let's shortcut things. */ /* If the peer is root, then it doesn't matter if we find a parent owned by root, let's shortcut things. */
if (peer_uid == 0) { if (peer_uid == 0) {
log_debug("Directory referenced by file descriptor is owned by foreign UID range, and peer is root."); log_debug("Directory referenced by file descriptor is owned by foreign UID range, and peer is root.");
*ret_current_owner_uid = st.st_uid; *ret_current_owner_uid = stx.stx_uid;
return DIRECTORY_IS_FOREIGN_OWNED; return DIRECTORY_IS_FOREIGN_OWNED;
} }
@ -854,29 +868,46 @@ static DirectoryOwnership validate_directory_fd(
if (new_parent_fd < 0) if (new_parent_fd < 0)
return log_debug_errno(errno, "Failed to open parent directory of directory file descriptor: %m"); return log_debug_errno(errno, "Failed to open parent directory of directory file descriptor: %m");
struct stat new_st; struct statx new_stx;
if (fstat(new_parent_fd, &new_st) < 0) r = xstatx_full(new_parent_fd,
return log_debug_errno(errno, "Failed to stat parent directory of directory file descriptor: %m"); /* path= */ NULL,
AT_EMPTY_PATH,
/* mandatory_mask= */ STATX_UID|STATX_MNT_ID|STATX_INO,
/* optional_mask= */ 0,
/* mandatory_attributes= */ STATX_ATTR_MOUNT_ROOT,
&new_stx);
if (r < 0)
return log_debug_errno(r, "Failed to statx() parent directory of directory file descriptor: %m");
/* Safety check to see if we hit the root dir */ /* Safety check to see if we hit the root dir */
if (stat_inode_same(&st, &new_st)) { if (statx_inode_same(&stx, &new_stx)) {
log_debug("Directory file descriptor is owned by foreign UID range, but didn't find parent directory that is owned by peer among ancestors."); log_debug("Directory file descriptor is owned by foreign UID range, but didn't find parent directory that is owned by peer among ancestors.");
*ret_current_owner_uid = st.st_uid; *ret_current_owner_uid = stx.stx_uid;
return DIRECTORY_IS_OTHERWISE_OWNED; return DIRECTORY_IS_OTHERWISE_OWNED;
} }
if (new_st.st_uid == peer_uid) { /* Parent inode is owned by the peer. That's good! Everything's fine. */ if (stx.stx_mnt_id != new_stx.stx_mnt_id) {
/* NB, this check is probably redundant, given we also check
* STATX_ATTR_MOUNT_ROOT. The only reason we have it here is to provide extra safety
* in case the mount tree is rearranged concurrently with our traversal, so that
* STATX_ATTR_MOUNT_ROOT might be out of date. */
log_debug("Won't cross mount boundaries, not checking for parent's ownership.");
*ret_current_owner_uid = stx.stx_uid;
return DIRECTORY_IS_OTHERWISE_OWNED;
}
if (new_stx.stx_uid == peer_uid) { /* Parent inode is owned by the peer. That's good! Everything's fine. */
log_debug("Directory file descriptor is owned by foreign UID range, and ancestor is owned by peer."); log_debug("Directory file descriptor is owned by foreign UID range, and ancestor is owned by peer.");
*ret_current_owner_uid = st.st_uid; *ret_current_owner_uid = stx.stx_uid;
return DIRECTORY_IS_FOREIGN_OWNED; return DIRECTORY_IS_FOREIGN_OWNED;
} }
close_and_replace(parent_fd, new_parent_fd); close_and_replace(parent_fd, new_parent_fd);
st = new_st; stx = new_stx;
} }
log_debug("Failed to find peer owned parent directory after %u levels, refusing.", n_level); log_debug("Failed to find peer owned parent directory after %u levels, refusing.", n_level);
*ret_current_owner_uid = st.st_uid; *ret_current_owner_uid = stx.stx_uid;
return DIRECTORY_IS_OTHERWISE_OWNED; return DIRECTORY_IS_OTHERWISE_OWNED;
} }

View File

@ -12,22 +12,28 @@
#include "fd-util.h" #include "fd-util.h"
#include "log.h" #include "log.h"
#include "main-func.h" #include "main-func.h"
#include "parse-argument.h"
#include "path-lookup.h" #include "path-lookup.h"
#include "pretty-print.h" #include "pretty-print.h"
#include "runtime-scope.h" #include "runtime-scope.h"
#include "set.h"
#include "sort-util.h" #include "sort-util.h"
#include "string-util.h" #include "string-util.h"
#include "time-util.h" #include "time-util.h"
#include "varlink-util.h"
#define MAX_CONCURRENT_METRICS_SOCKETS 20 #define METRICS_MAX 1024U
#define METRICS_LINKS_MAX 128U
#define TIMEOUT_USEC (30 * USEC_PER_SEC) /* 30 seconds */ #define TIMEOUT_USEC (30 * USEC_PER_SEC) /* 30 seconds */
static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM; static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO;
typedef struct Context { typedef struct Context {
unsigned n_open_connections; sd_event *event;
Set *links;
sd_json_variant **metrics; /* Collected metrics for sorting */ sd_json_variant **metrics; /* Collected metrics for sorting */
size_t n_metrics; size_t n_metrics, n_skipped_metrics;
} Context; } Context;
static int metric_compare(sd_json_variant *const *a, sd_json_variant *const *b) { static int metric_compare(sd_json_variant *const *a, sd_json_variant *const *b) {
@ -80,6 +86,11 @@ static int metrics_on_query_reply(
else else
log_error("Varlink error: %s", error_id); log_error("Varlink error: %s", error_id);
} else { } else {
if (context->n_metrics >= METRICS_MAX) {
context->n_skipped_metrics++;
return 0;
}
/* Collect metrics for later sorting */ /* Collect metrics for later sorting */
if (!GREEDY_REALLOC(context->metrics, context->n_metrics + 1)) if (!GREEDY_REALLOC(context->metrics, context->n_metrics + 1))
return log_oom(); return log_oom();
@ -87,24 +98,22 @@ static int metrics_on_query_reply(
} }
if (!FLAGS_SET(flags, SD_VARLINK_REPLY_CONTINUES)) { if (!FLAGS_SET(flags, SD_VARLINK_REPLY_CONTINUES)) {
assert(context->n_open_connections > 0); assert_se(set_remove(context->links, link) == link);
context->n_open_connections--; link = sd_varlink_close_unref(link);
if (context->n_open_connections == 0) if (set_isempty(context->links))
(void) sd_event_exit(ASSERT_PTR(sd_varlink_get_event(link)), EXIT_SUCCESS); (void) sd_event_exit(context->event, EXIT_SUCCESS);
} }
return 0; return 0;
} }
static int metrics_call(const char *path, sd_event *event, sd_varlink **ret, Context *context) { static int metrics_call(Context *context, const char *path) {
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
int r; int r;
assert(path);
assert(event);
assert(ret);
assert(context); assert(context);
assert(path);
r = sd_varlink_connect_address(&vl, path); r = sd_varlink_connect_address(&vl, path);
if (r < 0) if (r < 0)
@ -116,7 +125,7 @@ static int metrics_call(const char *path, sd_event *event, sd_varlink **ret, Con
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to set varlink timeout: %m"); return log_error_errno(r, "Failed to set varlink timeout: %m");
r = sd_varlink_attach_event(vl, event, SD_EVENT_PRIORITY_NORMAL); r = sd_varlink_attach_event(vl, context->event, SD_EVENT_PRIORITY_NORMAL);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m"); return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
@ -128,42 +137,42 @@ static int metrics_call(const char *path, sd_event *event, sd_varlink **ret, Con
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to issue io.systemd.Metrics.List call: %m"); return log_error_errno(r, "Failed to issue io.systemd.Metrics.List call: %m");
*ret = TAKE_PTR(vl); if (set_ensure_put(&context->links, &varlink_hash_ops, vl) < 0)
return log_oom();
TAKE_PTR(vl);
return 0; return 0;
} }
static void sd_varlink_unref_many(sd_varlink **array, size_t n) {
assert(array);
FOREACH_ARRAY(v, array, n)
sd_varlink_unref(*v);
free(array);
}
static void context_done(Context *context) { static void context_done(Context *context) {
assert(context); assert(context);
for (size_t i = 0; i < context->n_metrics; i++) set_free(context->links);
sd_json_variant_unref(context->metrics[i]); sd_json_variant_unref_many(context->metrics, context->n_metrics);
free(context->metrics); sd_event_unref(context->event);
} }
static void metrics_output_sorted(Context *context) { static int metrics_output_sorted(Context *context) {
int r;
assert(context); assert(context);
typesafe_qsort(context->metrics, context->n_metrics, metric_compare); typesafe_qsort(context->metrics, context->n_metrics, metric_compare);
FOREACH_ARRAY(m, context->metrics, context->n_metrics) FOREACH_ARRAY(m, context->metrics, context->n_metrics) {
sd_json_variant_dump( r = sd_json_variant_dump(
*m, *m,
SD_JSON_FORMAT_PRETTY_AUTO | SD_JSON_FORMAT_COLOR_AUTO | SD_JSON_FORMAT_FLUSH, arg_json_format_flags | SD_JSON_FORMAT_FLUSH,
stdout, stdout,
NULL); /* prefix= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to write JSON: %m");
}
if (context->n_metrics == 0) if (context->n_metrics == 0)
log_warning("No reporting sockets found."); log_info("No metrics collected.");
return 0;
} }
static int metrics_query(void) { static int metrics_query(void) {
@ -176,58 +185,63 @@ static int metrics_query(void) {
log_debug("Looking for reports in %s/", metrics_path); log_debug("Looking for reports in %s/", metrics_path);
_cleanup_closedir_ DIR *d = opendir(metrics_path); _cleanup_(context_done) Context context = {};
if (!d)
return log_full_errno(errno == ENOENT ? LOG_WARNING : LOG_ERR, errno,
"Failed to open metrics directory %s: %m", metrics_path);
_cleanup_(sd_event_unrefp) sd_event *event = NULL; r = sd_event_default(&context.event);
r = sd_event_default(&event);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to get event loop: %m"); return log_error_errno(r, "Failed to get event loop: %m");
r = sd_event_set_signal_exit(event, true); r = sd_event_set_signal_exit(context.event, true);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to enable exit on SIGINT/SIGTERM: %m"); return log_error_errno(r, "Failed to enable exit on SIGINT/SIGTERM: %m");
size_t n_varlinks = MAX_CONCURRENT_METRICS_SOCKETS; size_t n_skipped_sources = 0;
sd_varlink **varlinks = new0(sd_varlink *, n_varlinks); _cleanup_closedir_ DIR *d = opendir(metrics_path);
if (!varlinks) if (!d) {
return log_oom(); if (errno != ENOENT)
return log_error_errno(errno, "Failed to open metrics directory %s: %m", metrics_path);
CLEANUP_ARRAY(varlinks, n_varlinks, sd_varlink_unref_many); } else {
_cleanup_(context_done) Context context = {};
FOREACH_DIRENT(de, d, FOREACH_DIRENT(de, d,
return log_warning_errno(errno, "Failed to read %s: %m", metrics_path)) { return log_warning_errno(errno, "Failed to read %s: %m", metrics_path)) {
if (!IN_SET(de->d_type, DT_SOCK, DT_UNKNOWN)) if (!IN_SET(de->d_type, DT_SOCK, DT_UNKNOWN))
continue; continue;
if (set_size(context.links) >= METRICS_LINKS_MAX) {
n_skipped_sources++;
break;
}
_cleanup_free_ char *p = path_join(metrics_path, de->d_name); _cleanup_free_ char *p = path_join(metrics_path, de->d_name);
if (!p) if (!p)
return log_oom(); return log_oom();
r = metrics_call(p, event, &varlinks[context.n_open_connections], &context); (void) metrics_call(&context, p);
if (r < 0)
continue;
if (++context.n_open_connections >= MAX_CONCURRENT_METRICS_SOCKETS) {
log_warning("Too many concurrent metrics sockets, stop iterating");
break;
} }
} }
if (context.n_open_connections > 0) { if (set_isempty(context.links))
r = sd_event_loop(event); log_info("No metrics sources found.");
else {
r = sd_event_loop(context.event);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m"); return log_error_errno(r, "Failed to run event loop: %m");
r = metrics_output_sorted(&context);
if (r < 0)
return r;
if (n_skipped_sources > 0)
log_warning("Too many metrics sources, only %u sources contacted, %zu sources skipped.", set_size(context.links), n_skipped_sources);
if (context.n_skipped_metrics > 0)
log_warning("Too many metrics, only %zu metrics collected, %zu metrics skipped.", context.n_metrics, context.n_skipped_metrics);
if (n_skipped_sources > 0 ||
context.n_skipped_metrics > 0)
return EXIT_FAILURE;
} }
metrics_output_sorted(&context); return EXIT_SUCCESS;
return 0;
} }
static int help(void) { static int help(void) {
@ -244,6 +258,8 @@ static int help(void) {
" --version Show package version\n" " --version Show package version\n"
" --user Connect to user service manager\n" " --user Connect to user service manager\n"
" --system Connect to system service manager (default)\n" " --system Connect to system service manager (default)\n"
" --json=pretty|short\n"
" Configure JSON output\n"
"\nSee the %s for details.\n", "\nSee the %s for details.\n",
program_invocation_short_name, program_invocation_short_name,
ansi_highlight(), ansi_highlight(),
@ -258,6 +274,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VERSION = 0x100, ARG_VERSION = 0x100,
ARG_USER, ARG_USER,
ARG_SYSTEM, ARG_SYSTEM,
ARG_JSON,
}; };
static const struct option options[] = { static const struct option options[] = {
@ -265,28 +282,41 @@ static int parse_argv(int argc, char *argv[]) {
{ "version", no_argument, NULL, ARG_VERSION }, { "version", no_argument, NULL, ARG_VERSION },
{ "user", no_argument, NULL, ARG_USER }, { "user", no_argument, NULL, ARG_USER },
{ "system", no_argument, NULL, ARG_SYSTEM }, { "system", no_argument, NULL, ARG_SYSTEM },
{ "json", required_argument, NULL, ARG_JSON },
{} {}
}; };
int c; int c, r;
assert(argc >= 0); assert(argc >= 0);
assert(argv); assert(argv);
while ((c = getopt_long(argc, argv, "hp", options, NULL)) >= 0) while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
switch (c) { switch (c) {
case 'h': case 'h':
return help(); return help();
case ARG_VERSION: case ARG_VERSION:
return version(); return version();
case ARG_USER: case ARG_USER:
arg_runtime_scope = RUNTIME_SCOPE_USER; arg_runtime_scope = RUNTIME_SCOPE_USER;
break; break;
case ARG_SYSTEM: case ARG_SYSTEM:
arg_runtime_scope = RUNTIME_SCOPE_SYSTEM; arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
break; break;
case ARG_JSON:
r = parse_json_argument(optarg, &arg_json_format_flags);
if (r <= 0)
return r;
break;
case '?': case '?':
return -EINVAL; return -EINVAL;
default: default:
assert_not_reached(); assert_not_reached();
} }

View File

@ -922,6 +922,7 @@ int blockdev_get_root(int level, dev_t *ret) {
} }
int partition_node_of(const char *node, unsigned nr, char **ret) { int partition_node_of(const char *node, unsigned nr, char **ret) {
_cleanup_free_ char *fn = NULL, *dn = NULL;
int r; int r;
assert(node); assert(node);
@ -931,18 +932,12 @@ int partition_node_of(const char *node, unsigned nr, char **ret) {
/* Given a device node path to a block device returns the device node path to the partition block /* Given a device node path to a block device returns the device node path to the partition block
* device of the specified partition */ * device of the specified partition */
_cleanup_free_ char *fn = NULL; r = path_split_prefix_filename(node, &dn, &fn);
r = path_extract_filename(node, &fn);
if (r < 0) if (r < 0)
return r; return r;
if (r == O_DIRECTORY) if (r == O_DIRECTORY)
return -EISDIR; return -EISDIR;
_cleanup_free_ char *dn = NULL;
r = path_extract_directory(node, &dn);
if (r < 0 && r != -EDESTADDRREQ) /* allow if only filename is specified */
return r;
size_t l = strlen(fn); size_t l = strlen(fn);
assert(l > 0); /* underflow check for the subtraction below */ assert(l > 0); /* underflow check for the subtraction below */

View File

@ -155,6 +155,8 @@ int boot_entry_token_ensure_at(
assert(type); assert(type);
assert(token); assert(token);
/* Returns -EUNATCH if the selected token is not set */
if (*token) if (*token)
return 0; /* Already set. */ return 0; /* Already set. */
@ -181,7 +183,7 @@ int boot_entry_token_ensure_at(
return r; return r;
} }
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EUNATCH),
"No machine ID set, and /etc/os-release carries no ID=/IMAGE_ID= fields."); "No machine ID set, and /etc/os-release carries no ID=/IMAGE_ID= fields.");
case BOOT_ENTRY_TOKEN_MACHINE_ID: case BOOT_ENTRY_TOKEN_MACHINE_ID:
@ -189,14 +191,14 @@ int boot_entry_token_ensure_at(
if (r != 0) if (r != 0)
return r; return r;
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No machine ID set."); return log_error_errno(SYNTHETIC_ERRNO(EUNATCH), "No machine ID set.");
case BOOT_ENTRY_TOKEN_OS_IMAGE_ID: case BOOT_ENTRY_TOKEN_OS_IMAGE_ID:
r = entry_token_from_os_release(rfd, type, token); r = entry_token_from_os_release(rfd, type, token);
if (r != 0) if (r != 0)
return r; return r;
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EUNATCH),
"IMAGE_ID= field not set in /etc/os-release."); "IMAGE_ID= field not set in /etc/os-release.");
case BOOT_ENTRY_TOKEN_OS_ID: case BOOT_ENTRY_TOKEN_OS_ID:
@ -204,12 +206,12 @@ int boot_entry_token_ensure_at(
if (r != 0) if (r != 0)
return r; return r;
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EUNATCH),
"ID= field not set in /etc/os-release."); "ID= field not set in /etc/os-release.");
case BOOT_ENTRY_TOKEN_LITERAL: case BOOT_ENTRY_TOKEN_LITERAL:
/* In this case, the token should be already set by the user input. */ /* In this case, the token should be already set by the user input. */
return -EINVAL; return log_error_errno(SYNTHETIC_ERRNO(EUNATCH), "Literal token indicated but not specified.");
default: default:
assert_not_reached(); assert_not_reached();
@ -291,4 +293,4 @@ static const char *const boot_entry_token_type_table[] = {
[BOOT_ENTRY_TOKEN_AUTO] = "auto", [BOOT_ENTRY_TOKEN_AUTO] = "auto",
}; };
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(boot_entry_token_type, BootEntryTokenType); DEFINE_STRING_TABLE_LOOKUP(boot_entry_token_type, BootEntryTokenType);

View File

@ -34,4 +34,4 @@ int boot_entry_token_ensure_at(
int parse_boot_entry_token_type(const char *s, BootEntryTokenType *type, char **token); int parse_boot_entry_token_type(const char *s, BootEntryTokenType *type, char **token);
DECLARE_STRING_TABLE_LOOKUP_TO_STRING(boot_entry_token_type, BootEntryTokenType); DECLARE_STRING_TABLE_LOOKUP(boot_entry_token_type, BootEntryTokenType);

View File

@ -480,14 +480,13 @@ int get_credential_host_secret(CredentialSecretFlags flags, struct iovec *ret) {
if (!path_is_absolute(e)) if (!path_is_absolute(e))
return -EINVAL; return -EINVAL;
r = path_extract_directory(e, &_dirname); r = path_split_prefix_filename(e, &_dirname, &_filename);
if (r < 0)
return r;
r = path_extract_filename(e, &_filename);
if (r < 0) if (r < 0)
return r; return r;
if (r == O_DIRECTORY)
return -EINVAL;
/* We validate that the path is absolute above, hence dirname must be extractable. */
dirname = _dirname; dirname = _dirname;
filename = _filename; filename = _filename;
} else { } else {
@ -498,7 +497,8 @@ int get_credential_host_secret(CredentialSecretFlags flags, struct iovec *ret) {
assert(dirname); assert(dirname);
assert(filename); assert(filename);
mkdir_parents(dirname, 0755); (void) mkdir_parents(dirname, 0755);
dfd = open_mkdir(dirname, O_CLOEXEC, 0755); dfd = open_mkdir(dirname, O_CLOEXEC, 0755);
if (dfd < 0) if (dfd < 0)
return log_debug_errno(dfd, "Failed to create or open directory '%s': %m", dirname); return log_debug_errno(dfd, "Failed to create or open directory '%s': %m", dirname);

View File

@ -109,13 +109,9 @@ int generator_add_symlink_full(
* *
* If <instance> is specified, then <src> must be a template unit name, and we'll instantiate it. */ * If <instance> is specified, then <src> must be a template unit name, and we'll instantiate it. */
r = path_extract_directory(src, &dn); r = path_split_prefix_filename(src, &dn, &fn);
if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → just a file name was passed */
return log_error_errno(r, "Failed to extract directory name from '%s': %m", src);
r = path_extract_filename(src, &fn);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to extract file name from '%s': %m", src); return log_error_errno(r, "Failed to split '%s' into directory prefix and filename: %m", src);
if (r == O_DIRECTORY) if (r == O_DIRECTORY)
return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Expected path to regular file name, but got '%s', refusing.", src); return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Expected path to regular file name, but got '%s', refusing.", src);

View File

@ -12,9 +12,11 @@
* See JOURNAL_SIZE_MAX in coredump.c */ * See JOURNAL_SIZE_MAX in coredump.c */
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
#define ENTRY_SIZE_MAX (1024*1024*770u) #define ENTRY_SIZE_MAX (1024*1024*770u)
#define ENTRY_SIZE_UNPRIV_MAX (1024*1024*32u)
#define DATA_SIZE_MAX (1024*1024*768u) #define DATA_SIZE_MAX (1024*1024*768u)
#else #else
#define ENTRY_SIZE_MAX (1024*1024*13u) #define ENTRY_SIZE_MAX (1024*1024*13u)
#define ENTRY_SIZE_UNPRIV_MAX (1024*1024*8u)
#define DATA_SIZE_MAX (1024*1024*11u) #define DATA_SIZE_MAX (1024*1024*11u)
#endif #endif
#define LINE_CHUNK 8*1024u #define LINE_CHUNK 8*1024u

View File

@ -12,6 +12,8 @@
#include "string-table.h" #include "string-table.h"
#include "string-util.h" #include "string-util.h"
/* Note: none of these function change the file position of the provided fd, as they use pread() */
bool pe_header_is_64bit(const PeHeader *h) { bool pe_header_is_64bit(const PeHeader *h) {
assert(h); assert(h);

View File

@ -7,6 +7,7 @@
#include "discover-image.h" #include "discover-image.h"
#include "log.h" #include "log.h"
#include "mountpoint-util.h" #include "mountpoint-util.h"
#include "runtime-scope.h"
#include "snapshot-util.h" #include "snapshot-util.h"
#include "signal-util.h" #include "signal-util.h"
#include "tmpfile-util.h" #include "tmpfile-util.h"

View File

@ -1,7 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once #pragma once
#include "runtime-scope.h" #include "shared-forward.h"
/* create_ephemeral_snapshot - create a snapshot of the given directory. /* create_ephemeral_snapshot - create a snapshot of the given directory.
* *

View File

@ -291,17 +291,15 @@ int unit_file_resolve_symlink(
dir, dir ? "/" : "", filename); dir, dir ? "/" : "", filename);
if (!dir) { if (!dir) {
r = path_extract_directory(filename, &_dir); r = path_split_prefix_filename(filename, &_dir, &_filename);
if (r < 0)
return r;
dir = _dir;
r = path_extract_filename(filename, &_filename);
if (r < 0) if (r < 0)
return r; return r;
if (r == O_DIRECTORY) if (r == O_DIRECTORY)
return log_warning_errno(SYNTHETIC_ERRNO(EISDIR), return log_warning_errno(SYNTHETIC_ERRNO(EISDIR),
"Unexpected path to a directory \"%s\", refusing.", filename); "Unexpected path to a directory \"%s\", refusing.", filename);
/* We validate that the path is absolute above, hence dir must be extractable. */
dir = ASSERT_PTR(_dir);
filename = _filename; filename = _filename;
} }

View File

@ -36,21 +36,37 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
SD_VARLINK_DEFINE_FIELD_BY_TYPE(source, BootEntrySource, 0), SD_VARLINK_DEFINE_FIELD_BY_TYPE(source, BootEntrySource, 0),
SD_VARLINK_FIELD_COMMENT("The string identifier of the entry"), SD_VARLINK_FIELD_COMMENT("The string identifier of the entry"),
SD_VARLINK_DEFINE_FIELD(id, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(id, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Path to the primary definition file for the entry"),
SD_VARLINK_DEFINE_FIELD(path, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(path, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Directory path of the file system root the entry was found on"),
SD_VARLINK_DEFINE_FIELD(root, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(root, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The entry's title string"),
SD_VARLINK_DEFINE_FIELD(title, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(title, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The possibly mangled/augmented title to show for the entry"),
SD_VARLINK_DEFINE_FIELD(showTitle, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(showTitle, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("An explicitly configured sorting key for the enry"),
SD_VARLINK_DEFINE_FIELD(sortKey, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(sortKey, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The version of the entry"),
SD_VARLINK_DEFINE_FIELD(version, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(version, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Machine ID of the OS installation belonging to the entry, if known"),
SD_VARLINK_DEFINE_FIELD(machineId, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(machineId, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("EFI architecture name for this entry"),
SD_VARLINK_DEFINE_FIELD(architecture, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(architecture, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Command line options to pass to the invoked kernel or EFI binary"),
SD_VARLINK_DEFINE_FIELD(options, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(options, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Path to the Linux kernel to invoke, relative to the root directory of the file system containing the entry file"),
SD_VARLINK_DEFINE_FIELD(linux, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(linux, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Path to the EFI binary to invoke, relative to the root directory of the file system containing the entry file"),
SD_VARLINK_DEFINE_FIELD(efi, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(efi, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Path to an UKI EFI binary to invoke, relative to the root directory of the file system containing the entry file"),
SD_VARLINK_DEFINE_FIELD(uki, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(uki, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("An UKI profile index to invoke. If not specified defaults to the first profile."),
SD_VARLINK_DEFINE_FIELD(profile, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(profile, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Path to the initrd image to pass to the invoked kernel, relative to the root directory of the file system containing the entry file"),
SD_VARLINK_DEFINE_FIELD(initrd, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY), SD_VARLINK_DEFINE_FIELD(initrd, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
SD_VARLINK_FIELD_COMMENT("Devicetree file to pass to the invoked kernel, relative to the root directory of the file system containing the entry file"),
SD_VARLINK_DEFINE_FIELD(devicetree, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_DEFINE_FIELD(devicetree, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Devicetree overlay file to pass to the invoked kernel, relative to the root directory of the file system containing the entry file"),
SD_VARLINK_DEFINE_FIELD(devicetreeOverlay, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY), SD_VARLINK_DEFINE_FIELD(devicetreeOverlay, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY),
SD_VARLINK_FIELD_COMMENT("Indicates whether the boot loader reported this entry on the current boot"), SD_VARLINK_FIELD_COMMENT("Indicates whether the boot loader reported this entry on the current boot"),
SD_VARLINK_DEFINE_FIELD(isReported, SD_VARLINK_BOOL, 0), SD_VARLINK_DEFINE_FIELD(isReported, SD_VARLINK_BOOL, 0),
@ -83,12 +99,53 @@ static SD_VARLINK_DEFINE_METHOD(
SD_VARLINK_FIELD_COMMENT("The current state of the reboot-to-firmware-UI flag"), SD_VARLINK_FIELD_COMMENT("The current state of the reboot-to-firmware-UI flag"),
SD_VARLINK_DEFINE_OUTPUT(state, SD_VARLINK_BOOL, 0)); SD_VARLINK_DEFINE_OUTPUT(state, SD_VARLINK_BOOL, 0));
static SD_VARLINK_DEFINE_ENUM_TYPE(
Operation,
SD_VARLINK_FIELD_COMMENT("Install the boot loader afresh, creating everything it needs"),
SD_VARLINK_DEFINE_ENUM_VALUE(new),
SD_VARLINK_FIELD_COMMENT("Just update existing boot loader binaries"),
SD_VARLINK_DEFINE_ENUM_VALUE(update));
static SD_VARLINK_DEFINE_ENUM_TYPE(
BootEntryTokenType,
SD_VARLINK_FIELD_COMMENT("Pick identifiers for type #1 boot entries based on /etc/machine-id"),
SD_VARLINK_DEFINE_ENUM_VALUE(machine_id),
SD_VARLINK_FIELD_COMMENT("Pick identifiers for type #1 boot entries based on the IMAGE_ID= field from /etc/os-release"),
SD_VARLINK_DEFINE_ENUM_VALUE(os_image_id),
SD_VARLINK_FIELD_COMMENT("Pick identifiers for type #1 boot entries based on the ID= field from /etc/os-release"),
SD_VARLINK_DEFINE_ENUM_VALUE(os_id),
SD_VARLINK_FIELD_COMMENT("Pick identifiers for type #1 boot entries based on a manually chosen string"),
SD_VARLINK_DEFINE_ENUM_VALUE(literal),
SD_VARLINK_FIELD_COMMENT("Choose automatically how to pick identifiers for type #1 boot entries"),
SD_VARLINK_DEFINE_ENUM_VALUE(auto));
static SD_VARLINK_DEFINE_METHOD(
Install,
SD_VARLINK_FIELD_COMMENT("Operation, either 'new' or 'update'"),
SD_VARLINK_DEFINE_INPUT_BY_TYPE(operation, Operation, 0),
SD_VARLINK_FIELD_COMMENT("If true, continue on various failures"),
SD_VARLINK_DEFINE_INPUT(graceful, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Index into array of file descriptors passed along with this message, pointing to file descriptor to root file system to operate on"),
SD_VARLINK_DEFINE_INPUT(rootFileDescriptor, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Root directory to operate relative to. If both this and rootFileDescriptor is specified, this is purely informational. If only this is specified, it is what will be used."),
SD_VARLINK_DEFINE_INPUT(rootDirectory, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Selects how to identify boot entries"),
SD_VARLINK_DEFINE_INPUT_BY_TYPE(bootEntryTokenType, BootEntryTokenType, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("If true the boot loader will be registered in an EFI boot entry via EFI variables, otherwise this is omitted"),
SD_VARLINK_DEFINE_INPUT(touchVariables, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_ERROR( static SD_VARLINK_DEFINE_ERROR(
RebootToFirmwareNotSupported); RebootToFirmwareNotSupported);
static SD_VARLINK_DEFINE_ERROR( static SD_VARLINK_DEFINE_ERROR(
NoSuchBootEntry); NoSuchBootEntry);
static SD_VARLINK_DEFINE_ERROR(
NoESPFound);
static SD_VARLINK_DEFINE_ERROR(
BootEntryTokenUnavailable);
SD_VARLINK_DEFINE_INTERFACE( SD_VARLINK_DEFINE_INTERFACE(
io_systemd_BootControl, io_systemd_BootControl,
"io.systemd.BootControl", "io.systemd.BootControl",
@ -101,13 +158,23 @@ SD_VARLINK_DEFINE_INTERFACE(
&vl_type_BootEntryAddon, &vl_type_BootEntryAddon,
SD_VARLINK_SYMBOL_COMMENT("A structure encapsulating a boot entry"), SD_VARLINK_SYMBOL_COMMENT("A structure encapsulating a boot entry"),
&vl_type_BootEntry, &vl_type_BootEntry,
SD_VARLINK_SYMBOL_COMMENT("The operation to execute"),
&vl_type_Operation,
SD_VARLINK_SYMBOL_COMMENT("Enumerates boot entries. Method call must be called with 'more' flag set. Each response returns one entry. If no entries are defined returns the NoSuchBootEntry error."), SD_VARLINK_SYMBOL_COMMENT("Enumerates boot entries. Method call must be called with 'more' flag set. Each response returns one entry. If no entries are defined returns the NoSuchBootEntry error."),
&vl_method_ListBootEntries, &vl_method_ListBootEntries,
SD_VARLINK_SYMBOL_COMMENT("Sets the reboot-to-firmware-UI flag of the firmware, if this concept exists. Returns the RebootToFirmwareNotSupported error if not."), SD_VARLINK_SYMBOL_COMMENT("Sets the reboot-to-firmware-UI flag of the firmware, if this concept exists. Returns the RebootToFirmwareNotSupported error if not."),
&vl_method_SetRebootToFirmware, &vl_method_SetRebootToFirmware,
SD_VARLINK_SYMBOL_COMMENT("Gets the current state of the reboot-to-firmware-UI flag of the firmware, if this concept exists. Returns the RebootToFirmwareNotSupported error if not."), SD_VARLINK_SYMBOL_COMMENT("Gets the current state of the reboot-to-firmware-UI flag of the firmware, if this concept exists. Returns the RebootToFirmwareNotSupported error if not."),
&vl_method_GetRebootToFirmware, &vl_method_GetRebootToFirmware,
SD_VARLINK_SYMBOL_COMMENT("The boot entry token type to use."),
&vl_type_BootEntryTokenType,
SD_VARLINK_SYMBOL_COMMENT("Install the boot loader on the ESP."),
&vl_method_Install,
SD_VARLINK_SYMBOL_COMMENT("SetRebootToFirmware() and GetRebootToFirmware() return this if the firmware does not actually support the reboot-to-firmware-UI concept."), SD_VARLINK_SYMBOL_COMMENT("SetRebootToFirmware() and GetRebootToFirmware() return this if the firmware does not actually support the reboot-to-firmware-UI concept."),
&vl_error_RebootToFirmwareNotSupported, &vl_error_RebootToFirmwareNotSupported,
SD_VARLINK_SYMBOL_COMMENT("No boot entry defined."), SD_VARLINK_SYMBOL_COMMENT("No boot entry defined."),
&vl_error_NoSuchBootEntry); &vl_error_NoSuchBootEntry,
SD_VARLINK_SYMBOL_COMMENT("No EFI System Partition (ESP) found."),
&vl_error_NoESPFound,
SD_VARLINK_SYMBOL_COMMENT("The select boot entry token could not be determined."),
&vl_error_BootEntryTokenUnavailable);

View File

@ -25,7 +25,7 @@ if conf.get('ENABLE_SSH_PROXY_CONFIG') == 1
install : true, install : true,
install_dir : sshconfdir.startswith('/usr/') ? sshconfdir : libexecdir / 'ssh_config.d') install_dir : sshconfdir.startswith('/usr/') ? sshconfdir : libexecdir / 'ssh_config.d')
if conf.get('LINK_SSH_PROXY_DROPIN') == 1 if (not sshconfdir.startswith(sysconfdir) or install_sysconfdir) and conf.get('LINK_SSH_PROXY_DROPIN') == 1
if meson.version().version_compare('>=1.3.0') if meson.version().version_compare('>=1.3.0')
install_symlink( install_symlink(
'20-systemd-ssh-proxy.conf', '20-systemd-ssh-proxy.conf',

View File

@ -26,7 +26,7 @@ int verb_preset_all(int argc, char *argv[], void *userdata) {
CLEANUP_ARRAY(changes, n_changes, install_changes_free); CLEANUP_ARRAY(changes, n_changes, install_changes_free);
r = unit_file_preset_all(arg_runtime_scope, unit_file_flags_from_args(), arg_root, arg_preset_mode, &changes, &n_changes); r = unit_file_preset_all(arg_runtime_scope, unit_file_flags_from_args(), arg_root, arg_preset_mode, &changes, &n_changes);
/* We do not treat propagate failure of individual units here. */ /* We do not propagate failure for individual units here. */
(void) install_changes_dump(r, "preset all", changes, n_changes, arg_quiet); (void) install_changes_dump(r, "preset all", changes, n_changes, arg_quiet);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -1027,7 +1027,7 @@ TEST(last_path_component) {
} }
static void test_path_extract_filename_one(const char *input, const char *output, int ret) { static void test_path_extract_filename_one(const char *input, const char *output, int ret) {
_cleanup_free_ char *k = NULL; _cleanup_free_ char *k = NULL, *k2 = NULL;
int r; int r;
r = path_extract_filename(input, &k); r = path_extract_filename(input, &k);
@ -1037,6 +1037,13 @@ static void test_path_extract_filename_one(const char *input, const char *output
strnull(output), ret < 0 ? STRERROR(ret) : "-"); strnull(output), ret < 0 ? STRERROR(ret) : "-");
ASSERT_STREQ(k, output); ASSERT_STREQ(k, output);
assert_se(r == ret); assert_se(r == ret);
/* Extra safety check: make sure that path_split_prefix_filename() behaves the same */
r = path_split_prefix_filename(input, NULL, &k2);
if (r >= 0) {
ASSERT_STREQ(k2, k);
assert_se(r == ret);
}
} }
TEST(path_extract_filename) { TEST(path_extract_filename) {
@ -1071,7 +1078,7 @@ TEST(path_extract_filename) {
} }
static void test_path_extract_directory_one(const char *input, const char *output, int ret) { static void test_path_extract_directory_one(const char *input, const char *output, int ret) {
_cleanup_free_ char *k = NULL; _cleanup_free_ char *k = NULL, *k2 = NULL;
int r; int r;
r = path_extract_directory(input, &k); r = path_extract_directory(input, &k);
@ -1082,10 +1089,18 @@ static void test_path_extract_directory_one(const char *input, const char *outpu
ASSERT_STREQ(k, output); ASSERT_STREQ(k, output);
assert_se(r == ret); assert_se(r == ret);
/* Extra safety check: make sure that path_split_prefix_filename() behaves the same.
* We cant check the return value from it though as that differs based on the filename component.
* We can only assert that if path_extract_directory() fails, then
* path_split_prefix_filename() must also fail. */
r = path_split_prefix_filename(input, &k2, NULL);
ASSERT_STREQ(k2, k);
assert_se(!(ret < 0) || r < 0);
/* Extra safety check: let's make sure that if we split out the filename too (and it works) the /* Extra safety check: let's make sure that if we split out the filename too (and it works) the
* joined parts are identical to the original again */ * joined parts are identical to the original again */
if (r >= 0) { if (r >= 0) {
_cleanup_free_ char *f = NULL; _cleanup_free_ char *f = NULL, *k3 = NULL, *f2 = NULL;
r = path_extract_filename(input, &f); r = path_extract_filename(input, &f);
if (r >= 0) { if (r >= 0) {
@ -1094,6 +1109,13 @@ static void test_path_extract_directory_one(const char *input, const char *outpu
assert_se(j = path_join(k, f)); assert_se(j = path_join(k, f));
assert_se(path_equal(input, j)); assert_se(path_equal(input, j));
} }
/* And the same, but for path_split_prefix_filename() */
r = path_split_prefix_filename(input, &k3, &f2);
if (r >= 0) {
ASSERT_STREQ(k3, k);
ASSERT_STREQ(f2, f);
}
} }
} }

View File

@ -32,7 +32,7 @@ if conf.get('ENABLE_SSH_USERDB_CONFIG') == 1
install : true, install : true,
install_dir : sshdconfdir.startswith('/usr/') ? sshdconfdir : libexecdir / 'sshd_config.d') install_dir : sshdconfdir.startswith('/usr/') ? sshdconfdir : libexecdir / 'sshd_config.d')
if conf.get('LINK_SSHD_USERDB_DROPIN') == 1 if (not sshdconfdir.startswith(sysconfdir) or install_sysconfdir) and conf.get('LINK_SSHD_USERDB_DROPIN') == 1
if meson.version().version_compare('>=1.3.0') if meson.version().version_compare('>=1.3.0')
install_symlink( install_symlink(
'20-systemd-userdb.conf', '20-systemd-userdb.conf',

View File

@ -10,6 +10,7 @@
#include "build.h" #include "build.h"
#include "bus-util.h" #include "bus-util.h"
#include "chase.h"
#include "env-util.h" #include "env-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
@ -21,10 +22,14 @@
#include "pager.h" #include "pager.h"
#include "parse-argument.h" #include "parse-argument.h"
#include "parse-util.h" #include "parse-util.h"
#include "path-lookup.h"
#include "path-util.h"
#include "pidfd-util.h" #include "pidfd-util.h"
#include "polkit-agent.h" #include "polkit-agent.h"
#include "pretty-print.h" #include "pretty-print.h"
#include "process-util.h" #include "process-util.h"
#include "recurse-dir.h"
#include "runtime-scope.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
#include "terminal-util.h" #include "terminal-util.h"
@ -49,6 +54,8 @@ static usec_t arg_timeout = 0;
static bool arg_exec = false; static bool arg_exec = false;
static PushFds arg_push_fds = {}; static PushFds arg_push_fds = {};
static bool arg_ask_password = true; static bool arg_ask_password = true;
static bool arg_legend = true;
static RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
static void push_fds_done(PushFds *p) { static void push_fds_done(PushFds *p) {
assert(p); assert(p);
@ -85,6 +92,7 @@ static int help(void) {
" Invoke method\n" " Invoke method\n"
" --exec call ADDRESS METHOD PARAMS -- CMDLINE…\n" " --exec call ADDRESS METHOD PARAMS -- CMDLINE…\n"
" Invoke method and pass response and fds to command\n" " Invoke method and pass response and fds to command\n"
" list-registry Show list of services in the service registry\n"
" validate-idl [FILE] Validate interface description\n" " validate-idl [FILE] Validate interface description\n"
" help Show this help\n" " help Show this help\n"
"\n%3$sOptions:%4$s\n" "\n%3$sOptions:%4$s\n"
@ -92,6 +100,8 @@ static int help(void) {
" --version Show package version\n" " --version Show package version\n"
" --no-ask-password Do not prompt for password\n" " --no-ask-password Do not prompt for password\n"
" --no-pager Do not pipe output into a pager\n" " --no-pager Do not pipe output into a pager\n"
" --system Enumerate system registry\n"
" --user Enumerate user registry\n"
" --more Request multiple responses\n" " --more Request multiple responses\n"
" --collect Collect multiple responses in a JSON array\n" " --collect Collect multiple responses in a JSON array\n"
" --oneway Do not request response\n" " --oneway Do not request response\n"
@ -131,6 +141,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_EXEC, ARG_EXEC,
ARG_PUSH_FD, ARG_PUSH_FD,
ARG_NO_ASK_PASSWORD, ARG_NO_ASK_PASSWORD,
ARG_USER,
ARG_SYSTEM,
}; };
static const struct option options[] = { static const struct option options[] = {
@ -147,6 +159,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "exec", no_argument, NULL, ARG_EXEC }, { "exec", no_argument, NULL, ARG_EXEC },
{ "push-fd", required_argument, NULL, ARG_PUSH_FD }, { "push-fd", required_argument, NULL, ARG_PUSH_FD },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "user", no_argument, NULL, ARG_USER },
{ "system", no_argument, NULL, ARG_SYSTEM },
{}, {},
}; };
@ -263,6 +277,14 @@ static int parse_argv(int argc, char *argv[]) {
arg_ask_password = false; arg_ask_password = false;
break; break;
case ARG_USER:
arg_runtime_scope = RUNTIME_SCOPE_USER;
break;
case ARG_SYSTEM:
arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
break;
case '?': case '?':
return -EINVAL; return -EINVAL;
@ -973,6 +995,105 @@ static int verb_validate_idl(int argc, char *argv[], void *userdata) {
return 0; return 0;
} }
static int verb_list_registry(int argc, char *argv[], void *userdata) {
int r;
assert(argc <= 1);
_cleanup_free_ char *reg_path = NULL;
r = runtime_directory_generic(arg_runtime_scope, "varlink/registry", &reg_path);
if (r < 0)
return log_error_errno(r, "Failed to determine registry path: %m");
_cleanup_(table_unrefp) Table *table = table_new("interface", "entrypoint");
if (!table)
return log_oom();
(void) table_set_sort(table, (size_t) 0);
_cleanup_close_ int regfd = open(reg_path, O_DIRECTORY|O_CLOEXEC);
if (regfd < 0) {
if (errno != ENOENT)
return log_error_errno(errno, "Failed to open '%s': %m", reg_path);
} else {
_cleanup_free_ DirectoryEntries *des = NULL;
r = readdir_all(regfd, RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, &des);
if (r < 0)
return log_error_errno(r, "Failed to enumerate '%s': %m", reg_path);
FOREACH_ARRAY(i, des->entries, des->n_entries) {
struct dirent *de = *i;
if (!varlink_idl_interface_name_is_valid(de->d_name)) {
log_debug("Found file '%s' whose names does not qualify as valid Varlink interface name, skipping.", de->d_name);
continue;
}
_cleanup_free_ char *j = path_join(reg_path, de->d_name);
if (!j)
return log_oom();
switch (de->d_type) {
case DT_LNK: {
_cleanup_free_ char *resolved = NULL;
r = chase(j, /* root= */ NULL, CHASE_MUST_BE_SOCKET, &resolved, /* ret_fd= */ NULL);
if (r < 0) {
log_warning_errno(r, "Failed to resolve '%s', skipping: %m", j);
continue;
}
_cleanup_free_ char *address = strjoin("unix:", resolved);
if (!address)
return log_oom();
r = table_add_many(
table,
TABLE_STRING, de->d_name,
TABLE_STRING, address);
if (r < 0)
return r;
break;
}
case DT_SOCK: {
_cleanup_free_ char *address = strjoin("unix:", j);
if (!address)
return log_oom();
r = table_add_many(
table,
TABLE_STRING, de->d_name,
TABLE_STRING, address);
if (r < 0)
return r;
break;
}
default:
log_debug("Ignoring inode '%s' of unexpected type: %m", de->d_name);
}
}
}
if (!table_isempty(table) || sd_json_format_enabled(arg_json_format_flags)) {
r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, /* show_header= */ true);
if (r < 0)
return log_error_errno(r, "Failed to output table: %m");
}
if (arg_legend && !sd_json_format_enabled(arg_json_format_flags)) {
if (table_isempty(table))
printf("No services registered.\n");
else
printf("\n%zu registered services listed.\n", table_get_rows(table) - 1);
}
return 0;
}
static int varlinkctl_main(int argc, char *argv[]) { static int varlinkctl_main(int argc, char *argv[]) {
static const Verb verbs[] = { static const Verb verbs[] = {
{ "info", 2, 2, 0, verb_info }, { "info", 2, 2, 0, verb_info },
@ -980,6 +1101,7 @@ static int varlinkctl_main(int argc, char *argv[]) {
{ "introspect", 2, VERB_ANY, 0, verb_introspect }, { "introspect", 2, VERB_ANY, 0, verb_introspect },
{ "list-methods", 2, VERB_ANY, 0, verb_introspect }, { "list-methods", 2, VERB_ANY, 0, verb_introspect },
{ "call", 3, VERB_ANY, 0, verb_call }, { "call", 3, VERB_ANY, 0, verb_call },
{ "list-registry", VERB_ANY, 1, 0, verb_list_registry },
{ "validate-idl", 1, 2, 0, verb_validate_idl }, { "validate-idl", 1, 2, 0, verb_validate_idl },
{ "help", VERB_ANY, VERB_ANY, 0, verb_help }, { "help", VERB_ANY, VERB_ANY, 0, verb_help },
{} {}

View File

@ -42,7 +42,7 @@ if git.found() and fs.is_dir(meson.project_source_root() / '.git')
'ls-files', ':/@0@/*/*'.format(fuzz_testsdir), 'ls-files', ':/@0@/*/*'.format(fuzz_testsdir),
check: true) check: true)
else else
out = run_command(sh, '-c', 'cd "@0@"; echo @1@/*/*'.format(meson.project_source_root(), fuzz_testsdir), check: true) out = run_command(sh, '-c', 'cd "@0@"; printf "%s " @1@/*/*'.format(meson.project_source_root(), fuzz_testsdir), check: true)
endif endif
# Add crafted fuzz inputs we have in the repo # Add crafted fuzz inputs we have in the repo

View File

@ -37,6 +37,10 @@ varlinkctl introspect -j /run/systemd/journal/io.systemd.journal | jq --seq .
varlinkctl introspect /run/systemd/journal/io.systemd.journal io.systemd.Journal varlinkctl introspect /run/systemd/journal/io.systemd.journal io.systemd.Journal
varlinkctl introspect -j /run/systemd/journal/io.systemd.journal io.systemd.Journal | jq . varlinkctl introspect -j /run/systemd/journal/io.systemd.journal io.systemd.Journal | jq .
varlinkctl list-registry
varlinkctl list-registry -j | jq .
varlinkctl list-registry | grep io.systemd.Manager
if command -v userdbctl >/dev/null; then if command -v userdbctl >/dev/null; then
systemctl start systemd-userdbd systemctl start systemd-userdbd
varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }' varlinkctl call /run/systemd/userdb/io.systemd.Multiplexer io.systemd.UserDatabase.GetUserRecord '{ "userName" : "testuser", "service" : "io.systemd.Multiplexer" }'

View File

@ -27,9 +27,11 @@ restore_esp() {
fi fi
if [ -d /tmp/esp.bak/EFI/ ]; then if [ -d /tmp/esp.bak/EFI/ ]; then
mkdir -p "$(bootctl --print-esp-path)/EFI/"
cp -r /tmp/esp.bak/EFI/* "$(bootctl --print-esp-path)/EFI/" cp -r /tmp/esp.bak/EFI/* "$(bootctl --print-esp-path)/EFI/"
fi fi
if [ -d /tmp/esp.bak/loader/ ]; then if [ -d /tmp/esp.bak/loader/ ]; then
mkdir -p "$(bootctl --print-esp-path)/loader/"
cp -r /tmp/esp.bak/loader/* "$(bootctl --print-esp-path)/loader/" cp -r /tmp/esp.bak/loader/* "$(bootctl --print-esp-path)/loader/"
fi fi
rm -rf /tmp/esp.bak rm -rf /tmp/esp.bak
@ -40,13 +42,19 @@ backup_esp() {
return return
fi fi
# make a backup of the two key dirs in the ESP, and delete them
if [[ -d "$(bootctl --print-esp-path)/EFI" ]]; then if [[ -d "$(bootctl --print-esp-path)/EFI" ]]; then
mkdir -p /tmp/esp.bak mkdir -p /tmp/esp.bak
cp -r "$(bootctl --print-esp-path)/EFI/" /tmp/esp.bak/ cp -r "$(bootctl --print-esp-path)/EFI/" /tmp/esp.bak/
rm -rf "$(bootctl --print-esp-path)/EFI"
mkdir "$(bootctl --print-esp-path)/EFI"
fi fi
if [[ -d "$(bootctl --print-esp-path)/loader" ]]; then if [[ -d "$(bootctl --print-esp-path)/loader" ]]; then
mkdir -p /tmp/esp.bak mkdir -p /tmp/esp.bak
cp -r "$(bootctl --print-esp-path)/loader/" /tmp/esp.bak/ cp -r "$(bootctl --print-esp-path)/loader/" /tmp/esp.bak/
rm -rf "$(bootctl --print-esp-path)/loader"
mkdir "$(bootctl --print-esp-path)/loader"
fi fi
} }
@ -364,4 +372,22 @@ testcase_00_secureboot() {
grep -q addonfoobar /proc/cmdline grep -q addonfoobar /proc/cmdline
} }
remove_root_dir() {
rm -rf "$ROOTDIR"
}
testcase_install_varlink() {
varlinkctl introspect "$(type -p bootctl)"
if [ $# -eq 0 ]; then
backup_esp
trap restore_esp RETURN ERR
fi
(! bootctl is-installed )
SYSTEMD_LOG_TARGET=console varlinkctl call "$(type -p bootctl)" io.systemd.BootControl.Install "{\"operation\":\"new\",\"touchVariables\":false}"
bootctl is-installed
}
run_testcases run_testcases

View File

@ -0,0 +1,18 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# See tmpfiles.d(5) for details.
# Varlink AF_UNIX entrypoint socket inodes of relevant system services may be
# linked into this directory, to make them "well-known". Typically, tools such
# as the Varlink HTTP bridge expose services linked in here as public
# interfaces. The symlink should be named after the Varlink interface to
# expose. Interfaces that may be implemented by multiple services (such as
# the generic "io.systemd.service") should not be symlinked here.
d /run/varlink/registry/ 0755 root root
# Socket activated services should use Symlinks= in the .socket unit file to
# create these symlinks. If that's not applicable, consider creating the
# symlinks via a tmpfiles.d/ snippet, like we do here, so that registration can
# be influenced by the administrator.
L /run/varlink/registry/io.systemd.Unit - - - - ../../systemd/io.systemd.Manager
L /run/varlink/registry/io.systemd.Manager - - - - ../../systemd/io.systemd.Manager
L /run/varlink/registry/io.systemd.Journal - - - - ../../systemd/journal/io.systemd.journal

View File

@ -13,6 +13,7 @@ files = [['README' ],
['systemd-nspawn.conf', 'ENABLE_MACHINED' ], ['systemd-nspawn.conf', 'ENABLE_MACHINED' ],
['systemd-pstore.conf', 'ENABLE_PSTORE' ], ['systemd-pstore.conf', 'ENABLE_PSTORE' ],
['systemd-resolve.conf', 'ENABLE_RESOLVE' ], ['systemd-resolve.conf', 'ENABLE_RESOLVE' ],
['20-systemd-varlink.conf' ],
['systemd-tmp.conf' ], ['systemd-tmp.conf' ],
['tmp.conf' ], ['tmp.conf' ],
['x11.conf' ], ['x11.conf' ],
@ -59,3 +60,5 @@ endforeach
if install_sysconfdir if install_sysconfdir
install_emptydir(sysconfdir / 'tmpfiles.d') install_emptydir(sysconfdir / 'tmpfiles.d')
endif endif
subdir('user')

View File

@ -0,0 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# See tmpfiles.d(5) for details.
d %t/varlink/registry/ 0755
L %t/varlink/registry/io.systemd.Unit - - - - ../../systemd/io.systemd.Manager
L %t/varlink/registry/io.systemd.Manager - - - - ../../systemd/io.systemd.Manager

View File

@ -0,0 +1,11 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
files = [ '20-systemd-varlink.conf' ]
foreach f : files
install_data(f, install_dir : usertmpfilesdir)
endforeach
if install_sysconfdir
install_emptydir(sysconfdir / 'user-tmpfiles.d')
endif

View File

@ -572,3 +572,4 @@ loader.conf.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="rebo
loader.conf.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="reboot-on-error"]/listitem/para/variablelist/varlistentry[term="auto"] loader.conf.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="reboot-on-error"]/listitem/para/variablelist/varlistentry[term="auto"]
loader.conf.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="secure-boot-enroll-action"]/listitem/variablelist/varlistentry[term="reboot"] loader.conf.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="secure-boot-enroll-action"]/listitem/variablelist/varlistentry[term="reboot"]
loader.conf.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="secure-boot-enroll-action"]/listitem/variablelist/varlistentry[term="shutdown"] loader.conf.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="secure-boot-enroll-action"]/listitem/variablelist/varlistentry[term="shutdown"]
varlinkctl.xml ./refsect1[title="Options"]/variablelist/varlistentry[term="--system"]

View File

@ -15,6 +15,7 @@ Before=sockets.target
[Socket] [Socket]
ListenStream=/run/systemd/io.systemd.AskPassword ListenStream=/run/systemd/io.systemd.AskPassword
Symlinks=/run/varlink/registry/io.systemd.AskPassword
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0666 SocketMode=0666
Accept=yes Accept=yes

View File

@ -8,7 +8,7 @@
# (at your option) any later version. # (at your option) any later version.
[Unit] [Unit]
Description=Boot Entries Service Socket Description=Boot Loader Control Service Socket
Documentation=man:bootctl(1) Documentation=man:bootctl(1)
DefaultDependencies=no DefaultDependencies=no
After=local-fs.target After=local-fs.target
@ -16,6 +16,7 @@ Before=sockets.target
[Socket] [Socket]
ListenStream=/run/systemd/io.systemd.BootControl ListenStream=/run/systemd/io.systemd.BootControl
Symlinks=/run/varlink/registry/io.systemd.BootControl
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0600 SocketMode=0600
Accept=yes Accept=yes

View File

@ -8,7 +8,7 @@
# (at your option) any later version. # (at your option) any later version.
[Unit] [Unit]
Description=Boot Entries Service Description=Boot Loader Control Service
Documentation=man:bootctl(1) Documentation=man:bootctl(1)
DefaultDependencies=no DefaultDependencies=no
Conflicts=shutdown.target Conflicts=shutdown.target

View File

@ -15,6 +15,7 @@ Before=sockets.target
[Socket] [Socket]
ListenStream=/run/systemd/io.systemd.Credentials ListenStream=/run/systemd/io.systemd.Credentials
Symlinks=/run/varlink/registry/io.systemd.Credentials
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0666 SocketMode=0666
Accept=yes Accept=yes

View File

@ -15,6 +15,7 @@ Before=sockets.target
[Socket] [Socket]
ListenStream=/run/systemd/io.systemd.FactoryReset ListenStream=/run/systemd/io.systemd.FactoryReset
Symlinks=/run/varlink/registry/io.systemd.FactoryReset
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0666 SocketMode=0666
Accept=yes Accept=yes

View File

@ -15,5 +15,6 @@ Documentation=man:machine-info(5)
[Socket] [Socket]
ListenStream=/run/systemd/io.systemd.Hostname ListenStream=/run/systemd/io.systemd.Hostname
Symlinks=/run/varlink/registry/io.systemd.Hostname
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0666 SocketMode=0666

View File

@ -20,5 +20,6 @@ Before=shutdown.target
[Socket] [Socket]
ListenStream=/run/systemd/io.systemd.Import ListenStream=/run/systemd/io.systemd.Import
Symlinks=/run/varlink/registry/io.systemd.Import
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0666 SocketMode=0666

View File

@ -13,6 +13,7 @@ Documentation=man:systemd-logind.service(8)
[Socket] [Socket]
ListenStream=/run/systemd/io.systemd.Login ListenStream=/run/systemd/io.systemd.Login
Symlinks=/run/varlink/registry/io.systemd.Login
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0666 SocketMode=0666
Service=systemd-logind.service Service=systemd-logind.service

View File

@ -13,6 +13,6 @@ Documentation=man:systemd-machined.service(8)
[Socket] [Socket]
ListenStream=/run/systemd/machine/io.systemd.Machine ListenStream=/run/systemd/machine/io.systemd.Machine
ListenStream=/run/systemd/machine/io.systemd.MachineImage Symlinks=/run/systemd/machine/io.systemd.MachineImage /run/varlink/registry/io.systemd.Machine /run/varlink/registry/io.systemd.MachineImage
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0666 SocketMode=0666

View File

@ -16,6 +16,8 @@ Before=sockets.target shutdown.target
[Socket] [Socket]
ListenStream=/run/systemd/io.systemd.MountFileSystem ListenStream=/run/systemd/io.systemd.MountFileSystem
Symlinks=/run/varlink/registry/io.systemd.MountFileSystem
FileDescriptorName=varlink
SocketMode=0666 SocketMode=0666
[Install] [Install]

View File

@ -17,6 +17,7 @@ Before=shutdown.target
[Socket] [Socket]
ListenStream=/run/systemd/io.systemd.MuteConsole ListenStream=/run/systemd/io.systemd.MuteConsole
Symlinks=/run/varlink/registry/io.systemd.MuteConsole
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0600 SocketMode=0600
Accept=yes Accept=yes

View File

@ -17,6 +17,7 @@ Conflicts=shutdown.target
[Socket] [Socket]
ListenStream=/run/systemd/netif/io.systemd.Network ListenStream=/run/systemd/netif/io.systemd.Network
Symlinks=/run/varlink/registry/io.systemd.Network
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0666 SocketMode=0666
Service=systemd-networkd.service Service=systemd-networkd.service

View File

@ -16,7 +16,8 @@ Before=sockets.target shutdown.target
[Socket] [Socket]
ListenStream=/run/systemd/io.systemd.NamespaceResource ListenStream=/run/systemd/io.systemd.NamespaceResource
Symlinks=/run/systemd/userdb/io.systemd.NamespaceResource Symlinks=/run/systemd/userdb/io.systemd.NamespaceResource /run/varlink/registry/io.systemd.NamespaceResource
FileDescriptorName=varlink
SocketMode=0666 SocketMode=0666
[Install] [Install]

View File

@ -17,6 +17,7 @@ ConditionSecurity=measured-uki
[Socket] [Socket]
ListenStream=/run/systemd/io.systemd.PCRExtend ListenStream=/run/systemd/io.systemd.PCRExtend
Symlinks=/run/varlink/registry/io.systemd.PCRExtend
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0600 SocketMode=0600
Accept=yes Accept=yes

View File

@ -17,6 +17,7 @@ ConditionSecurity=measured-uki
[Socket] [Socket]
ListenStream=/run/systemd/io.systemd.PCRLock ListenStream=/run/systemd/io.systemd.PCRLock
Symlinks=/run/varlink/registry/io.systemd.PCRLock
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0600 SocketMode=0600
Accept=yes Accept=yes

View File

@ -17,6 +17,7 @@ Before=shutdown.target
[Socket] [Socket]
ListenStream=/run/systemd/io.systemd.Repart ListenStream=/run/systemd/io.systemd.Repart
Symlinks=/run/varlink/registry/io.systemd.Repart
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0600 SocketMode=0600
Accept=yes Accept=yes

View File

@ -17,6 +17,7 @@ Conflicts=shutdown.target
[Socket] [Socket]
Service=systemd-resolved.service Service=systemd-resolved.service
ListenStream=/run/systemd/resolve/io.systemd.Resolve.Monitor ListenStream=/run/systemd/resolve/io.systemd.Resolve.Monitor
Symlinks=/run/varlink/registry/io.systemd.Resolve.Monitor
FileDescriptorName=varlink-monitor FileDescriptorName=varlink-monitor
SocketMode=0666 SocketMode=0666

View File

@ -17,6 +17,7 @@ Conflicts=shutdown.target
[Socket] [Socket]
Service=systemd-resolved.service Service=systemd-resolved.service
ListenStream=/run/systemd/resolve/io.systemd.Resolve ListenStream=/run/systemd/resolve/io.systemd.Resolve
Symlinks=/run/varlink/registry/io.systemd.Resolve
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0666 SocketMode=0666

View File

@ -17,6 +17,7 @@ ConditionCapability=CAP_SYS_ADMIN
[Socket] [Socket]
ListenStream=/run/systemd/io.systemd.sysext ListenStream=/run/systemd/io.systemd.sysext
Symlinks=/run/varlink/registry/io.systemd.sysext
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0666 SocketMode=0666
Accept=yes Accept=yes

View File

@ -17,6 +17,7 @@ ConditionPathIsReadWrite=/sys
[Socket] [Socket]
Service=systemd-udevd.service Service=systemd-udevd.service
ListenStream=/run/udev/io.systemd.Udev ListenStream=/run/udev/io.systemd.Udev
Symlinks=/run/varlink/registry/io.systemd.Udev
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0600 SocketMode=0600
RemoveOnStop=yes RemoveOnStop=yes

View File

@ -15,7 +15,8 @@ Before=sockets.target
[Socket] [Socket]
ListenStream=/run/systemd/userdb/io.systemd.Multiplexer ListenStream=/run/systemd/userdb/io.systemd.Multiplexer
Symlinks=/run/systemd/userdb/io.systemd.NameServiceSwitch /run/systemd/userdb/io.systemd.DropIn Symlinks=/run/systemd/userdb/io.systemd.NameServiceSwitch /run/systemd/userdb/io.systemd.DropIn /run/varlink/registry/io.systemd.UserDatabase
FileDescriptorName=varlink
SocketMode=0666 SocketMode=0666
RemoveOnStop=yes RemoveOnStop=yes

View File

@ -15,6 +15,7 @@ Before=sockets.target
[Socket] [Socket]
ListenStream=%t/systemd/io.systemd.AskPassword ListenStream=%t/systemd/io.systemd.AskPassword
Symlinks=%t/varlink/registry/io.systemd.AskPassword
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0600 SocketMode=0600
Accept=yes Accept=yes

View File

@ -14,5 +14,6 @@ Documentation=man:org.freedesktop.import1(5)
[Socket] [Socket]
ListenStream=%t/systemd/io.systemd.Import ListenStream=%t/systemd/io.systemd.Import
Symlinks=%t/varlink/registry/io.systemd.Import
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0600 SocketMode=0600

View File

@ -13,6 +13,6 @@ Documentation=man:systemd-machined.service(8)
[Socket] [Socket]
ListenStream=%t/systemd/machine/io.systemd.Machine ListenStream=%t/systemd/machine/io.systemd.Machine
ListenStream=%t/systemd/machine/io.systemd.MachineImage Symlinks=%t/systemd/machine/io.systemd.MachineImage %t/varlink/registry/io.systemd.Machine %t/varlink/registry/io.systemd.MachineImage
FileDescriptorName=varlink FileDescriptorName=varlink
SocketMode=0600 SocketMode=0600