mirror of
https://github.com/systemd/systemd.git
synced 2025-03-10 16:58:28 +03:00
Compare commits
115 Commits
0bf70e5895
...
5f5bd3941c
Author | SHA1 | Date | |
---|---|---|---|
|
5f5bd3941c | ||
|
f23262b53f | ||
|
47fde5edc0 | ||
|
ec56bfd860 | ||
|
7a5d90b6a3 | ||
|
9926ec9115 | ||
|
da1aa98049 | ||
|
2e6bb5a62e | ||
|
29dc3fbcdc | ||
|
348cb8b94a | ||
|
22b1b873f1 | ||
|
96ef027f53 | ||
|
bf2bb1aba9 | ||
|
0e4009e9bc | ||
|
ccaa76ac48 | ||
|
2232038187 | ||
|
1563404159 | ||
|
2138278d25 | ||
|
34b5a27b0b | ||
|
ba3f148307 | ||
|
b133f57544 | ||
|
8c5b4df543 | ||
|
1995084a9e | ||
|
ac1a711d9a | ||
|
d4dda34854 | ||
|
7337f4b197 | ||
|
3ee5cab490 | ||
|
3add2d73b3 | ||
|
1dd345b00d | ||
|
8ca50bde48 | ||
|
0eb51d9913 | ||
|
6b99f3ba5a | ||
|
95cd07e772 | ||
|
0f55038c84 | ||
|
37e02b455b | ||
|
a04af8516e | ||
|
094e2ace12 | ||
|
f8988a5e45 | ||
|
d1bbfaeba5 | ||
|
2b717a7f14 | ||
|
1c0ade2e1f | ||
|
8cbcdc78db | ||
|
4103bf9f2f | ||
|
026dfd60d4 | ||
|
1af989e8de | ||
|
8506a9955c | ||
|
d2cd189324 | ||
|
b226b7fb6d | ||
|
060e2512cd | ||
|
81082f2dc2 | ||
|
616586b910 | ||
|
cf7d0a2d2e | ||
|
06ffa66a5b | ||
|
33bfa69b2e | ||
|
f108996319 | ||
|
dec47e58a6 | ||
|
cdcb1eeeb8 | ||
|
77d4a263c1 | ||
|
5f29c86ace | ||
|
182ffb5819 | ||
|
e9f781a5a4 | ||
|
cb3801a4c9 | ||
|
8a135111ca | ||
|
5e837858e7 | ||
|
a3fecea5e2 | ||
|
f01132aacf | ||
|
ad920b4cb3 | ||
|
a7396f8364 | ||
|
71ec342d13 | ||
|
3b9010b170 | ||
|
cf89e48028 | ||
|
3cbf00a30c | ||
|
0f67cb3606 | ||
|
5ceb38cb1e | ||
|
312cf91005 | ||
|
b1b128d0e2 | ||
|
91cdc8ab0f | ||
|
009a02b263 | ||
|
b83358b87f | ||
|
bf1ef54d30 | ||
|
8f9ea89ce4 | ||
|
5229cd839a | ||
|
595ca10f37 | ||
|
6e3f32cc56 | ||
|
1462736c7e | ||
|
f441831c9e | ||
|
4bc06da775 | ||
|
ced0ef3b35 | ||
|
e95861d909 | ||
|
0f72af536f | ||
|
b5ea69f5ac | ||
|
04fa5c1580 | ||
|
394a678aec | ||
|
32c3e1379d | ||
|
0d1ebcf67d | ||
|
0e5a83f510 | ||
|
8f114904fc | ||
|
cffae6e113 | ||
|
a48803fd84 | ||
|
22583a002e | ||
|
be1bcb85ce | ||
|
85d040dabd | ||
|
67e5622bfe | ||
|
0da73fab56 | ||
|
64b504bde3 | ||
|
12807b5a49 | ||
|
4a4e7ec0e9 | ||
|
d517427dff | ||
|
5e6a48bf99 | ||
|
781c8c3f72 | ||
|
25b1a73f71 | ||
|
a5370d35d6 | ||
|
1184626a26 | ||
|
9311c28b34 | ||
|
19491cc90f |
13
.github/workflows/coverage.yml
vendored
13
.github/workflows/coverage.yml
vendored
@ -7,6 +7,14 @@ on:
|
||||
# Calculate coverage daily at midnight
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- v[0-9]+-stable
|
||||
paths:
|
||||
- .github/workflows/coverage.yml
|
||||
- test/integration-test-wrapper.py
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@ -16,7 +24,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
- uses: systemd/mkosi@07ef37c4c0dad5dfc6cec86c967a7600df1cd88c
|
||||
- uses: systemd/mkosi@ba07d53000b6c560ad0b9f07550aca93c0284e88
|
||||
|
||||
# Freeing up disk space with rm -rf can take multiple minutes. Since we don't need the extra free space
|
||||
# immediately, we remove the files in the background. However, we first move them to a different location
|
||||
@ -49,7 +57,6 @@ jobs:
|
||||
Distribution=arch
|
||||
|
||||
[Build]
|
||||
ToolsTree=default
|
||||
ToolsTreeDistribution=arch
|
||||
UseSubvolumes=yes
|
||||
WithTests=no
|
||||
@ -64,7 +71,7 @@ jobs:
|
||||
MESON_OPTIONS=--werror
|
||||
COVERAGE=1
|
||||
|
||||
[Host]
|
||||
[Runtime]
|
||||
QemuMem=4G
|
||||
EOF
|
||||
|
||||
|
5
.github/workflows/mkosi.yml
vendored
5
.github/workflows/mkosi.yml
vendored
@ -113,7 +113,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
- uses: systemd/mkosi@c4bbf3b71a3e2cf947995caedf10f69da3c4957a
|
||||
- uses: systemd/mkosi@ba07d53000b6c560ad0b9f07550aca93c0284e88
|
||||
|
||||
# Freeing up disk space with rm -rf can take multiple minutes. Since we don't need the extra free space
|
||||
# immediately, we remove the files in the background. However, we first move them to a different location
|
||||
@ -152,7 +152,6 @@ jobs:
|
||||
|
||||
[Build]
|
||||
UseSubvolumes=yes
|
||||
ToolsTree=default
|
||||
ToolsTreeDistribution=fedora
|
||||
ToolsTreeRelease=rawhide
|
||||
|
||||
@ -171,7 +170,7 @@ jobs:
|
||||
[Content]
|
||||
SELinuxRelabel=${{ matrix.relabel }}
|
||||
|
||||
[Host]
|
||||
[Runtime]
|
||||
QemuMem=4G
|
||||
EOF
|
||||
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,6 +7,7 @@
|
||||
.config.args
|
||||
.gdb_history
|
||||
.deps/
|
||||
.venv/
|
||||
.mypy_cache/
|
||||
__pycache__/
|
||||
/*.gcda
|
||||
|
20
TODO
20
TODO
@ -122,6 +122,18 @@ Deprecations and removals:
|
||||
|
||||
Features:
|
||||
|
||||
* importd: introduce a per-user instance, that downloads into per-user DDI dirs
|
||||
|
||||
* sysupdated: similar
|
||||
|
||||
* portabled: similar
|
||||
|
||||
* machined: implement a per-user instance, that manages per-user DDI dirs for
|
||||
images. systemd-nspawn/systemd-vmspawn should probably register with both the
|
||||
system and the user scoped machined instance. The former to get the machine
|
||||
name registered as hostname, and the latter so that the image stuff is nicely
|
||||
per-user managed.
|
||||
|
||||
* resolved: make resolved process DNR DHCP info
|
||||
|
||||
* Teach systemd-ssh-generator to generated an /run/issue.d/ drop-in telling
|
||||
@ -391,8 +403,6 @@ Features:
|
||||
the bg via vmspawn/nspawn if not done so yet and then requests a shell inside
|
||||
it for the invoking user.
|
||||
|
||||
* importd/…: define per-user dirs for container/VM images too.
|
||||
|
||||
* add a new specifier to unit files that figures out the DDI the unit file is
|
||||
from, tracing through overlayfs, DM, loopback block device.
|
||||
|
||||
@ -446,10 +456,6 @@ Features:
|
||||
* credentials: add a flag to the scoped credentials that if set require PK
|
||||
reauthentication when unlocking a secret.
|
||||
|
||||
* teach systemd --user to properly load credentials off disk, with
|
||||
/etc/credstore equivalent and similar. Make sure that $CREDENTIALS_DIRECTORY=
|
||||
actually works too when run with user privs.
|
||||
|
||||
* extend the smbios11 logic for passing credentials so that instead of passing
|
||||
the credential data literally it can also just reference an AF_VSOCK CID/port
|
||||
to read them from. This way the data doesn't remain in the SMBIOS blob during
|
||||
@ -795,7 +801,7 @@ Features:
|
||||
* udevd: extend memory pressure logic: also kill any idle worker processes
|
||||
|
||||
* udevadm: to make symlink querying with udevadm nicer:
|
||||
- do not enable the pager for queries like 'udevadm info -q -r symlink'
|
||||
- do not enable the pager for queries like 'udevadm info -q symlink -r'
|
||||
- add mode with newlines instead of spaces (for grep)?
|
||||
|
||||
* SIGRTMIN+18 and memory pressure handling should still be added to: hostnamed,
|
||||
|
210
docs/HACKING.md
210
docs/HACKING.md
@ -7,94 +7,97 @@ SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
# Hacking on systemd
|
||||
|
||||
We welcome all contributions to systemd.
|
||||
If you notice a bug or a missing feature, please feel invited to fix it, and submit your work as a
|
||||
We welcome all contributions to systemd. If you notice a bug or a missing
|
||||
feature, please feel invited to fix it, and submit your work as a
|
||||
[GitHub Pull Request (PR)](https://github.com/systemd/systemd/pull/new).
|
||||
|
||||
Please make sure to follow our [Coding Style](/CODING_STYLE) when submitting patches.
|
||||
Also have a look at our [Contribution Guidelines](/CONTRIBUTING).
|
||||
Please make sure to follow our [Coding Style](/CODING_STYLE) when submitting
|
||||
patches. Also have a look at our [Contribution Guidelines](/CONTRIBUTING).
|
||||
|
||||
When adding new functionality, tests should be added.
|
||||
For shared functionality (in `src/basic/` and `src/shared/`) unit tests should be sufficient.
|
||||
The general policy is to keep tests in matching files underneath `src/test/`,
|
||||
e.g. `src/test/test-path-util.c` contains tests for any functions in `src/basic/path-util.c`.
|
||||
If adding a new source file, consider adding a matching test executable.
|
||||
For features at a higher level, tests in `src/test/` are very strongly recommended.
|
||||
If that is not possible, integration tests in `test/` are encouraged.
|
||||
When adding new functionality, tests should be added. For shared functionality
|
||||
(in `src/basic/` and `src/shared/`) unit tests should be sufficient. The general
|
||||
policy is to keep tests in matching files underneath `src/test/`, e.g.
|
||||
`src/test/test-path-util.c` contains tests for any functions in
|
||||
`src/basic/path-util.c`. If adding a new source file, consider adding a matching
|
||||
test executable. For features at a higher level, tests in `src/test/` are very
|
||||
strongly recommended. If that is not possible, integration tests in `test/` are
|
||||
encouraged. Please always test your work before submitting a PR.
|
||||
|
||||
Please always test your work before submitting a PR.
|
||||
For many of the components of systemd testing is straightforward as you can simply compile systemd and run the relevant tool from the build directory.
|
||||
## Hacking on systemd with mkosi
|
||||
|
||||
For some components (most importantly, systemd/PID 1 itself) this is not possible, however.
|
||||
In order to simplify testing for cases like this we provide a set of `mkosi` config files directly in the source tree.
|
||||
[mkosi](https://mkosi.systemd.io/)
|
||||
is a tool for building clean OS images from an upstream distribution in combination with a fresh build of the project in the local working directory.
|
||||
To make use of this, please install `mkosi` from the [GitHub repository](https://github.com/systemd/mkosi#running-mkosi-from-the-repository).
|
||||
`mkosi` will build an image for the host distro by default.
|
||||
First, run `mkosi genkey` to generate a key and certificate to be used for secure boot and verity signing.
|
||||
After that is done, it is sufficient to type `mkosi` in the systemd project directory to generate a disk image you can boot either in `systemd-nspawn` or in a UEFI-capable VM:
|
||||
[mkosi](https://mkosi.systemd.io/) is our swiss army knife for hacking on
|
||||
systemd. It makes sure all necessary dependencies are available to build systemd
|
||||
and allows building and booting an OS image with the latest systemd installed
|
||||
for testing purposes.
|
||||
|
||||
First, install `mkosi` from the
|
||||
[GitHub repository](https://github.com/systemd/mkosi#running-mkosi-from-the-repository).
|
||||
Note that it's not possible to use your distribution's packaged version of mkosi
|
||||
as mkosi has to be installed outside of `/usr` for the following steps to work.
|
||||
|
||||
Then, you can build and run systemd executables as follows:
|
||||
|
||||
```sh
|
||||
$ sudo mkosi boot # nspawn still needs sudo for now
|
||||
$ mkosi -f sandbox meson setup build
|
||||
$ mkosi -f sandbox ninja -C build
|
||||
$ mkosi -f sandbox build/systemctl --version
|
||||
```
|
||||
|
||||
or:
|
||||
To build and boot an OS image with the latest systemd installed:
|
||||
|
||||
```sh
|
||||
$ mkosi qemu
|
||||
$ mkosi -f genkey # Generate signing keys once.
|
||||
$ mkosi -f sandbox ninja -C build mkosi # (re-)build the OS image
|
||||
$ sudo mkosi boot # Boot the image with systemd-nspawn.
|
||||
$ mkosi qemu # Boot the image with qemu.
|
||||
```
|
||||
|
||||
By default, the tools from your host system are used to build the image.
|
||||
Sometimes we start using mkosi features that rely on functionality in systemd
|
||||
tools that's not in an official release yet. In that case, you'll need to build
|
||||
systemd from source on the host and configure mkosi to use the tools from the
|
||||
systemd build directory.
|
||||
|
||||
To do a local build, most distributions provide very simple and convenient ways
|
||||
to install most development packages necessary to build systemd:
|
||||
Putting this all together, here's a series of commands for preparing a patch for
|
||||
systemd:
|
||||
|
||||
```sh
|
||||
# Fedora
|
||||
$ sudo dnf builddep systemd
|
||||
# Debian/Ubuntu
|
||||
$ sudo apt-get build-dep systemd
|
||||
# Arch
|
||||
$ sudo pacman -S devtools
|
||||
$ pkgctl repo clone --protocol=https systemd
|
||||
$ git clone https://github.com/systemd/mkosi.git
|
||||
$ ln -s $PWD/mkosi/bin/mkosi ~/.local/bin/mkosi # Make sure ~/.local/bin is in $PATH.
|
||||
$ git clone https://github.com/systemd/systemd.git
|
||||
$ cd systemd
|
||||
$ makepkg -seoc
|
||||
$ git checkout -b <BRANCH> # where BRANCH is the name of the branch
|
||||
$ $EDITOR src/core/main.c # or wherever you'd like to make your changes
|
||||
$ mkosi -f sandbox meson setup build # Set up meson
|
||||
$ mkosi -f genkey # Generate signing keys once.
|
||||
$ mkosi -f sandbox ninja -C build mkosi # (re-)build the test image
|
||||
$ mkosi qemu # Boot the image in qemu
|
||||
$ git add -p # interactively put together your patch
|
||||
$ git commit # commit it
|
||||
$ git push -u <REMOTE> # where REMOTE is your "fork" on GitHub
|
||||
```
|
||||
|
||||
After installing the development packages, systemd can be built from source as follows:
|
||||
And after that, head over to your repo on GitHub and click "Compare & pull
|
||||
request"
|
||||
|
||||
```sh
|
||||
$ meson setup build <options>
|
||||
$ ninja -C build
|
||||
$ meson test -C build
|
||||
```
|
||||
Happy hacking!
|
||||
|
||||
To have `mkosi` use the systemd tools from the `build/` directory, add the
|
||||
following to `mkosi.local.conf`:
|
||||
The following sections contain advanced topics on how to speed up development or
|
||||
streamline debugging. Feel free to read them if you're interested but they're
|
||||
not required to write basic patches.
|
||||
|
||||
## Building the OS image without a tools tree
|
||||
|
||||
By default, `mkosi` will first build a tools tree and use it build the image and
|
||||
provide the environment for `mkosi sandbox`. To disable the tools tree and use
|
||||
binaries from your host instead, write the following to `mkosi.local.conf`:
|
||||
|
||||
```conf
|
||||
[Host]
|
||||
ExtraSearchPaths=build/
|
||||
[Build]
|
||||
ToolsTree=
|
||||
```
|
||||
|
||||
And if you want `mkosi` to build a tools image and use the tools from there
|
||||
instead of looking for tools on the host, add the following to
|
||||
`mkosi.local.conf`:
|
||||
## Rebuilding systemd without rebuilding the OS image
|
||||
|
||||
```conf
|
||||
[Host]
|
||||
ToolsTree=default
|
||||
```
|
||||
|
||||
Every time you rerun the `mkosi` command a fresh image is built, incorporating
|
||||
all current changes you made to the project tree. To build the latest changes
|
||||
and re-install after booting the image, run one of the following commands in
|
||||
another terminal on your host (choose the right one depending on the
|
||||
distribution of the container or virtual machine):
|
||||
Every time the `mkosi` target is built, a fresh image is built. To build the
|
||||
latest changes and re-install systemd without rebuilding the image, run one of
|
||||
the following commands in another terminal on your host after booting the image
|
||||
(choose the right one depending on the distribution of the container or virtual
|
||||
machine):
|
||||
|
||||
```sh
|
||||
mkosi -t none && mkosi ssh dnf upgrade --disablerepo="*" --assumeyes "/work/build/*.rpm" # CentOS/Fedora
|
||||
@ -107,26 +110,6 @@ and optionally restart the daemon(s) you're working on using
|
||||
`systemctl restart <units>` or `systemctl daemon-reexec` if you're working on
|
||||
pid1 or `systemctl soft-reboot` to restart everything.
|
||||
|
||||
Putting this all together, here's a series of commands for preparing a patch for systemd:
|
||||
|
||||
```sh
|
||||
$ git clone https://github.com/systemd/mkosi.git
|
||||
$ ln -s $PWD/mkosi/bin/mkosi /usr/local/bin/mkosi
|
||||
$ git clone https://github.com/systemd/systemd.git
|
||||
$ cd systemd
|
||||
$ git checkout -b <BRANCH> # where BRANCH is the name of the branch
|
||||
$ vim src/core/main.c # or wherever you'd like to make your changes
|
||||
$ mkosi -f qemu # (re-)build and boot up the test image in qemu
|
||||
$ mkosi -t none # Build new packages without rebuilding the image
|
||||
$ git add -p # interactively put together your patch
|
||||
$ git commit # commit it
|
||||
$ git push -u <REMOTE> # where REMOTE is your "fork" on GitHub
|
||||
```
|
||||
|
||||
And after that, head over to your repo on GitHub and click "Compare & pull request"
|
||||
|
||||
Happy hacking!
|
||||
|
||||
## Building distribution packages with mkosi
|
||||
|
||||
To build distribution packages for a specific distribution and release without
|
||||
@ -201,67 +184,6 @@ Those are not useful when compiling for distribution and can be disabled by sett
|
||||
|
||||
See [Testing systemd using sanitizers](/TESTING_WITH_SANITIZERS) for more information on how to build with sanitizers enabled in mkosi.
|
||||
|
||||
## Fuzzers
|
||||
|
||||
systemd includes fuzzers in `src/fuzz/` that use libFuzzer and are automatically run by [OSS-Fuzz](https://github.com/google/oss-fuzz) with sanitizers.
|
||||
To add a fuzz target, create a new `src/fuzz/fuzz-foo.c` file with a `LLVMFuzzerTestOneInput` function and add it to the list in `src/fuzz/meson.build`.
|
||||
|
||||
Whenever possible, a seed corpus and a dictionary should also be added with new fuzz targets.
|
||||
The dictionary should be named `src/fuzz/fuzz-foo.dict` and the seed corpus should be built and exported as `$OUT/fuzz-foo_seed_corpus.zip` in `tools/oss-fuzz.sh`.
|
||||
|
||||
The fuzzers can be built locally if you have libFuzzer installed by running `tools/oss-fuzz.sh`, or by running:
|
||||
|
||||
```sh
|
||||
CC=clang CXX=clang++ \
|
||||
meson setup build-libfuzz -Dllvm-fuzz=true -Db_sanitize=address,undefined -Db_lundef=false \
|
||||
-Dc_args='-fno-omit-frame-pointer -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION'
|
||||
ninja -C build-libfuzz fuzzers
|
||||
```
|
||||
|
||||
Each fuzzer then can be then run manually together with a directory containing the initial corpus:
|
||||
|
||||
```
|
||||
export UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
|
||||
build-libfuzz/fuzz-varlink-idl test/fuzz/fuzz-varlink-idl/
|
||||
```
|
||||
|
||||
Note: the `halt_on_error=1` UBSan option is especially important,
|
||||
otherwise the fuzzer won't crash when undefined behavior is triggered.
|
||||
|
||||
You should also confirm that the fuzzers can be built and run using
|
||||
[the OSS-Fuzz toolchain](https://google.github.io/oss-fuzz/advanced-topics/reproducing/#building-using-docker):
|
||||
|
||||
```sh
|
||||
path_to_systemd=...
|
||||
|
||||
git clone --depth=1 https://github.com/google/oss-fuzz
|
||||
cd oss-fuzz
|
||||
|
||||
for sanitizer in address undefined memory; do
|
||||
for engine in libfuzzer afl honggfuzz; do
|
||||
./infra/helper.py build_fuzzers --sanitizer "$sanitizer" --engine "$engine" \
|
||||
--clean systemd "$path_to_systemd"
|
||||
|
||||
./infra/helper.py check_build --sanitizer "$sanitizer" --engine "$engine" \
|
||||
-e ALLOWED_BROKEN_TARGETS_PERCENTAGE=0 systemd
|
||||
done
|
||||
done
|
||||
|
||||
./infra/helper.py build_fuzzers --clean --architecture i386 systemd "$path_to_systemd"
|
||||
./infra/helper.py check_build --architecture i386 -e ALLOWED_BROKEN_TARGETS_PERCENTAGE=0 systemd
|
||||
|
||||
./infra/helper.py build_fuzzers --clean --sanitizer coverage systemd "$path_to_systemd"
|
||||
./infra/helper.py coverage --no-corpus-download systemd
|
||||
```
|
||||
|
||||
If you find a bug that impacts the security of systemd,
|
||||
please follow the guidance in [CONTRIBUTING.md](/CONTRIBUTING) on how to report a security vulnerability.
|
||||
|
||||
For more details on building fuzzers and integrating with OSS-Fuzz, visit:
|
||||
|
||||
- [Setting up a new project - OSS-Fuzz](https://google.github.io/oss-fuzz/getting-started/new-project-guide/)
|
||||
- [Tutorials - OSS-Fuzz](https://google.github.io/oss-fuzz/reference/useful-links/#tutorials)
|
||||
|
||||
## Debugging binaries that need to run as root in vscode
|
||||
|
||||
When trying to debug binaries that need to run as root,
|
||||
|
@ -3,3 +3,7 @@
|
||||
# Dell iDRAC Virtual USB NIC
|
||||
usb:v413CpA102*
|
||||
ID_NET_NAME_FROM_DATABASE=idrac
|
||||
|
||||
# Disable inclusion of PCI domain in interface names on Azure MANA
|
||||
pci:v00001414d000000BA*
|
||||
ID_NET_NAME_INCLUDE_DOMAIN=0
|
||||
|
@ -10,7 +10,7 @@ sudo systemd-cryptsetup attach mytest /dev/sdXn - fido2-device=auto
|
||||
# If that worked, let's now add the same line persistently to /etc/crypttab,
|
||||
# for the future. We don't want to use the (unstable) /dev/sdX name, so let's
|
||||
# figure out a stable link:
|
||||
udevadm info -q -r symlink /dev/sdXn
|
||||
udevadm info -q symlink -r /dev/sdXn
|
||||
|
||||
# Now add the line using the by-uuid symlink to /etc/crypttab:
|
||||
sudo bash -c 'echo "mytest /dev/disk/by-uuid/... - fido2-device=auto" >>/etc/crypttab'
|
||||
|
@ -97,6 +97,18 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.break=</varname></term>
|
||||
<term><varname>rd.systemd.break=</varname></term>
|
||||
<listitem>
|
||||
<para>Parameters understood by
|
||||
<citerefentry><refentrytitle>systemd-debug-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
to pause the boot process at a certain point and spawn a debug shell.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.run=</varname></term>
|
||||
<term><varname>systemd.run_success_action=</varname></term>
|
||||
|
@ -205,6 +205,11 @@
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">smbios11</arg>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-analyze</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">chid</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
@ -1084,6 +1089,37 @@ io.systemd.credential:vmm.notify_socket=vsock-stream:2:254570042
|
||||
<xi:include href="version-info.xml" xpointer="v257"/>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title><command>systemd-analyze chid</command></title>
|
||||
|
||||
<para>Shows a list of Computer Hardware IDs (CHIDs) of the local system. These IDs identify the
|
||||
system's computer hardware, based on SMBIOS data. See <ulink
|
||||
url="https://learn.microsoft.com/en-us/windows-hardware/drivers/dashboard/using-chids">Using Computer
|
||||
Hardware IDs (CHIDs)</ulink> for details about CHIDs.</para>
|
||||
|
||||
<example>
|
||||
<title>Example output</title>
|
||||
<programlisting>$ systemd-analyze chid
|
||||
TYPE INPUT CHID
|
||||
3 MFPSmp 520537c0-3b59-504f-b062-9682ea236b21
|
||||
4 MFPS-- edf05dc8-a53d-5b2c-8023-630bca2a2463
|
||||
5 MFP--- ebc6a4d9-ec48-537a-916b-c69fa4fdd814
|
||||
6 M--Smp 5ebe4bba-f598-5e90-9ff2-9fd0d3211465
|
||||
7 M--S-- 1a3fb835-b42a-5f9c-a38c-eff5bfd5c41d
|
||||
8 M-P-mp 2a831dce-8163-5bad-8406-435b8c752dd8
|
||||
9 M-P--- 7c21c878-4a75-50f7-9816-21e811588da0
|
||||
10 MF--mp 9a003537-bcc5-500e-b10a-8d8892e4fc64
|
||||
11 MF---- bb9122bb-8a5c-50d2-a742-a85beb719909
|
||||
13 M---mp bfc36935-5032-5987-a0a3-6311f01de33a
|
||||
|
||||
LEGEND: M → sys_vendor (LENOVO) ┄ F → product_family (ThinkPad X1 Carbon Gen 9) ┄ P → product_name (20XW0055GE)
|
||||
S → product_sku (LENOVO_MT_20XW_BU_Think_FM_ThinkPad X1 Carbon Gen 9) ┄ m → board_vendor (LENOVO)
|
||||
p → board_name (20XW0055GE)</programlisting>
|
||||
</example>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/>
|
||||
</refsect2>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -31,45 +31,131 @@
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><filename>systemd-debug-generator</filename> is a generator
|
||||
that reads the kernel command line and understands three
|
||||
options:</para>
|
||||
<para><command>systemd-debug-generator</command> is a generator that provides some debugging
|
||||
functionality.</para>
|
||||
|
||||
<para>If the <option>systemd.mask=</option> or <option>rd.systemd.mask=</option>
|
||||
option is specified and followed by a unit name, this unit is
|
||||
masked for the runtime (i.e. for this session — from boot to shutdown), similarly to the effect of
|
||||
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||
<command>mask</command> command. This is useful to boot with
|
||||
certain units removed from the initial boot transaction for
|
||||
debugging system startup. May be specified more than once.
|
||||
<option>rd.systemd.mask=</option> is honored only by initial
|
||||
RAM disk (initrd) while <option>systemd.mask=</option> is
|
||||
honored only in the main system.</para>
|
||||
|
||||
<para>If the <option>systemd.wants=</option> or
|
||||
<option>rd.systemd.wants=</option> option is specified
|
||||
and followed by a unit name, a start job for this unit is added to
|
||||
the initial transaction. This is useful to start one or more
|
||||
additional units at boot. May be specified more than once.
|
||||
<option>rd.systemd.wants=</option> is honored only by initial
|
||||
RAM disk (initrd) while <option>systemd.wants=</option> is
|
||||
honored only in the main system.</para>
|
||||
|
||||
<para>If the <option>systemd.debug_shell</option> or <option>rd.systemd.debug_shell</option> option is
|
||||
specified, the debug shell service <literal>debug-shell.service</literal> is pulled into the boot
|
||||
transaction and a debug shell will be spawned during early boot. By default,
|
||||
<filename>&DEBUGTTY;</filename> is used, but a specific tty can also be specified, either with or without
|
||||
the <filename>/dev/</filename> prefix. To set the tty to use without enabling the debug shell, the
|
||||
<option>systemd.default_debug_tty=</option> option can be used which also takes a tty with or without the
|
||||
<filename>/dev/</filename> prefix. Note that the shell may also be turned on persistently by enabling it
|
||||
with <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||
<command>enable</command> command. <option>rd.systemd.debug_shell</option> is honored only by initial
|
||||
RAM disk (initrd) while <option>systemd.debug_shell</option> is honored only in the main system.</para>
|
||||
|
||||
<para><filename>systemd-debug-generator</filename> implements
|
||||
<para><command>systemd-debug-generator</command> implements
|
||||
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Kernel Command Line</title>
|
||||
|
||||
<para><command>systemd-debug-generator</command> understands the following kernel command line
|
||||
parameters:</para>
|
||||
|
||||
<variablelist class='kernel-commandline-options'>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.mask=</varname></term>
|
||||
<term><varname>rd.systemd.mask=</varname></term>
|
||||
|
||||
<listitem><para>These options take a unit name as argument. The unit specified is masked for the
|
||||
runtime (i.e. for this session — from boot to shutdown), similarly to the effect of
|
||||
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||
<command>mask</command> command. This is useful to boot with certain units removed from the initial
|
||||
boot transaction for debugging system startup. May be specified more than once. The option prefixed
|
||||
with <literal>rd.</literal> is honored only in the initrd, while the one without prefix is only
|
||||
honored in the main system.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v215"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.wants=</varname></term>
|
||||
<term><varname>rd.systemd.wants=</varname></term>
|
||||
|
||||
<listitem><para>These options take a unit name as argument. A start job for this unit is added to the
|
||||
initial transaction. This is useful to start one or more additional units at boot. May be specified
|
||||
more than once. The option prefixed with <literal>rd.</literal> is honored only in the initrd, while
|
||||
the one that is not prefixed only in the main system.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v215"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.debug_shell</varname></term>
|
||||
<term><varname>rd.systemd.debug_shell</varname></term>
|
||||
<term><varname>systemd.default_debug_tty=</varname></term>
|
||||
<term><varname>rd.systemd.default_debug_tty=</varname></term>
|
||||
|
||||
<listitem><para>If the <option>systemd.debug_shell</option> or
|
||||
<option>rd.systemd.debug_shell</option> option is specified, the debug shell service
|
||||
<literal>debug-shell.service</literal> is pulled into the boot transaction and a debug shell will be
|
||||
spawned during early boot. By default, <filename>&DEBUGTTY;</filename> is used, but a specific tty
|
||||
can also be specified, either with or without the <filename>/dev/</filename> prefix. To set the tty
|
||||
to use without enabling the debug shell, the <option>systemd.default_debug_tty=</option> option can
|
||||
be used which also takes a tty with or without the <filename>/dev/</filename> prefix. Note that the
|
||||
shell may also be turned on persistently by enabling it with
|
||||
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||
<command>enable</command> command. The options prefixed with <literal>rd.</literal> are honored only
|
||||
in the initrd, while the ones without prefix are only honored in the main system.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v215"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.break=</varname></term>
|
||||
<term><varname>rd.systemd.break=</varname></term>
|
||||
|
||||
<listitem><para>Takes one of <option>pre-udev</option>, <option>pre-basic</option>,
|
||||
<option>pre-mount</option>, or <option>pre-switch-root</option> (the default for the
|
||||
<literal>rd.</literal> option). It also accepts multiple values separated by comma
|
||||
(<literal>,</literal>). These options allow to pause the boot process at a certain point and spawn a
|
||||
debug shell. After exiting this shell, the system will resume booting. The option prefixed with
|
||||
<literal>rd.</literal> is honored only in the initrd, while the one without prefix is only honored in
|
||||
the main system.</para>
|
||||
|
||||
<table>
|
||||
<title>Available breakpoints</title>
|
||||
|
||||
<tgroup cols='4'>
|
||||
<colspec colname='breakpoint' />
|
||||
<colspec colname='description' />
|
||||
<colspec colname='initrd' />
|
||||
<colspec colname='main' />
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Breakpoints</entry>
|
||||
<entry>Description</entry>
|
||||
<entry>Can be used in the initrd</entry>
|
||||
<entry>Can be used in the main system</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><option>pre-udev</option></entry>
|
||||
<entry>Before starting to process kernel uevents, i.e., before <filename>systemd-udevd.service</filename> starts.</entry>
|
||||
<entry>✓</entry>
|
||||
<entry>✓</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><option>pre-basic</option></entry>
|
||||
<entry>Before leaving early boot and regular services start, i.e., before <filename>basic.target</filename> is reached.</entry>
|
||||
<entry>✓</entry>
|
||||
<entry>✓</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><option>pre-mount</option></entry>
|
||||
<entry>Before the root filesystem is mounted, i.e., before <filename>sysroot.mount</filename> starts.</entry>
|
||||
<entry>✓</entry>
|
||||
<entry>✗</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><option>pre-switch-root</option></entry>
|
||||
<entry>Before switching from the initrd to the real root.</entry>
|
||||
<entry>✓</entry>
|
||||
<entry>✗</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>System Credentials</title>
|
||||
|
||||
@ -108,6 +194,8 @@
|
||||
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd.system-credentials</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
|
||||
|
@ -503,6 +503,17 @@
|
||||
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--system</option></term>
|
||||
<term><option>--user</option></term>
|
||||
|
||||
<listitem><para>When used together with <option>--discover</option> controls whether to search for
|
||||
images installed system-wide or in the user's directories in <varname>$HOME</varname>. If neither
|
||||
switch is specified, will search within both scopes.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="image-policy-open" />
|
||||
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
||||
<xi:include href="standard-options.xml" xpointer="no-legend" />
|
||||
|
@ -3468,37 +3468,43 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
|
||||
<term><varname>LoadCredentialEncrypted=</varname><replaceable>ID</replaceable><optional>:<replaceable>PATH</replaceable></optional></term>
|
||||
|
||||
<listitem><para>Pass a credential to the unit. Credentials are limited-size binary or textual objects
|
||||
that may be passed to unit processes. They are primarily used for passing cryptographic keys (both
|
||||
public and private) or certificates, user account information or identity information from host to
|
||||
services. The data is accessible from the unit's processes via the file system, at a read-only
|
||||
location that (if possible and permitted) is backed by non-swappable memory. The data is only
|
||||
accessible to the user associated with the unit, via the
|
||||
<varname>User=</varname>/<varname>DynamicUser=</varname> settings (as well as the superuser). When
|
||||
available, the location of credentials is exported as the <varname>$CREDENTIALS_DIRECTORY</varname>
|
||||
environment variable to the unit's processes.</para>
|
||||
that may be passed to unit processes. They are primarily intended for passing cryptographic keys
|
||||
(both public and private) or certificates, user account information or identity information from host
|
||||
to services, but can be freely used to pass any kind of limited-size information to a service. The
|
||||
data is accessible from the unit's processes via the file system, at a read-only location that (if
|
||||
possible and permitted) is backed by non-swappable memory. The data is only accessible to the user
|
||||
associated with the unit, via the <varname>User=</varname>/<varname>DynamicUser=</varname> settings
|
||||
(as well as the superuser). When available, the location of credentials is exported as the
|
||||
<varname>$CREDENTIALS_DIRECTORY</varname> environment variable to the unit's processes.</para>
|
||||
|
||||
<para>The <varname>LoadCredential=</varname> setting takes a textual ID to use as name for a
|
||||
credential plus a file system path, separated by a colon. The ID must be a short ASCII string
|
||||
suitable as filename in the filesystem, and may be chosen freely by the user. If the specified path
|
||||
is absolute it is opened as regular file and the credential data is read from it. If the absolute
|
||||
path refers to an <constant>AF_UNIX</constant> stream socket in the file system a connection is made
|
||||
to it (only once at unit start-up) and the credential data read from the connection, providing an
|
||||
to it (once at process invocation) and the credential data read from the connection, providing an
|
||||
easy IPC integration point for dynamically transferring credentials from other services.</para>
|
||||
|
||||
<para>If the specified path is not absolute and itself qualifies as valid credential identifier it is
|
||||
attempted to find a credential that the service manager itself received under the specified name —
|
||||
which may be used to propagate credentials from an invoking environment (e.g. a container manager
|
||||
that invoked the service manager) into a service. If no matching system credential is found, the
|
||||
directories <filename>/etc/credstore/</filename>, <filename>/run/credstore/</filename> and
|
||||
<filename>/usr/lib/credstore/</filename> are searched for files under the credential's name — which
|
||||
hence are recommended locations for credential data on disk. If
|
||||
that invoked the service manager) into a service. If no matching passed credential is found, the
|
||||
system service manager will search the directories <filename>/etc/credstore/</filename>,
|
||||
<filename>/run/credstore/</filename> and <filename>/usr/lib/credstore/</filename> for files under the
|
||||
credential's name — which hence are recommended locations for credential data on disk. If
|
||||
<varname>LoadCredentialEncrypted=</varname> is used <filename>/run/credstore.encrypted/</filename>,
|
||||
<filename>/etc/credstore.encrypted/</filename>, and
|
||||
<filename>/usr/lib/credstore.encrypted/</filename> are searched as well.</para>
|
||||
<filename>/usr/lib/credstore.encrypted/</filename> are searched as well. The per-user service manager
|
||||
will search <filename>$XDG_CONFIG_HOME/credstore/</filename>,
|
||||
<filename>$XDG_RUNTIME_DIR/credstore/</filename>, <filename>$HOME/.local/lib/credstore/</filename>
|
||||
(and the counterparts ending with <filename>…/credstore.encrypted/</filename>) instead. The
|
||||
<citerefentry><refentrytitle>systemd-path</refentrytitle><manvolnum>1</manvolnum></citerefentry> tool
|
||||
may be used to query the precise credential store search path.</para>
|
||||
|
||||
<para>If the file system path is omitted it is chosen identical to the credential name, i.e. this is
|
||||
a terse way to declare credentials to inherit from the service manager into a service. This option
|
||||
may be used multiple times, each time defining an additional credential to pass to the unit.</para>
|
||||
a terse way to declare credentials to inherit from the service manager or credstore directories into
|
||||
a service. This option may be used multiple times, each time defining an additional credential to
|
||||
pass to the unit.</para>
|
||||
|
||||
<para>Note that if the path is not specified or a valid credential identifier is given, i.e.
|
||||
in the above two cases, a missing credential is not considered fatal.</para>
|
||||
|
@ -64,6 +64,10 @@
|
||||
override existing definitions. For tests, generators may be called with just one argument; the generator
|
||||
should assume that all three paths are the same in that case.</para>
|
||||
|
||||
<para>Generators executed by the system manager are invoked in a sandbox with a private writable
|
||||
<filename>/tmp/</filename> directory and where most of the file system is read-only except for the
|
||||
generator output directories.</para>
|
||||
|
||||
<para>Directory paths for generator output differ by priority: <filename>…/generator.early</filename> has
|
||||
priority higher than the admin configuration in <filename>/etc/</filename>, while
|
||||
<filename>…/generator</filename> has lower priority than <filename>/etc/</filename> but higher than
|
||||
|
@ -672,7 +672,7 @@
|
||||
|
||||
<listitem><para>Specifies what anchor point <varname>Path=</varname> should be relative to. Takes one
|
||||
of <constant>root</constant>, <constant>esp</constant>, <constant>xbootldr</constant>,
|
||||
<constant>boot</constant> or <constant>directory</constant>. If unspecified, defaults to
|
||||
<constant>boot</constant> or <constant>explicit</constant>. If unspecified, defaults to
|
||||
<constant>root</constant>.</para>
|
||||
|
||||
<para>If set to <constant>root</constant>, <constant>esp</constant>, <constant>xbootldr</constant>,
|
||||
|
@ -93,7 +93,7 @@
|
||||
XML file.
|
||||
This may be used by software centers (such as GNOME Software or KDE Discover) to present rich
|
||||
metadata about this feature.
|
||||
This includes display names, chagnelogs, icons, and more.
|
||||
This includes display names, changelogs, icons, and more.
|
||||
This setting supports specifier expansion; see below for details on supported specifiers.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
@ -177,7 +177,7 @@ Enabled=false
|
||||
</programlisting></para>
|
||||
|
||||
<para>The above defines the <literal>devel</literal> feature, and disables it by default.
|
||||
Now let's a define a transfer that's associated with this feature:</para>
|
||||
Now let's define a transfer that's associated with this feature:</para>
|
||||
|
||||
<para><programlisting># /usr/lib/sysupdate.d/50-devel.transfer
|
||||
[Transfer]
|
||||
|
@ -10,7 +10,7 @@ sudo systemd-cryptsetup attach mytest /dev/sdXn - tpm2-device=auto
|
||||
# If that worked, let's now add the same line persistently to /etc/crypttab,
|
||||
# for the future. We don't want to use the (unstable) /dev/sdX name, so let's
|
||||
# figure out a stable link:
|
||||
udevadm info -q -r symlink /dev/sdXn
|
||||
udevadm info -q symlink -r /dev/sdXn
|
||||
|
||||
# Now add the line using the by-uuid symlink to /etc/crypttab:
|
||||
sudo bash -c 'echo "mytest /dev/disk/by-uuid/... - tpm2-device=auto" >>/etc/crypttab'
|
||||
|
@ -26,7 +26,7 @@ sudo systemd-cryptsetup attach mytest /dev/sdXn - pkcs11-uri=auto
|
||||
# If that worked, let's now add the same line persistently to /etc/crypttab,
|
||||
# for the future. We don't want to use the (unstable) /dev/sdX name, so let's
|
||||
# figure out a stable link:
|
||||
udevadm info -q -r symlink /dev/sdXn
|
||||
udevadm info -q symlink -r /dev/sdXn
|
||||
|
||||
# Now add the line using the by-uuid symlink to /etc/crypttab:
|
||||
sudo bash -c 'echo "mytest /dev/disk/by-uuid/... - pkcs11-uri=auto" >>/etc/crypttab'
|
||||
|
@ -1,5 +1,6 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -e
|
||||
|
||||
if command -v flatpak-spawn >/dev/null; then
|
||||
SPAWN=(flatpak-spawn --host)
|
||||
@ -7,7 +8,7 @@ else
|
||||
SPAWN=()
|
||||
fi
|
||||
|
||||
MKOSI_CONFIG="$("${SPAWN[@]}" --host mkosi --json summary | jq -r .Images[-1])"
|
||||
MKOSI_CONFIG="$("${SPAWN[@]}" mkosi --json summary | jq -r .Images[-1])"
|
||||
DISTRIBUTION="$(jq -r .Distribution <<< "$MKOSI_CONFIG")"
|
||||
RELEASE="$(jq -r .Release <<< "$MKOSI_CONFIG")"
|
||||
ARCH="$(jq -r .Architecture <<< "$MKOSI_CONFIG")"
|
||||
|
@ -29,6 +29,7 @@ RepartDirectories=mkosi.repart
|
||||
OutputDirectory=build/mkosi.output
|
||||
|
||||
[Build]
|
||||
ToolsTree=default
|
||||
BuildDirectory=build/mkosi.builddir
|
||||
CacheDirectory=build/mkosi.cache
|
||||
BuildSourcesEphemeral=yes
|
||||
@ -130,7 +131,7 @@ Packages=
|
||||
zsh
|
||||
zstd
|
||||
|
||||
[Host]
|
||||
[Runtime]
|
||||
Credentials=
|
||||
journal.storage=persistent
|
||||
tty.serial.hvc0.agetty.autologin=root
|
||||
|
@ -3,6 +3,7 @@
|
||||
[Build]
|
||||
ToolsTreePackages=
|
||||
gcc
|
||||
gdb
|
||||
gperf
|
||||
lcov
|
||||
llvm
|
||||
|
7
mkosi.conf.d/05-tools/mkosi.conf.d/centos.conf
Normal file
7
mkosi.conf.d/05-tools/mkosi.conf.d/centos.conf
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
[Match]
|
||||
ToolsTreeDistribution=centos
|
||||
|
||||
[Build]
|
||||
ToolsTreeRepositories=epel,epel-next
|
@ -5,6 +5,7 @@ ToolsTreeDistribution=opensuse
|
||||
|
||||
[Build]
|
||||
ToolsTreePackages=
|
||||
libz1
|
||||
gh
|
||||
mypy
|
||||
pkgconfig(blkid)
|
||||
|
7
mkosi.conf.d/20-extra-search-path.conf
Normal file
7
mkosi.conf.d/20-extra-search-path.conf
Normal file
@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
[Match]
|
||||
PathExists=build/
|
||||
|
||||
[Build]
|
||||
ExtraSearchPaths=build/
|
9
po/fi.po
9
po/fi.po
@ -8,8 +8,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-11-28 18:16+0900\n"
|
||||
"PO-Revision-Date: 2024-11-20 19:13+0000\n"
|
||||
"Last-Translator: Jiri Grönroos <jiri.gronroos@iki.fi>\n"
|
||||
"PO-Revision-Date: 2024-12-20 15:38+0000\n"
|
||||
"Last-Translator: Ricky Tigg <ricky.tigg@gmail.com>\n"
|
||||
"Language-Team: Finnish <https://translate.fedoraproject.org/projects/systemd/"
|
||||
"main/fi/>\n"
|
||||
"Language: fi\n"
|
||||
@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.8.2\n"
|
||||
"X-Generator: Weblate 5.9.1\n"
|
||||
|
||||
#: src/core/org.freedesktop.systemd1.policy.in:22
|
||||
msgid "Send passphrase back to system"
|
||||
@ -1176,9 +1176,8 @@ msgid "Manage optional features"
|
||||
msgstr "Hallitse valinnaisia ominaisuuksia"
|
||||
|
||||
#: src/sysupdate/org.freedesktop.sysupdate1.policy:76
|
||||
#, fuzzy
|
||||
msgid "Authentication is required to manage optional features."
|
||||
msgstr "Todennus vaaditaan valinnaisten ominaisuuksien hallintaan"
|
||||
msgstr "Todennus vaaditaan valinnaisten ominaisuuksien hallintaan."
|
||||
|
||||
#: src/timedate/org.freedesktop.timedate1.policy:22
|
||||
msgid "Set system time"
|
||||
|
@ -67,7 +67,7 @@ _systemd_analyze() {
|
||||
)
|
||||
|
||||
local -A VERBS=(
|
||||
[STANDALONE]='time blame unit-files unit-paths exit-status compare-versions calendar timestamp timespan pcrs srk has-tpm2 smbios11'
|
||||
[STANDALONE]='time blame unit-files unit-paths exit-status compare-versions calendar timestamp timespan pcrs srk has-tpm2 smbios11 chid'
|
||||
[CRITICAL_CHAIN]='critical-chain'
|
||||
[DOT]='dot'
|
||||
[DUMP]='dump'
|
||||
|
@ -29,6 +29,8 @@ _systemd_dissect() {
|
||||
local cur=${COMP_WORDS[COMP_CWORD]} prev_1=${COMP_WORDS[COMP_CWORD-1]} prev_2=${COMP_WORDS[COMP_CWORD-2]} words cword
|
||||
local -A OPTS=(
|
||||
[STANDALONE]='-h --help --version
|
||||
--user
|
||||
--system
|
||||
--discover
|
||||
--no-pager
|
||||
--no-legend
|
||||
|
343
src/analyze/analyze-chid.c
Normal file
343
src/analyze/analyze-chid.c
Normal file
@ -0,0 +1,343 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "analyze.h"
|
||||
#include "analyze-chid.h"
|
||||
#include "chid-fundamental.h"
|
||||
#include "efi-api.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "format-table.h"
|
||||
#include "parse-util.h"
|
||||
#include "strv.h"
|
||||
#include "utf8.h"
|
||||
#include "virt.h"
|
||||
|
||||
static int parse_chid_type(const char *s, size_t *ret) {
|
||||
unsigned u;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
r = safe_atou(s, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (u >= CHID_TYPES_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
if (ret)
|
||||
*ret = u;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *const chid_smbios_friendly[_CHID_SMBIOS_FIELDS_MAX] = {
|
||||
[CHID_SMBIOS_MANUFACTURER] = "manufacturer",
|
||||
[CHID_SMBIOS_FAMILY] = "family",
|
||||
[CHID_SMBIOS_PRODUCT_NAME] = "product-name",
|
||||
[CHID_SMBIOS_PRODUCT_SKU] = "product-sku",
|
||||
[CHID_SMBIOS_BASEBOARD_MANUFACTURER] = "baseboard-manufacturer",
|
||||
[CHID_SMBIOS_BASEBOARD_PRODUCT] = "baseboard-product",
|
||||
[CHID_SMBIOS_BIOS_VENDOR] = "bios-vendor",
|
||||
[CHID_SMBIOS_BIOS_VERSION] = "bios-version",
|
||||
[CHID_SMBIOS_BIOS_MAJOR] = "bios-major",
|
||||
[CHID_SMBIOS_BIOS_MINOR] = "bios-minor",
|
||||
[CHID_SMBIOS_ENCLOSURE_TYPE] = "enclosure-type",
|
||||
};
|
||||
|
||||
static const char chid_smbios_fields_char[_CHID_SMBIOS_FIELDS_MAX] = {
|
||||
[CHID_SMBIOS_MANUFACTURER] = 'M',
|
||||
[CHID_SMBIOS_FAMILY] = 'F',
|
||||
[CHID_SMBIOS_PRODUCT_NAME] = 'P',
|
||||
[CHID_SMBIOS_PRODUCT_SKU] = 'S',
|
||||
[CHID_SMBIOS_BASEBOARD_MANUFACTURER] = 'm',
|
||||
[CHID_SMBIOS_BASEBOARD_PRODUCT] = 'p',
|
||||
[CHID_SMBIOS_BIOS_VENDOR] = 'B',
|
||||
[CHID_SMBIOS_BIOS_VERSION] = 'v',
|
||||
[CHID_SMBIOS_BIOS_MAJOR] = 'R',
|
||||
[CHID_SMBIOS_BIOS_MINOR] = 'r',
|
||||
[CHID_SMBIOS_ENCLOSURE_TYPE] = 'e',
|
||||
};
|
||||
|
||||
static char *chid_smbios_fields_string(uint32_t combination) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
|
||||
for (ChidSmbiosFields f = 0; f < _CHID_SMBIOS_FIELDS_MAX; f++) {
|
||||
char c;
|
||||
|
||||
c = (combination & (UINT32_C(1) << f)) ? chid_smbios_fields_char[f] : '-';
|
||||
|
||||
if (!strextend(&s, CHAR_TO_STR(c)))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return TAKE_PTR(s);
|
||||
}
|
||||
|
||||
static int add_chid(Table *table, const EFI_GUID guids[static CHID_TYPES_MAX], size_t t) {
|
||||
int r;
|
||||
|
||||
assert(table);
|
||||
assert(guids);
|
||||
assert(t < CHID_TYPES_MAX);
|
||||
|
||||
sd_id128_t id = efi_guid_to_id128(guids + t);
|
||||
|
||||
if (sd_id128_is_null(id))
|
||||
return 0;
|
||||
|
||||
_cleanup_free_ char *flags = chid_smbios_fields_string(chid_smbios_table[t]);
|
||||
if (!flags)
|
||||
return log_oom();
|
||||
|
||||
r = table_add_many(table,
|
||||
TABLE_UINT, (unsigned) t,
|
||||
TABLE_STRING, flags,
|
||||
TABLE_UUID, id);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void smbios_fields_free(char16_t *(*fields)[_CHID_SMBIOS_FIELDS_MAX]) {
|
||||
assert(fields);
|
||||
|
||||
FOREACH_ARRAY(i, *fields, _CHID_SMBIOS_FIELDS_MAX)
|
||||
free(*i);
|
||||
}
|
||||
|
||||
static int smbios_fields_acquire(char16_t *fields[static _CHID_SMBIOS_FIELDS_MAX]) {
|
||||
|
||||
static const char *const smbios_files[_CHID_SMBIOS_FIELDS_MAX] = {
|
||||
[CHID_SMBIOS_MANUFACTURER] = "sys_vendor",
|
||||
[CHID_SMBIOS_FAMILY] = "product_family",
|
||||
[CHID_SMBIOS_PRODUCT_NAME] = "product_name",
|
||||
[CHID_SMBIOS_PRODUCT_SKU] = "product_sku",
|
||||
[CHID_SMBIOS_BASEBOARD_MANUFACTURER] = "board_vendor",
|
||||
[CHID_SMBIOS_BASEBOARD_PRODUCT] = "board_name",
|
||||
[CHID_SMBIOS_BIOS_VENDOR] = "bios_vendor",
|
||||
[CHID_SMBIOS_BIOS_VERSION] = "bios_version",
|
||||
[CHID_SMBIOS_BIOS_MAJOR] = "bios_release",
|
||||
[CHID_SMBIOS_BIOS_MINOR] = "bios_release",
|
||||
[CHID_SMBIOS_ENCLOSURE_TYPE] = "chassis_type",
|
||||
};
|
||||
|
||||
int r;
|
||||
|
||||
_cleanup_close_ int smbios_fd = open("/sys/class/dmi/id", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (smbios_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open SMBIOS sysfs object: %m");
|
||||
|
||||
for (ChidSmbiosFields f = 0; f < _CHID_SMBIOS_FIELDS_MAX; f++) {
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
size_t size;
|
||||
|
||||
/* According to the CHID spec we should not generate CHIDs for SMBIOS fields that aren't set
|
||||
* or are set to an empty string. Hence leave them NULL here. */
|
||||
|
||||
if (!smbios_files[f])
|
||||
continue;
|
||||
|
||||
r = read_virtual_file_at(smbios_fd, smbios_files[f], SIZE_MAX, &buf, &size);
|
||||
if (r == -ENOENT) {
|
||||
log_debug_errno(r, "SMBIOS field '%s' not set, skipping.", smbios_files[f]);
|
||||
continue;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read SMBIOS field '%s': %m", smbios_files[f]);
|
||||
|
||||
if (size == 0 || (size == 1 && buf[0] == '\n')) {
|
||||
log_debug("SMBIOS field '%s' is empty, skipping.", smbios_files[f]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buf[size-1] != '\n')
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected SMBIOS field '%s' to end in newline, but it doesn't, refusing.", smbios_files[f]);
|
||||
|
||||
buf[size-1] = 0;
|
||||
size--;
|
||||
|
||||
switch (f) {
|
||||
|
||||
case CHID_SMBIOS_BIOS_MAJOR:
|
||||
case CHID_SMBIOS_BIOS_MINOR: {
|
||||
/* The kernel exposes this a string <major>.<minor>, split them apart again. */
|
||||
char *dot = memchr(buf, '.', size);
|
||||
if (!dot)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "BIOS release field '%s' contains no dot?", smbios_files[f]);
|
||||
|
||||
const char *p;
|
||||
if (f == CHID_SMBIOS_BIOS_MAJOR) {
|
||||
*dot = 0;
|
||||
p = buf;
|
||||
} else {
|
||||
assert(f == CHID_SMBIOS_BIOS_MINOR);
|
||||
p = dot + 1;
|
||||
}
|
||||
|
||||
/* The kernel exports the enclosure in decimal, we need it in hex (zero left-padded) */
|
||||
|
||||
uint8_t u;
|
||||
r = safe_atou8(p, &u);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse BIOS release: %s", p);
|
||||
|
||||
buf = mfree(buf);
|
||||
if (asprintf(&buf, "%02x", u) < 0)
|
||||
return log_oom();
|
||||
|
||||
size = strlen(buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case CHID_SMBIOS_ENCLOSURE_TYPE: {
|
||||
/* The kernel exports the enclosure in decimal, we need it in hex (no padding!) */
|
||||
|
||||
uint8_t u;
|
||||
r = safe_atou8(buf, &u);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse enclosure type: %s", buf);
|
||||
|
||||
buf = mfree(buf);
|
||||
if (u == 0)
|
||||
buf = strdup(""); /* zero is mapped to empty string */
|
||||
else
|
||||
(void) asprintf(&buf, "%x", u);
|
||||
if (!buf)
|
||||
return log_oom();
|
||||
|
||||
size = strlen(buf);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
fields[f] = utf8_to_utf16(buf, size);
|
||||
if (!fields[f])
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int verb_chid(int argc, char *argv[], void *userdata) {
|
||||
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
int r;
|
||||
|
||||
if (detect_container() > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Container environments do not have SMBIOS.");
|
||||
|
||||
table = table_new("type", "input", "chid");
|
||||
if (!table)
|
||||
return log_oom();
|
||||
|
||||
(void) table_set_align_percent(table, table_get_cell(table, 0, 0), 100);
|
||||
(void) table_set_align_percent(table, table_get_cell(table, 0, 1), 50);
|
||||
|
||||
_cleanup_(smbios_fields_free) char16_t* smbios_fields[_CHID_SMBIOS_FIELDS_MAX] = {};
|
||||
r = smbios_fields_acquire(smbios_fields);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
EFI_GUID chids[CHID_TYPES_MAX] = {};
|
||||
chid_calculate((const char16_t* const*) smbios_fields, chids);
|
||||
|
||||
if (strv_isempty(strv_skip(argv, 1)))
|
||||
for (size_t t = 0; t < CHID_TYPES_MAX; t++) {
|
||||
r = add_chid(table, chids, t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
else {
|
||||
STRV_FOREACH(as, strv_skip(argv, 1)) {
|
||||
size_t t;
|
||||
r = parse_chid_type(*as, &t);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to pare CHID type: %s", *as);
|
||||
|
||||
r = add_chid(table, chids, t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
(void) table_set_sort(table, (size_t) 0);
|
||||
}
|
||||
|
||||
r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to output table: %m");
|
||||
|
||||
if (!sd_json_format_enabled(arg_json_format_flags)) {
|
||||
_cleanup_free_ char *legend = NULL;
|
||||
bool separator = false;
|
||||
size_t w = 0;
|
||||
|
||||
legend = strjoin(ansi_grey(), "LEGEND: ", ansi_normal());
|
||||
if (!legend)
|
||||
return log_oom();
|
||||
|
||||
for (ChidSmbiosFields f = 0; f < _CHID_SMBIOS_FIELDS_MAX; f++) {
|
||||
_cleanup_free_ char *c = NULL;
|
||||
|
||||
if (smbios_fields[f]) {
|
||||
_cleanup_free_ char *u = NULL;
|
||||
|
||||
u = utf16_to_utf8(smbios_fields[f], SIZE_MAX);
|
||||
if (!u)
|
||||
return log_oom();
|
||||
|
||||
c = cescape(u);
|
||||
if (!c)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (!strextend(&legend,
|
||||
ansi_grey(),
|
||||
separator ? " " : "",
|
||||
separator ? special_glyph(SPECIAL_GLYPH_HORIZONTAL_DOTTED) : "",
|
||||
separator ? " " : "",
|
||||
ansi_normal(),
|
||||
CHAR_TO_STR(chid_smbios_fields_char[f]),
|
||||
ansi_grey(),
|
||||
" ",
|
||||
special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
|
||||
" ",
|
||||
ansi_normal(),
|
||||
chid_smbios_friendly[f],
|
||||
ansi_grey(),
|
||||
" (",
|
||||
c ? ansi_highlight() : ansi_grey(),
|
||||
strna(c),
|
||||
ansi_grey(),
|
||||
")",
|
||||
ansi_normal()))
|
||||
return log_oom();
|
||||
|
||||
w += separator * 3 +
|
||||
4 +
|
||||
utf8_console_width(chid_smbios_friendly[f]) +
|
||||
2 +
|
||||
utf8_console_width(strna(c)) +
|
||||
1;
|
||||
|
||||
if (w > 79) {
|
||||
if (!strextend(&legend, "\n "))
|
||||
return log_oom();
|
||||
|
||||
separator = false;
|
||||
w = 8;
|
||||
} else
|
||||
separator = true;
|
||||
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
puts(legend);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
4
src/analyze/analyze-chid.h
Normal file
4
src/analyze/analyze-chid.h
Normal file
@ -0,0 +1,4 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
int verb_chid(int argc, char *argv[], void *userdata);
|
@ -18,6 +18,7 @@
|
||||
#include "analyze-calendar.h"
|
||||
#include "analyze-capability.h"
|
||||
#include "analyze-cat-config.h"
|
||||
#include "analyze-chid.h"
|
||||
#include "analyze-compare-versions.h"
|
||||
#include "analyze-condition.h"
|
||||
#include "analyze-critical-chain.h"
|
||||
@ -219,6 +220,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" filesystems [NAME...] List known filesystems\n"
|
||||
" architectures [NAME...] List known architectures\n"
|
||||
" smbios11 List strings passed via SMBIOS Type #11\n"
|
||||
" chid List local CHIDs\n"
|
||||
"\n%3$sExpression Evaluation:%4$s\n"
|
||||
" condition CONDITION... Evaluate conditions and asserts\n"
|
||||
" compare-versions VERSION1 [OP] VERSION2\n"
|
||||
@ -593,10 +595,6 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Option --offline= requires one or more units to perform a security review.");
|
||||
|
||||
if (sd_json_format_enabled(arg_json_format_flags) && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf", "plot", "fdstore", "pcrs", "architectures", "capability", "exit-status"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Option --json= is only supported for security, inspect-elf, plot, fdstore, pcrs, architectures, capability, exit-status right now.");
|
||||
|
||||
if (arg_threshold != 100 && !streq_ptr(argv[optind], "security"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Option --threshold= is only supported for security right now.");
|
||||
@ -631,10 +629,6 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (streq_ptr(argv[optind], "condition") && arg_unit && optind < argc - 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No conditions can be passed if --unit= is used.");
|
||||
|
||||
if ((!arg_legend && !STRPTR_IN_SET(argv[optind], "plot", "architectures")) ||
|
||||
(streq_ptr(argv[optind], "plot") && !arg_legend && !arg_table && !sd_json_format_enabled(arg_json_format_flags)))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --no-legend is only supported for plot with either --table or --json=.");
|
||||
|
||||
if (arg_table && !streq_ptr(argv[optind], "plot"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --table is only supported for plot right now.");
|
||||
|
||||
@ -691,6 +685,7 @@ static int run(int argc, char *argv[]) {
|
||||
{ "srk", VERB_ANY, 1, 0, verb_srk },
|
||||
{ "architectures", VERB_ANY, VERB_ANY, 0, verb_architectures },
|
||||
{ "smbios11", VERB_ANY, 1, 0, verb_smbios11 },
|
||||
{ "chid", VERB_ANY, VERB_ANY, 0, verb_chid },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -6,6 +6,7 @@ systemd_analyze_sources = files(
|
||||
'analyze-calendar.c',
|
||||
'analyze-capability.c',
|
||||
'analyze-cat-config.c',
|
||||
'analyze-chid.c',
|
||||
'analyze-compare-versions.c',
|
||||
'analyze-condition.c',
|
||||
'analyze-critical-chain.c',
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
#define AUDIT_SESSION_INVALID UINT32_MAX
|
||||
|
||||
int audit_session_from_pid(const PidRef *pid, uint32_t *id);
|
||||
int audit_loginuid_from_pid(const PidRef *pid, uid_t *uid);
|
||||
int audit_session_from_pid(const PidRef *pid, uint32_t *ret_id);
|
||||
int audit_loginuid_from_pid(const PidRef *pid, uid_t *ret_uid);
|
||||
|
||||
bool use_audit(void);
|
||||
|
||||
|
@ -8,8 +8,9 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "capability-util.h"
|
||||
#include "cap-list.h"
|
||||
#include "capability-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "log.h"
|
||||
#include "logarithm.h"
|
||||
@ -17,6 +18,8 @@
|
||||
#include "missing_prctl.h"
|
||||
#include "missing_threads.h"
|
||||
#include "parse-util.h"
|
||||
#include "pidref.h"
|
||||
#include "stat-util.h"
|
||||
#include "user-util.h"
|
||||
|
||||
int have_effective_cap(int value) {
|
||||
@ -607,3 +610,78 @@ int capability_get_ambient(uint64_t *ret) {
|
||||
*ret = a;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int pidref_get_capability(const PidRef *pidref, CapabilityQuintet *ret) {
|
||||
int r;
|
||||
|
||||
if (!pidref_is_set(pidref))
|
||||
return -ESRCH;
|
||||
if (pidref_is_remote(pidref))
|
||||
return -EREMOTE;
|
||||
|
||||
const char *path = procfs_file_alloca(pidref->pid, "status");
|
||||
_cleanup_fclose_ FILE *f = fopen(path, "re");
|
||||
if (!f) {
|
||||
if (errno == ENOENT && proc_mounted() == 0)
|
||||
return -ENOSYS;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
CapabilityQuintet q = CAPABILITY_QUINTET_NULL;
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
|
||||
r = read_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
static const struct {
|
||||
const char *field;
|
||||
size_t offset;
|
||||
} fields[] = {
|
||||
{ "CapBnd:", offsetof(CapabilityQuintet, bounding) },
|
||||
{ "CapInh:", offsetof(CapabilityQuintet, inheritable) },
|
||||
{ "CapPrm:", offsetof(CapabilityQuintet, permitted) },
|
||||
{ "CapEff:", offsetof(CapabilityQuintet, effective) },
|
||||
{ "CapAmb:", offsetof(CapabilityQuintet, ambient) },
|
||||
};
|
||||
|
||||
FOREACH_ELEMENT(i, fields) {
|
||||
|
||||
const char *p = first_word(line, i->field);
|
||||
if (!p)
|
||||
continue;
|
||||
|
||||
uint64_t *v = (uint64_t*) ((uint8_t*) &q + i->offset);
|
||||
|
||||
if (*v != CAP_MASK_UNSET)
|
||||
return -EBADMSG;
|
||||
|
||||
r = safe_atoux64(p, v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (*v == CAP_MASK_UNSET)
|
||||
return -EBADMSG;
|
||||
}
|
||||
}
|
||||
|
||||
if (q.effective == CAP_MASK_UNSET ||
|
||||
q.inheritable == CAP_MASK_UNSET ||
|
||||
q.permitted == CAP_MASK_UNSET ||
|
||||
q.effective == CAP_MASK_UNSET ||
|
||||
q.ambient == CAP_MASK_UNSET)
|
||||
return -EBADMSG;
|
||||
|
||||
r = pidref_verify(pidref);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = q;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "macro.h"
|
||||
#include "missing_capability.h"
|
||||
#include "pidref.h"
|
||||
|
||||
/* Special marker used when storing a capabilities mask as "unset" */
|
||||
#define CAP_MASK_UNSET UINT64_MAX
|
||||
@ -66,14 +67,18 @@ typedef struct CapabilityQuintet {
|
||||
|
||||
assert_cc(CAP_LAST_CAP < 64);
|
||||
|
||||
#define CAPABILITY_QUINTET_NULL { CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET }
|
||||
#define CAPABILITY_QUINTET_NULL (CapabilityQuintet) { CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET }
|
||||
|
||||
static inline bool capability_is_set(uint64_t v) {
|
||||
return v != CAP_MASK_UNSET;
|
||||
}
|
||||
|
||||
static inline bool capability_quintet_is_set(const CapabilityQuintet *q) {
|
||||
return q->effective != CAP_MASK_UNSET ||
|
||||
q->bounding != CAP_MASK_UNSET ||
|
||||
q->inheritable != CAP_MASK_UNSET ||
|
||||
q->permitted != CAP_MASK_UNSET ||
|
||||
q->ambient != CAP_MASK_UNSET;
|
||||
return capability_is_set(q->effective) ||
|
||||
capability_is_set(q->bounding) ||
|
||||
capability_is_set(q->inheritable) ||
|
||||
capability_is_set(q->permitted) ||
|
||||
capability_is_set(q->ambient);
|
||||
}
|
||||
|
||||
/* Mangles the specified caps quintet taking the current bounding set into account:
|
||||
@ -84,3 +89,5 @@ bool capability_quintet_mangle(CapabilityQuintet *q);
|
||||
int capability_quintet_enforce(const CapabilityQuintet *q);
|
||||
|
||||
int capability_get_ambient(uint64_t *ret);
|
||||
|
||||
int pidref_get_capability(const PidRef *pidref, CapabilityQuintet *ret);
|
||||
|
@ -1402,6 +1402,26 @@ int cg_pid_get_session(pid_t pid, char **ret_session) {
|
||||
return cg_path_get_session(cgroup, ret_session);
|
||||
}
|
||||
|
||||
int cg_pidref_get_session(const PidRef *pidref, char **ret) {
|
||||
int r;
|
||||
|
||||
if (!pidref_is_set(pidref))
|
||||
return -ESRCH;
|
||||
|
||||
_cleanup_free_ char *session = NULL;
|
||||
r = cg_pid_get_session(pidref->pid, &session);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = pidref_verify(pidref);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cg_path_get_owner_uid(const char *path, uid_t *ret_uid) {
|
||||
_cleanup_free_ char *slice = NULL;
|
||||
char *start, *end;
|
||||
@ -1439,6 +1459,27 @@ int cg_pid_get_owner_uid(pid_t pid, uid_t *ret_uid) {
|
||||
return cg_path_get_owner_uid(cgroup, ret_uid);
|
||||
}
|
||||
|
||||
int cg_pidref_get_owner_uid(const PidRef *pidref, uid_t *ret) {
|
||||
int r;
|
||||
|
||||
if (!pidref_is_set(pidref))
|
||||
return -ESRCH;
|
||||
|
||||
uid_t uid;
|
||||
r = cg_pid_get_owner_uid(pidref->pid, &uid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = pidref_verify(pidref);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = uid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cg_path_get_slice(const char *p, char **ret_slice) {
|
||||
const char *e = NULL;
|
||||
|
||||
|
@ -286,7 +286,9 @@ int cg_shift_path(const char *cgroup, const char *cached_root, const char **ret_
|
||||
int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **ret_cgroup);
|
||||
|
||||
int cg_pid_get_session(pid_t pid, char **ret_session);
|
||||
int cg_pidref_get_session(const PidRef *pidref, char **ret_session);
|
||||
int cg_pid_get_owner_uid(pid_t pid, uid_t *ret_uid);
|
||||
int cg_pidref_get_owner_uid(const PidRef *pidref, uid_t *ret_uid);
|
||||
int cg_pid_get_unit(pid_t pid, char **ret_unit);
|
||||
int cg_pidref_get_unit(const PidRef *pidref, char **ret);
|
||||
int cg_pid_get_user_unit(pid_t pid, char **ret_unit);
|
||||
|
@ -500,22 +500,6 @@ int pidref_is_kernel_thread(const PidRef *pid) {
|
||||
return result;
|
||||
}
|
||||
|
||||
int get_process_capeff(pid_t pid, char **ret) {
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(pid >= 0);
|
||||
assert(ret);
|
||||
|
||||
p = procfs_file_alloca(pid, "status");
|
||||
|
||||
r = get_proc_field(p, "CapEff", WHITESPACE, ret);
|
||||
if (r == -ENOENT)
|
||||
return -ESRCH;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int get_process_link_contents(pid_t pid, const char *proc_file, char **ret) {
|
||||
const char *p;
|
||||
int r;
|
||||
|
@ -50,7 +50,6 @@ int get_process_exe(pid_t pid, char **ret);
|
||||
int pid_get_uid(pid_t pid, uid_t *ret);
|
||||
int pidref_get_uid(const PidRef *pid, uid_t *ret);
|
||||
int get_process_gid(pid_t pid, gid_t *ret);
|
||||
int get_process_capeff(pid_t pid, char **ret);
|
||||
int get_process_cwd(pid_t pid, char **ret);
|
||||
int get_process_root(pid_t pid, char **ret);
|
||||
int get_process_environ(pid_t pid, char **ret);
|
||||
|
@ -975,6 +975,28 @@ int getpeerpidfd(int fd) {
|
||||
return pidfd;
|
||||
}
|
||||
|
||||
int getpeerpidref(int fd, PidRef *ret) {
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
int pidfd = getpeerpidfd(fd);
|
||||
if (pidfd < 0) {
|
||||
if (!ERRNO_IS_NEG_NOT_SUPPORTED(pidfd) && pidfd != -EINVAL)
|
||||
return pidfd;
|
||||
|
||||
struct ucred ucred;
|
||||
r = getpeercred(fd, &ucred);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return pidref_set_pid(ret, ucred.pid);
|
||||
}
|
||||
|
||||
return pidref_set_pidfd_consume(ret, pidfd);
|
||||
}
|
||||
|
||||
ssize_t send_many_fds_iov_sa(
|
||||
int transport_fd,
|
||||
int *fds_array, size_t n_fds_array,
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "macro.h"
|
||||
#include "missing_network.h"
|
||||
#include "missing_socket.h"
|
||||
#include "pidref.h"
|
||||
#include "sparse-endian.h"
|
||||
|
||||
union sockaddr_union {
|
||||
@ -154,6 +155,7 @@ int getpeercred(int fd, struct ucred *ucred);
|
||||
int getpeersec(int fd, char **ret);
|
||||
int getpeergroups(int fd, gid_t **ret);
|
||||
int getpeerpidfd(int fd);
|
||||
int getpeerpidref(int fd, PidRef *ret);
|
||||
|
||||
ssize_t send_many_fds_iov_sa(
|
||||
int transport_fd,
|
||||
|
@ -761,26 +761,24 @@ bool tty_is_console(const char *tty) {
|
||||
}
|
||||
|
||||
int vtnr_from_tty(const char *tty) {
|
||||
int i, r;
|
||||
int r;
|
||||
|
||||
assert(tty);
|
||||
|
||||
tty = skip_dev_prefix(tty);
|
||||
|
||||
if (!startswith(tty, "tty") )
|
||||
const char *e = startswith(tty, "tty");
|
||||
if (!e)
|
||||
return -EINVAL;
|
||||
|
||||
if (!ascii_isdigit(tty[3]))
|
||||
return -EINVAL;
|
||||
|
||||
r = safe_atoi(tty+3, &i);
|
||||
unsigned u;
|
||||
r = safe_atou(e, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!vtnr_is_valid(u))
|
||||
return -ERANGE;
|
||||
|
||||
if (i < 0 || i > 63)
|
||||
return -EINVAL;
|
||||
|
||||
return i;
|
||||
return (int) u;
|
||||
}
|
||||
|
||||
int resolve_dev_console(char **ret) {
|
||||
|
@ -164,3 +164,7 @@ static inline bool osc_char_is_valid(char c) {
|
||||
* ECMA-48 5th edition, section 8.3.89 */
|
||||
return (unsigned char) c >= 32U && (unsigned char) c < 127;
|
||||
}
|
||||
|
||||
static inline bool vtnr_is_valid(unsigned n) {
|
||||
return n >= 1 && n <= 63;
|
||||
}
|
||||
|
@ -85,7 +85,9 @@ static int device_set_sysfs(Device *d, const char *sysfs) {
|
||||
Unit *u = UNIT(ASSERT_PTR(d));
|
||||
int r;
|
||||
|
||||
if (streq_ptr(d->sysfs, sysfs))
|
||||
assert(sysfs);
|
||||
|
||||
if (path_equal(d->sysfs, sysfs))
|
||||
return 0;
|
||||
|
||||
Hashmap **devices = &u->manager->devices_by_sysfs;
|
||||
@ -332,6 +334,20 @@ static void device_catchup(Unit *u) {
|
||||
Device *d = ASSERT_PTR(DEVICE(u));
|
||||
|
||||
/* Second, let's update the state with the enumerated state */
|
||||
|
||||
/* If Device.found (set from Device.deserialized_found) does not have DEVICE_FOUND_UDEV, and the
|
||||
* device has not been processed by udevd while enumeration, it indicates the unit was never active
|
||||
* before reexecution, hence we can safely drop the flag from Device.enumerated_found. The device
|
||||
* will be set up later when udev finishes processing (see also comment in
|
||||
* device_setup_devlink_unit_one()).
|
||||
*
|
||||
* NB: 💣💣💣 If Device.found already contains udev, i.e. the unit was fully ready before
|
||||
* reexecution, do not unset the flag. Otherwise, e.g. if systemd-udev-trigger.service is started
|
||||
* just before reexec, reload, and so on, devices being reprocessed (carrying ID_PROCESSING=1
|
||||
* property) on enumeration and will enter dead state. See issue #35329. */
|
||||
if (!FLAGS_SET(d->found, DEVICE_FOUND_UDEV) && !d->processed)
|
||||
d->enumerated_found &= ~DEVICE_FOUND_UDEV;
|
||||
|
||||
device_update_found_one(d, d->enumerated_found, _DEVICE_FOUND_MASK);
|
||||
}
|
||||
|
||||
@ -777,8 +793,16 @@ static int device_setup_devlink_unit_one(Manager *m, const char *devlink, Set **
|
||||
assert(ready_units);
|
||||
assert(not_ready_units);
|
||||
|
||||
if (sd_device_new_from_devname(&dev, devlink) >= 0 && device_is_ready(dev))
|
||||
if (sd_device_new_from_devname(&dev, devlink) >= 0 && device_is_ready(dev)) {
|
||||
if (MANAGER_IS_RUNNING(m) && device_is_processed(dev) <= 0)
|
||||
/* The device is being processed by udevd. We will receive relevant uevent for the
|
||||
* device later when completed. Let's ignore the device now. */
|
||||
return 0;
|
||||
|
||||
/* Note, even if the device is being processed by udevd, setup the unit on enumerate.
|
||||
* See also the comments in device_catchup(). */
|
||||
return device_setup_unit(m, dev, devlink, /* main = */ false, ready_units);
|
||||
}
|
||||
|
||||
/* the devlink is already removed or not ready */
|
||||
if (device_by_path(m, devlink, &u) < 0)
|
||||
@ -874,14 +898,15 @@ static int device_setup_extra_units(Manager *m, sd_device *dev, Set **ready_unit
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_setup_units(Manager *m, sd_device *dev, Set **ready_units, Set **not_ready_units) {
|
||||
static int device_setup_units(Manager *m, sd_device *dev, Set **ret_ready_units, Set **ret_not_ready_units) {
|
||||
_cleanup_set_free_ Set *ready_units = NULL, *not_ready_units = NULL;
|
||||
const char *syspath, *devname = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(dev);
|
||||
assert(ready_units);
|
||||
assert(not_ready_units);
|
||||
assert(ret_ready_units);
|
||||
assert(ret_not_ready_units);
|
||||
|
||||
r = sd_device_get_syspath(dev, &syspath);
|
||||
if (r < 0)
|
||||
@ -901,13 +926,13 @@ static int device_setup_units(Manager *m, sd_device *dev, Set **ready_units, Set
|
||||
/* Add the main unit named after the syspath. If this one fails, don't bother with the rest,
|
||||
* as this one shall be the main device unit the others just follow. (Compare with how
|
||||
* device_following() is implemented, see below, which looks for the sysfs device.) */
|
||||
r = device_setup_unit(m, dev, syspath, /* main = */ true, ready_units);
|
||||
r = device_setup_unit(m, dev, syspath, /* main = */ true, &ready_units);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Add an additional unit for the device node */
|
||||
if (sd_device_get_devname(dev, &devname) >= 0)
|
||||
(void) device_setup_unit(m, dev, devname, /* main = */ false, ready_units);
|
||||
(void) device_setup_unit(m, dev, devname, /* main = */ false, &ready_units);
|
||||
|
||||
} else {
|
||||
Unit *u;
|
||||
@ -915,28 +940,30 @@ static int device_setup_units(Manager *m, sd_device *dev, Set **ready_units, Set
|
||||
/* If the device exists but not ready, then save the units and unset udev bits later. */
|
||||
|
||||
if (device_by_path(m, syspath, &u) >= 0) {
|
||||
r = set_ensure_put(not_ready_units, NULL, DEVICE(u));
|
||||
r = set_ensure_put(¬_ready_units, NULL, DEVICE(u));
|
||||
if (r < 0)
|
||||
log_unit_debug_errno(u, r, "Failed to store unit, ignoring: %m");
|
||||
}
|
||||
|
||||
if (sd_device_get_devname(dev, &devname) >= 0 &&
|
||||
device_by_path(m, devname, &u) >= 0) {
|
||||
r = set_ensure_put(not_ready_units, NULL, DEVICE(u));
|
||||
r = set_ensure_put(¬_ready_units, NULL, DEVICE(u));
|
||||
if (r < 0)
|
||||
log_unit_debug_errno(u, r, "Failed to store unit, ignoring: %m");
|
||||
}
|
||||
}
|
||||
|
||||
/* Next, add/update additional .device units point to aliases and symlinks. */
|
||||
(void) device_setup_extra_units(m, dev, ready_units, not_ready_units);
|
||||
(void) device_setup_extra_units(m, dev, &ready_units, ¬_ready_units);
|
||||
|
||||
/* Safety check: no unit should be in ready_units and not_ready_units simultaneously. */
|
||||
Unit *u;
|
||||
SET_FOREACH(u, *not_ready_units)
|
||||
if (set_remove(*ready_units, u))
|
||||
SET_FOREACH(u, not_ready_units)
|
||||
if (set_remove(ready_units, u))
|
||||
log_unit_error(u, "Cannot activate and deactivate the unit simultaneously. Deactivating.");
|
||||
|
||||
*ret_ready_units = TAKE_PTR(ready_units);
|
||||
*ret_not_ready_units = TAKE_PTR(not_ready_units);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1046,13 +1073,32 @@ static void device_enumerate(Manager *m) {
|
||||
|
||||
FOREACH_DEVICE(e, dev) {
|
||||
_cleanup_set_free_ Set *ready_units = NULL, *not_ready_units = NULL;
|
||||
const char *syspath;
|
||||
bool processed;
|
||||
Device *d;
|
||||
|
||||
r = sd_device_get_syspath(dev, &syspath);
|
||||
if (r < 0) {
|
||||
log_device_debug_errno(dev, r, "Failed to get syspath of enumerated device, ignoring: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
r = device_is_processed(dev);
|
||||
if (r < 0)
|
||||
log_device_debug_errno(dev, r, "Failed to check if device is processed by udevd, assuming not: %m");
|
||||
processed = r > 0;
|
||||
|
||||
if (device_setup_units(m, dev, &ready_units, ¬_ready_units) < 0)
|
||||
continue;
|
||||
|
||||
SET_FOREACH(d, ready_units)
|
||||
SET_FOREACH(d, ready_units) {
|
||||
device_update_found_one(d, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
|
||||
|
||||
/* Why we need to check the syspath here? Because the device unit may be generated by
|
||||
* a devlink, and the syspath may be different from the one of the original device. */
|
||||
if (path_equal(d->sysfs, syspath))
|
||||
d->processed = processed;
|
||||
}
|
||||
SET_FOREACH(d, not_ready_units)
|
||||
device_update_found_one(d, DEVICE_NOT_FOUND, DEVICE_FOUND_UDEV);
|
||||
}
|
||||
@ -1097,7 +1143,6 @@ static void device_remove_old_on_move(Manager *m, sd_device *dev) {
|
||||
}
|
||||
|
||||
static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
|
||||
_cleanup_set_free_ Set *ready_units = NULL, *not_ready_units = NULL;
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
sd_device_action_t action;
|
||||
const char *sysfs;
|
||||
@ -1150,6 +1195,7 @@ static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *
|
||||
* change events */
|
||||
ready = device_is_ready(dev);
|
||||
|
||||
_cleanup_set_free_ Set *ready_units = NULL, *not_ready_units = NULL;
|
||||
(void) device_setup_units(m, dev, &ready_units, ¬_ready_units);
|
||||
|
||||
if (action == SD_DEVICE_REMOVE) {
|
||||
|
@ -29,7 +29,9 @@ struct Device {
|
||||
|
||||
DeviceState state, deserialized_state;
|
||||
DeviceFound found, deserialized_found, enumerated_found;
|
||||
|
||||
bool processed; /* Whether udevd has done processing the device, i.e. the device has database and
|
||||
* ID_PROCESSING=1 udev property is not set. This is used only by enumeration and
|
||||
* subsequent catchup process. */
|
||||
bool bind_mounts;
|
||||
|
||||
/* The SYSTEMD_WANTS udev property for this device the last time we saw it */
|
||||
|
@ -117,10 +117,9 @@ int exec_context_put_load_credential(ExecContext *c, const char *id, const char
|
||||
return -ENOMEM;
|
||||
|
||||
r = hashmap_ensure_put(&c->load_credentials, &exec_load_credential_hash_ops, lc->id, lc);
|
||||
if (r < 0) {
|
||||
assert(r != -EEXIST);
|
||||
assert(r != -EEXIST);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
TAKE_PTR(lc);
|
||||
}
|
||||
@ -167,10 +166,9 @@ int exec_context_put_set_credential(
|
||||
return -ENOMEM;
|
||||
|
||||
r = hashmap_ensure_put(&c->set_credentials, &exec_set_credential_hash_ops, sc->id, sc);
|
||||
if (r < 0) {
|
||||
assert(r != -EEXIST);
|
||||
assert(r != -EEXIST);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
TAKE_PTR(sc);
|
||||
}
|
||||
@ -193,19 +191,22 @@ int exec_context_put_import_credential(ExecContext *c, const char *glob, const c
|
||||
|
||||
*ic = (ExecImportCredential) {
|
||||
.glob = strdup(glob),
|
||||
.rename = rename ? strdup(rename) : NULL,
|
||||
};
|
||||
if (!ic->glob || (rename && !ic->rename))
|
||||
if (!ic->glob)
|
||||
return -ENOMEM;
|
||||
if (rename) {
|
||||
ic->rename = strdup(rename);
|
||||
if (!ic->rename)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (ordered_set_contains(c->import_credentials, ic))
|
||||
return 0;
|
||||
|
||||
r = ordered_set_ensure_put(&c->import_credentials, &exec_import_credential_hash_ops, ic);
|
||||
if (r < 0) {
|
||||
assert(r != -EEXIST);
|
||||
assert(r != -EEXIST);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
TAKE_PTR(ic);
|
||||
|
||||
@ -383,30 +384,46 @@ typedef enum CredentialSearchPath {
|
||||
_CREDENTIAL_SEARCH_PATH_INVALID = -EINVAL,
|
||||
} CredentialSearchPath;
|
||||
|
||||
static char** credential_search_path(const ExecParameters *params, CredentialSearchPath path) {
|
||||
static int credential_search_path(const ExecParameters *params, CredentialSearchPath path, char ***ret) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
int r;
|
||||
|
||||
assert(params);
|
||||
assert(path >= 0 && path < _CREDENTIAL_SEARCH_PATH_MAX);
|
||||
assert(ret);
|
||||
|
||||
/* Assemble a search path to find credentials in. For non-encrypted credentials, We'll look in
|
||||
* /etc/credstore/ (and similar directories in /usr/lib/ + /run/). If we're looking for encrypted
|
||||
* credentials, we'll look in /etc/credstore.encrypted/ (and similar dirs). */
|
||||
|
||||
if (IN_SET(path, CREDENTIAL_SEARCH_PATH_ENCRYPTED, CREDENTIAL_SEARCH_PATH_ALL)) {
|
||||
if (strv_extend(&l, params->received_encrypted_credentials_directory) < 0)
|
||||
return NULL;
|
||||
r = strv_extend(&l, params->received_encrypted_credentials_directory);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore.encrypted"), /* filter_duplicates= */ true) < 0)
|
||||
return NULL;
|
||||
_cleanup_strv_free_ char **add = NULL;
|
||||
r = credential_store_path_encrypted(params->runtime_scope, &add);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_extend_strv_consume(&l, TAKE_PTR(add), /* filter_duplicates= */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (IN_SET(path, CREDENTIAL_SEARCH_PATH_TRUSTED, CREDENTIAL_SEARCH_PATH_ALL)) {
|
||||
if (strv_extend(&l, params->received_credentials_directory) < 0)
|
||||
return NULL;
|
||||
r = strv_extend(&l, params->received_credentials_directory);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore"), /* filter_duplicates= */ true) < 0)
|
||||
return NULL;
|
||||
_cleanup_strv_free_ char **add = NULL;
|
||||
r = credential_store_path(params->runtime_scope, &add);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_extend_strv_consume(&l, TAKE_PTR(add), /* filter_duplicates= */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
@ -414,7 +431,8 @@ static char** credential_search_path(const ExecParameters *params, CredentialSea
|
||||
log_debug("Credential search path is: %s", strempty(t));
|
||||
}
|
||||
|
||||
return TAKE_PTR(l);
|
||||
*ret = TAKE_PTR(l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct load_cred_args {
|
||||
@ -445,15 +463,38 @@ static int maybe_decrypt_and_write_credential(
|
||||
assert(data || size == 0);
|
||||
|
||||
if (args->encrypted) {
|
||||
r = decrypt_credential_and_warn(
|
||||
id,
|
||||
now(CLOCK_REALTIME),
|
||||
/* tpm2_device= */ NULL,
|
||||
/* tpm2_signature_path= */ NULL,
|
||||
getuid(),
|
||||
&IOVEC_MAKE(data, size),
|
||||
CREDENTIAL_ANY_SCOPE,
|
||||
&plaintext);
|
||||
switch (args->params->runtime_scope) {
|
||||
|
||||
case RUNTIME_SCOPE_SYSTEM:
|
||||
/* In system mode talk directly to the TPM */
|
||||
r = decrypt_credential_and_warn(
|
||||
id,
|
||||
now(CLOCK_REALTIME),
|
||||
/* tpm2_device= */ NULL,
|
||||
/* tpm2_signature_path= */ NULL,
|
||||
getuid(),
|
||||
&IOVEC_MAKE(data, size),
|
||||
CREDENTIAL_ANY_SCOPE,
|
||||
&plaintext);
|
||||
break;
|
||||
|
||||
case RUNTIME_SCOPE_USER:
|
||||
/* In per user mode we'll not have access to the machine secret, nor to the TPM (most
|
||||
* likely), hence go via the IPC service instead. Do this if we are run in root's
|
||||
* per-user invocation too, to minimize differences and because isolating this logic
|
||||
* into a separate process is generally a good thing anyway. */
|
||||
r = ipc_decrypt_credential(
|
||||
id,
|
||||
now(CLOCK_REALTIME),
|
||||
getuid(),
|
||||
&IOVEC_MAKE(data, size),
|
||||
/* flags= */ 0, /* only allow user creds in user scope */
|
||||
&plaintext);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -611,9 +652,9 @@ static int load_credential(
|
||||
* directory we received ourselves. We don't support the AF_UNIX stuff in this mode, since we
|
||||
* are operating on a credential store, i.e. this is guaranteed to be regular files. */
|
||||
|
||||
search_path = credential_search_path(args->params, CREDENTIAL_SEARCH_PATH_ALL);
|
||||
if (!search_path)
|
||||
return -ENOMEM;
|
||||
r = credential_search_path(args->params, CREDENTIAL_SEARCH_PATH_ALL, &search_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
missing_ok = true;
|
||||
} else
|
||||
@ -797,9 +838,9 @@ static int acquire_credentials(
|
||||
ORDERED_SET_FOREACH(ic, context->import_credentials) {
|
||||
_cleanup_free_ char **search_path = NULL;
|
||||
|
||||
search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_TRUSTED);
|
||||
if (!search_path)
|
||||
return -ENOMEM;
|
||||
r = credential_search_path(params, CREDENTIAL_SEARCH_PATH_TRUSTED, &search_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
args.encrypted = false;
|
||||
|
||||
@ -811,9 +852,10 @@ static int acquire_credentials(
|
||||
return r;
|
||||
|
||||
search_path = strv_free(search_path);
|
||||
search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ENCRYPTED);
|
||||
if (!search_path)
|
||||
return -ENOMEM;
|
||||
|
||||
r = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ENCRYPTED, &search_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
args.encrypted = true;
|
||||
|
||||
|
@ -3,9 +3,11 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bitfield.h"
|
||||
#include "creds-util.h"
|
||||
#include "dropin.h"
|
||||
#include "errno-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "generator.h"
|
||||
@ -27,6 +29,7 @@ static char **arg_wants = NULL;
|
||||
static bool arg_debug_shell = false;
|
||||
static char *arg_debug_tty = NULL;
|
||||
static char *arg_default_debug_tty = NULL;
|
||||
static uint32_t arg_breakpoints = 0;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_default_unit, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_mask, strv_freep);
|
||||
@ -34,6 +37,91 @@ STATIC_DESTRUCTOR_REGISTER(arg_wants, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_debug_tty, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_default_debug_tty, freep);
|
||||
|
||||
typedef enum BreakpointType {
|
||||
BREAKPOINT_PRE_UDEV,
|
||||
BREAKPOINT_PRE_BASIC,
|
||||
BREAKPOINT_PRE_SYSROOT_MOUNT,
|
||||
BREAKPOINT_PRE_SWITCH_ROOT,
|
||||
_BREAKPOINT_TYPE_MAX,
|
||||
_BREAKPOINT_TYPE_INVALID = -EINVAL,
|
||||
} BreakpointType;
|
||||
|
||||
typedef enum BreakpointValidity {
|
||||
BREAKPOINT_DEFAULT = 1 << 0,
|
||||
BREAKPOINT_IN_INITRD = 1 << 1,
|
||||
BREAKPOINT_ON_HOST = 1 << 2,
|
||||
} BreakpointValidity;
|
||||
|
||||
typedef struct BreakpointInfo {
|
||||
BreakpointType type;
|
||||
const char *name;
|
||||
const char *unit;
|
||||
BreakpointValidity validity;
|
||||
} BreakpointInfo;
|
||||
|
||||
static const struct BreakpointInfo breakpoint_info_table[_BREAKPOINT_TYPE_MAX] = {
|
||||
{ BREAKPOINT_PRE_UDEV, "pre-udev", "breakpoint-pre-udev.service", BREAKPOINT_IN_INITRD | BREAKPOINT_ON_HOST },
|
||||
{ BREAKPOINT_PRE_BASIC, "pre-basic", "breakpoint-pre-basic.service", BREAKPOINT_IN_INITRD | BREAKPOINT_ON_HOST },
|
||||
{ BREAKPOINT_PRE_SYSROOT_MOUNT, "pre-mount", "breakpoint-pre-mount.service", BREAKPOINT_IN_INITRD },
|
||||
{ BREAKPOINT_PRE_SWITCH_ROOT, "pre-switch-root", "breakpoint-pre-switch-root.service", BREAKPOINT_IN_INITRD | BREAKPOINT_DEFAULT },
|
||||
};
|
||||
|
||||
static BreakpointType parse_breakpoint_from_string_one(const char *s) {
|
||||
assert(s);
|
||||
|
||||
FOREACH_ARRAY(i, breakpoint_info_table, ELEMENTSOF(breakpoint_info_table))
|
||||
if (streq(i->name, s))
|
||||
return i->type;
|
||||
|
||||
return _BREAKPOINT_TYPE_INVALID;
|
||||
}
|
||||
|
||||
static int parse_breakpoint_from_string(const char *s, uint32_t *ret_breakpoints) {
|
||||
uint32_t breakpoints = 0;
|
||||
int r;
|
||||
|
||||
assert(ret_breakpoints);
|
||||
|
||||
/* Empty value? set default breakpoint */
|
||||
if (isempty(s)) {
|
||||
if (in_initrd()) {
|
||||
FOREACH_ARRAY(i, breakpoint_info_table, ELEMENTSOF(breakpoint_info_table))
|
||||
if (i->validity & BREAKPOINT_DEFAULT) {
|
||||
breakpoints |= 1 << i->type;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
log_warning("No default breakpoint defined on the host, ignoring breakpoint request from kernel command line.");
|
||||
} else
|
||||
for (;;) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
BreakpointType tt;
|
||||
|
||||
r = extract_first_word(&s, &t, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
tt = parse_breakpoint_from_string_one(t);
|
||||
if (tt < 0) {
|
||||
log_warning("Invalid breakpoint value '%s', ignoring.", t);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_initrd() && !FLAGS_SET(breakpoint_info_table[tt].validity, BREAKPOINT_IN_INITRD))
|
||||
log_warning("Breakpoint '%s' not valid in the initrd, ignoring.", t);
|
||||
else if (!in_initrd() && !FLAGS_SET(breakpoint_info_table[tt].validity, BREAKPOINT_ON_HOST))
|
||||
log_warning("Breakpoint '%s' not valid on the host, ignoring.", t);
|
||||
else
|
||||
breakpoints |= 1 << tt;
|
||||
}
|
||||
|
||||
*ret_breakpoints = breakpoints;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
|
||||
int r;
|
||||
|
||||
@ -88,6 +176,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
|
||||
|
||||
return free_and_strdup_warn(&arg_default_unit, value);
|
||||
|
||||
} else if (streq(key, "systemd.break")) {
|
||||
uint32_t breakpoints = 0;
|
||||
|
||||
r = parse_breakpoint_from_string(value, &breakpoints);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to parse breakpoint value '%s': %m", value);
|
||||
|
||||
arg_breakpoints |= breakpoints;
|
||||
|
||||
} else if (!value) {
|
||||
const char *target;
|
||||
|
||||
@ -269,6 +366,10 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
|
||||
RET_GATHER(r, install_debug_shell_dropin());
|
||||
}
|
||||
|
||||
BIT_FOREACH(i, arg_breakpoints)
|
||||
if (strv_extend(&arg_wants, breakpoint_info_table[i].unit) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (get_credentials_dir(&credentials_dir) >= 0)
|
||||
RET_GATHER(r, process_unit_credentials(credentials_dir));
|
||||
|
||||
|
@ -95,6 +95,7 @@ static char *arg_loop_ref = NULL;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
static bool arg_mtree_hash = true;
|
||||
static bool arg_via_service = false;
|
||||
static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
@ -151,6 +152,8 @@ static int help(void) {
|
||||
" Generate JSON output\n"
|
||||
" --loop-ref=NAME Set reference string for loopback device\n"
|
||||
" --mtree-hash=BOOL Whether to include SHA256 hash in the mtree output\n"
|
||||
" --user Discover user images\n"
|
||||
" --system Discover system images\n"
|
||||
"\n%3$sCommands:%4$s\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
@ -274,6 +277,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_VALIDATE,
|
||||
ARG_MTREE_HASH,
|
||||
ARG_MAKE_ARCHIVE,
|
||||
ARG_SYSTEM,
|
||||
ARG_USER,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -307,10 +312,13 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "validate", no_argument, NULL, ARG_VALIDATE },
|
||||
{ "mtree-hash", required_argument, NULL, ARG_MTREE_HASH },
|
||||
{ "make-archive", no_argument, NULL, ARG_MAKE_ARCHIVE },
|
||||
{ "system", no_argument, NULL, ARG_SYSTEM },
|
||||
{ "user", no_argument, NULL, ARG_USER },
|
||||
{}
|
||||
};
|
||||
|
||||
_cleanup_free_ char **buf = NULL; /* we use free(), not strv_free() here, as we don't copy the strings here */
|
||||
bool system_scope_requested = false, user_scope_requested = false;
|
||||
int c, r;
|
||||
|
||||
assert(argc >= 0);
|
||||
@ -531,7 +539,6 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case ARG_MAKE_ARCHIVE:
|
||||
|
||||
r = dlopen_libarchive();
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Archive support not available (compiled without libarchive, or libarchive not installed?).");
|
||||
@ -539,6 +546,14 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_action = ACTION_MAKE_ARCHIVE;
|
||||
break;
|
||||
|
||||
case ARG_SYSTEM:
|
||||
system_scope_requested = true;
|
||||
break;
|
||||
|
||||
case ARG_USER:
|
||||
user_scope_requested = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -547,6 +562,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (system_scope_requested || user_scope_requested)
|
||||
arg_runtime_scope = system_scope_requested && user_scope_requested ? _RUNTIME_SCOPE_INVALID :
|
||||
system_scope_requested ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
|
||||
|
||||
switch (arg_action) {
|
||||
|
||||
case ACTION_DISSECT:
|
||||
@ -1851,7 +1870,7 @@ static int action_discover(void) {
|
||||
return log_oom();
|
||||
|
||||
for (ImageClass cl = 0; cl < _IMAGE_CLASS_MAX; cl++) {
|
||||
r = image_discover(cl, NULL, images);
|
||||
r = image_discover(arg_runtime_scope, cl, NULL, images);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to discover images: %m");
|
||||
}
|
||||
|
@ -28,23 +28,36 @@
|
||||
#include "memory-util-fundamental.h"
|
||||
#include "sha1-fundamental.h"
|
||||
|
||||
static void get_chid(const char16_t *const smbios_fields[static _CHID_SMBIOS_FIELDS_MAX], uint32_t mask, EFI_GUID *ret_chid) {
|
||||
static void get_chid(
|
||||
const char16_t *const smbios_fields[static _CHID_SMBIOS_FIELDS_MAX],
|
||||
uint32_t mask,
|
||||
EFI_GUID *ret_chid) {
|
||||
|
||||
assert(mask != 0);
|
||||
assert(ret_chid);
|
||||
const EFI_GUID namespace = { UINT32_C(0x12d8ff70), UINT16_C(0x7f4c), UINT16_C(0x7d4c), {} }; /* Swapped to BE */
|
||||
|
||||
struct sha1_ctx ctx = {};
|
||||
sha1_init_ctx(&ctx);
|
||||
|
||||
static const EFI_GUID namespace = { UINT32_C(0x12d8ff70), UINT16_C(0x7f4c), UINT16_C(0x7d4c), {} }; /* Swapped to BE */
|
||||
sha1_process_bytes(&namespace, sizeof(namespace), &ctx);
|
||||
|
||||
for (unsigned i = 0; i < _CHID_SMBIOS_FIELDS_MAX; i++)
|
||||
if ((mask >> i) & 1) {
|
||||
if (i > 0)
|
||||
sha1_process_bytes(L"&", 2, &ctx);
|
||||
sha1_process_bytes(smbios_fields[i], strlen16(smbios_fields[i]) * sizeof(char16_t), &ctx);
|
||||
for (ChidSmbiosFields i = 0; i < _CHID_SMBIOS_FIELDS_MAX; i++) {
|
||||
if (!FLAGS_SET(mask, UINT32_C(1) << i))
|
||||
continue;
|
||||
|
||||
if (!smbios_fields[i]) {
|
||||
/* If some SMBIOS field is missing, don't generate the CHID, as per spec */
|
||||
memzero(ret_chid, sizeof(EFI_GUID));
|
||||
return;
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
sha1_process_bytes(L"&", 2, &ctx);
|
||||
|
||||
sha1_process_bytes(smbios_fields[i], strlen16(smbios_fields[i]) * sizeof(char16_t), &ctx);
|
||||
}
|
||||
|
||||
uint8_t hash[SHA1_DIGEST_SIZE];
|
||||
sha1_finish_ctx(&ctx, hash);
|
||||
|
||||
@ -61,7 +74,31 @@ static void get_chid(const char16_t *const smbios_fields[static _CHID_SMBIOS_FIE
|
||||
ret_chid->Data4[0] = (ret_chid->Data4[0] & UINT8_C(0x3f)) | UINT8_C(0x80);
|
||||
}
|
||||
|
||||
static const uint32_t chid_smbios_table[CHID_TYPES_MAX] = {
|
||||
const uint32_t chid_smbios_table[CHID_TYPES_MAX] = {
|
||||
[0] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_FAMILY) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_NAME) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_SKU) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BIOS_VENDOR) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BIOS_VERSION) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BIOS_MAJOR) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BIOS_MINOR),
|
||||
|
||||
[1] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_FAMILY) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_NAME) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BIOS_VENDOR) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BIOS_VERSION) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BIOS_MAJOR) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BIOS_MINOR),
|
||||
|
||||
[2] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_NAME) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BIOS_VENDOR) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BIOS_VERSION) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BIOS_MAJOR) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BIOS_MINOR),
|
||||
|
||||
[3] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_FAMILY) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_NAME) |
|
||||
@ -102,18 +139,26 @@ static const uint32_t chid_smbios_table[CHID_TYPES_MAX] = {
|
||||
[11] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_FAMILY),
|
||||
|
||||
[12] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_ENCLOSURE_TYPE),
|
||||
|
||||
[13] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BASEBOARD_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_BASEBOARD_PRODUCT),
|
||||
|
||||
[14] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER),
|
||||
};
|
||||
|
||||
void chid_calculate(const char16_t *const smbios_fields[static _CHID_SMBIOS_FIELDS_MAX], EFI_GUID ret_chids[static CHID_TYPES_MAX]) {
|
||||
assert(smbios_fields);
|
||||
assert(ret_chids);
|
||||
|
||||
for (size_t i = 0; i < CHID_TYPES_MAX; i++)
|
||||
if (chid_smbios_table[i] != 0)
|
||||
get_chid(smbios_fields, chid_smbios_table[i], &ret_chids[i]);
|
||||
else
|
||||
for (size_t i = 0; i < CHID_TYPES_MAX; i++) {
|
||||
if (chid_smbios_table[i] == 0) {
|
||||
memzero(&ret_chids[i], sizeof(EFI_GUID));
|
||||
continue;
|
||||
}
|
||||
|
||||
get_chid(smbios_fields, chid_smbios_table[i], &ret_chids[i]);
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,15 @@ typedef enum ChidSmbiosFields {
|
||||
CHID_SMBIOS_PRODUCT_SKU,
|
||||
CHID_SMBIOS_BASEBOARD_MANUFACTURER,
|
||||
CHID_SMBIOS_BASEBOARD_PRODUCT,
|
||||
CHID_SMBIOS_BIOS_VENDOR,
|
||||
CHID_SMBIOS_BIOS_VERSION,
|
||||
CHID_SMBIOS_BIOS_MAJOR,
|
||||
CHID_SMBIOS_BIOS_MINOR,
|
||||
CHID_SMBIOS_ENCLOSURE_TYPE,
|
||||
_CHID_SMBIOS_FIELDS_MAX,
|
||||
} ChidSmbiosFields;
|
||||
|
||||
extern const uint32_t chid_smbios_table[CHID_TYPES_MAX];
|
||||
|
||||
/* CHID (also called HWID by fwupd) is described at https://github.com/fwupd/fwupd/blob/main/docs/hwids.md */
|
||||
void chid_calculate(const char16_t *const smbios_fields[static _CHID_SMBIOS_FIELDS_MAX], EFI_GUID ret_chids[static CHID_TYPES_MAX]);
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
static ImportCompressType arg_compress = IMPORT_COMPRESS_UNKNOWN;
|
||||
static ImageClass arg_class = IMAGE_MACHINE;
|
||||
static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
|
||||
|
||||
static void determine_compression_from_filename(const char *p) {
|
||||
|
||||
@ -66,7 +67,7 @@ static int export_tar(int argc, char *argv[], void *userdata) {
|
||||
|
||||
local = argv[1];
|
||||
if (image_name_is_valid(local)) {
|
||||
r = image_find(arg_class, local, NULL, &image);
|
||||
r = image_find(arg_runtime_scope, arg_class, local, NULL, &image);
|
||||
if (r == -ENOENT)
|
||||
return log_error_errno(r, "Image %s not found.", local);
|
||||
if (r < 0)
|
||||
@ -139,7 +140,7 @@ static int export_raw(int argc, char *argv[], void *userdata) {
|
||||
|
||||
local = argv[1];
|
||||
if (image_name_is_valid(local)) {
|
||||
r = image_find(arg_class, local, NULL, &image);
|
||||
r = image_find(arg_runtime_scope, arg_class, local, NULL, &image);
|
||||
if (r == -ENOENT)
|
||||
return log_error_errno(r, "Image %s not found.", local);
|
||||
if (r < 0)
|
||||
|
@ -34,6 +34,7 @@ static bool arg_sync = true;
|
||||
static bool arg_direct = false;
|
||||
static const char *arg_image_root = NULL;
|
||||
static ImageClass arg_class = IMAGE_MACHINE;
|
||||
static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
|
||||
|
||||
typedef struct ProgressInfo {
|
||||
RateLimit limit;
|
||||
@ -145,7 +146,7 @@ static int import_fs(int argc, char *argv[], void *userdata) {
|
||||
return log_oom();
|
||||
|
||||
if (!arg_force) {
|
||||
r = image_find(arg_class, local, NULL, NULL);
|
||||
r = image_find(arg_runtime_scope, arg_class, local, NULL, NULL);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
|
||||
|
@ -30,6 +30,7 @@ static const char *arg_image_root = NULL;
|
||||
static ImportFlags arg_import_flags = IMPORT_BTRFS_SUBVOL | IMPORT_BTRFS_QUOTA | IMPORT_CONVERT_QCOW2 | IMPORT_SYNC;
|
||||
static uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX;
|
||||
static ImageClass arg_class = IMAGE_MACHINE;
|
||||
static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
|
||||
|
||||
static int normalize_local(const char *local, char **ret) {
|
||||
_cleanup_free_ char *ll = NULL;
|
||||
@ -63,7 +64,7 @@ static int normalize_local(const char *local, char **ret) {
|
||||
local = "imported";
|
||||
|
||||
if (!FLAGS_SET(arg_import_flags, IMPORT_FORCE)) {
|
||||
r = image_find(arg_class, local, NULL, NULL);
|
||||
r = image_find(arg_runtime_scope, arg_class, local, NULL, NULL);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
|
||||
|
@ -111,6 +111,8 @@ struct Manager {
|
||||
|
||||
bool use_btrfs_subvol;
|
||||
bool use_btrfs_quota;
|
||||
|
||||
RuntimeScope runtime_scope; /* for now: always RUNTIME_SCOPE_SYSTEM */
|
||||
};
|
||||
|
||||
#define TRANSFERS_MAX 64
|
||||
@ -721,6 +723,7 @@ static int manager_new(Manager **ret) {
|
||||
*m = (Manager) {
|
||||
.use_btrfs_subvol = true,
|
||||
.use_btrfs_quota = true,
|
||||
.runtime_scope = RUNTIME_SCOPE_SYSTEM,
|
||||
};
|
||||
|
||||
r = sd_event_default(&m->event);
|
||||
@ -1332,6 +1335,7 @@ static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_er
|
||||
static int method_list_images(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
ImageClass class = _IMAGE_CLASS_INVALID;
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(msg);
|
||||
@ -1372,7 +1376,7 @@ static int method_list_images(sd_bus_message *msg, void *userdata, sd_bus_error
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
||||
r = image_discover(c, /* root= */ NULL, h);
|
||||
r = image_discover(m->runtime_scope, c, /* root= */ NULL, h);
|
||||
if (r < 0) {
|
||||
if (class >= 0)
|
||||
return r;
|
||||
|
@ -33,6 +33,7 @@ static ImportFlags arg_import_flags = IMPORT_PULL_SETTINGS | IMPORT_PULL_ROOTHAS
|
||||
static uint64_t arg_offset = UINT64_MAX, arg_size_max = UINT64_MAX;
|
||||
static char *arg_checksum = NULL;
|
||||
static ImageClass arg_class = IMAGE_MACHINE;
|
||||
static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_checksum, freep);
|
||||
|
||||
@ -66,7 +67,7 @@ static int normalize_local(const char *local, const char *url, char **ret) {
|
||||
local);
|
||||
|
||||
if (!FLAGS_SET(arg_import_flags, IMPORT_FORCE)) {
|
||||
r = image_find(arg_class, local, NULL, NULL);
|
||||
r = image_find(arg_runtime_scope, arg_class, local, NULL, NULL);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
|
||||
|
@ -132,6 +132,7 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
|
||||
.log_level_max = -1,
|
||||
.log_ratelimit_interval = s->ratelimit_interval,
|
||||
.log_ratelimit_burst = s->ratelimit_burst,
|
||||
.capability_quintet = CAPABILITY_QUINTET_NULL,
|
||||
};
|
||||
|
||||
r = hashmap_ensure_put(&s->client_contexts, NULL, PID_TO_PTR(pid), c);
|
||||
@ -154,7 +155,6 @@ static void client_context_reset(Server *s, ClientContext *c) {
|
||||
c->comm = mfree(c->comm);
|
||||
c->exe = mfree(c->exe);
|
||||
c->cmdline = mfree(c->cmdline);
|
||||
c->capeff = mfree(c->capeff);
|
||||
|
||||
c->auditid = AUDIT_SESSION_INVALID;
|
||||
c->loginuid = UID_INVALID;
|
||||
@ -184,6 +184,8 @@ static void client_context_reset(Server *s, ClientContext *c) {
|
||||
|
||||
c->log_filter_allowed_patterns = set_free_free(c->log_filter_allowed_patterns);
|
||||
c->log_filter_denied_patterns = set_free_free(c->log_filter_denied_patterns);
|
||||
|
||||
c->capability_quintet = CAPABILITY_QUINTET_NULL;
|
||||
}
|
||||
|
||||
static ClientContext* client_context_free(Server *s, ClientContext *c) {
|
||||
@ -233,8 +235,7 @@ static void client_context_read_basic(ClientContext *c) {
|
||||
if (pid_get_cmdline(c->pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &t) >= 0)
|
||||
free_and_replace(c->cmdline, t);
|
||||
|
||||
if (get_process_capeff(c->pid, &t) >= 0)
|
||||
free_and_replace(c->capeff, t);
|
||||
(void) pidref_get_capability(&PIDREF_MAKE_FROM_PID(c->pid), &c->capability_quintet);
|
||||
}
|
||||
|
||||
static int client_context_read_label(
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "capability-util.h"
|
||||
#include "set.h"
|
||||
#include "time-util.h"
|
||||
|
||||
@ -27,7 +28,7 @@ struct ClientContext {
|
||||
char *comm;
|
||||
char *exe;
|
||||
char *cmdline;
|
||||
char *capeff;
|
||||
CapabilityQuintet capability_quintet;
|
||||
|
||||
uint32_t auditid;
|
||||
uid_t loginuid;
|
||||
|
@ -1109,7 +1109,7 @@ static void server_dispatch_message_real(
|
||||
* Let's use a heap allocation for this one. */
|
||||
cmdline1 = set_iovec_string_field(iovec, &n, "_CMDLINE=", c->cmdline);
|
||||
|
||||
IOVEC_ADD_STRING_FIELD(iovec, n, c->capeff, "_CAP_EFFECTIVE"); /* Read from /proc/.../status */
|
||||
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->capability_quintet.effective, uint64_t, capability_is_set, "%" PRIx64, "_CAP_EFFECTIVE");
|
||||
IOVEC_ADD_SIZED_FIELD(iovec, n, c->label, c->label_size, "_SELINUX_CONTEXT");
|
||||
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "_AUDIT_SESSION");
|
||||
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->loginuid, uid_t, uid_is_valid, UID_FMT, "_AUDIT_LOGINUID");
|
||||
@ -1144,7 +1144,7 @@ static void server_dispatch_message_real(
|
||||
if (o->cmdline)
|
||||
cmdline2 = set_iovec_string_field(iovec, &n, "OBJECT_CMDLINE=", o->cmdline);
|
||||
|
||||
IOVEC_ADD_STRING_FIELD(iovec, n, o->capeff, "OBJECT_CAP_EFFECTIVE");
|
||||
IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->capability_quintet.effective, uint64_t, capability_is_set, "%" PRIx64, "OBJECT_CAP_EFFECTIVE");
|
||||
IOVEC_ADD_SIZED_FIELD(iovec, n, o->label, o->label_size, "OBJECT_SELINUX_CONTEXT");
|
||||
IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "OBJECT_AUDIT_SESSION");
|
||||
IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->loginuid, uid_t, uid_is_valid, UID_FMT, "OBJECT_AUDIT_LOGINUID");
|
||||
|
@ -125,14 +125,13 @@ int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_di
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
char **p = ASSERT_PTR(userdata);
|
||||
const char *path;
|
||||
int json_dispatch_const_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
const char **p = ASSERT_PTR(userdata), *path;
|
||||
|
||||
assert(variant);
|
||||
|
||||
if (sd_json_variant_is_null(variant)) {
|
||||
*p = mfree(*p);
|
||||
*p = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -145,8 +144,24 @@ int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispa
|
||||
if (!path_is_absolute(path))
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an absolute file system path.", strna(name));
|
||||
|
||||
if (free_and_strdup(p, path) < 0)
|
||||
return json_log_oom(variant, flags);
|
||||
*p = path;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
char **s = ASSERT_PTR(userdata);
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert_return(variant, -EINVAL);
|
||||
|
||||
r = json_dispatch_const_path(name, variant, flags, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = free_and_strdup(s, p);
|
||||
if (r < 0)
|
||||
return json_log(variant, flags, r, "Failed to allocate path string: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -113,6 +113,7 @@ int json_dispatch_user_group_name(const char *name, sd_json_variant *variant, sd
|
||||
int json_dispatch_const_user_group_name(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_const_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_ifindex(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
|
@ -334,46 +334,46 @@ _public_ int sd_pidfd_get_cgroup(int pidfd, char **ret_cgroup) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int sd_peer_get_session(int fd, char **session) {
|
||||
struct ucred ucred = UCRED_INVALID;
|
||||
_public_ int sd_peer_get_session(int fd, char **ret) {
|
||||
int r;
|
||||
|
||||
assert_return(fd >= 0, -EBADF);
|
||||
assert_return(session, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = getpeercred(fd, &ucred);
|
||||
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
|
||||
r = getpeerpidref(fd, &pidref);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return cg_pid_get_session(ucred.pid, session);
|
||||
return cg_pidref_get_session(&pidref, ret);
|
||||
}
|
||||
|
||||
_public_ int sd_peer_get_owner_uid(int fd, uid_t *uid) {
|
||||
struct ucred ucred;
|
||||
_public_ int sd_peer_get_owner_uid(int fd, uid_t *ret) {
|
||||
int r;
|
||||
|
||||
assert_return(fd >= 0, -EBADF);
|
||||
assert_return(uid, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = getpeercred(fd, &ucred);
|
||||
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
|
||||
r = getpeerpidref(fd, &pidref);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return cg_pid_get_owner_uid(ucred.pid, uid);
|
||||
return cg_pidref_get_owner_uid(&pidref, ret);
|
||||
}
|
||||
|
||||
_public_ int sd_peer_get_unit(int fd, char **unit) {
|
||||
struct ucred ucred;
|
||||
_public_ int sd_peer_get_unit(int fd, char **ret) {
|
||||
int r;
|
||||
|
||||
assert_return(fd >= 0, -EBADF);
|
||||
assert_return(unit, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = getpeercred(fd, &ucred);
|
||||
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
|
||||
r = getpeerpidref(fd, &pidref);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return cg_pid_get_unit(ucred.pid, unit);
|
||||
return cg_pidref_get_unit(&pidref, ret);
|
||||
}
|
||||
|
||||
_public_ int sd_peer_get_user_unit(int fd, char **unit) {
|
||||
|
@ -84,3 +84,19 @@ static inline char** generator_binary_paths(RuntimeScope runtime_scope) {
|
||||
static inline char** env_generator_binary_paths(RuntimeScope runtime_scope) {
|
||||
return generator_binary_paths_internal(runtime_scope, true);
|
||||
}
|
||||
|
||||
static inline int credential_store_path(RuntimeScope runtime_scope, char ***ret) {
|
||||
return sd_path_lookup_strv(
|
||||
runtime_scope == RUNTIME_SCOPE_SYSTEM ?
|
||||
SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE : SD_PATH_USER_SEARCH_CREDENTIAL_STORE,
|
||||
/* suffix= */ NULL,
|
||||
ret);
|
||||
}
|
||||
|
||||
static inline int credential_store_path_encrypted(RuntimeScope runtime_scope, char ***ret) {
|
||||
return sd_path_lookup_strv(
|
||||
runtime_scope == RUNTIME_SCOPE_SYSTEM ?
|
||||
SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE_ENCRYPTED : SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED,
|
||||
/* suffix= */ NULL,
|
||||
ret);
|
||||
}
|
||||
|
@ -36,7 +36,12 @@ static int from_environment(const char *envname, const char *fallback, const cha
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int from_home_dir(const char *envname, const char *suffix, char **buffer, const char **ret) {
|
||||
static int from_home_dir(
|
||||
const char *envname,
|
||||
const char *suffix,
|
||||
char **buffer,
|
||||
const char **ret) {
|
||||
|
||||
_cleanup_free_ char *h = NULL;
|
||||
int r;
|
||||
|
||||
@ -350,6 +355,30 @@ static int get_path(uint64_t type, char **buffer, const char **ret) {
|
||||
case SD_PATH_SYSTEMD_USER_ENVIRONMENT_GENERATOR:
|
||||
*ret = USER_ENV_GENERATOR_DIR;
|
||||
return 0;
|
||||
|
||||
case SD_PATH_SYSTEM_CREDENTIAL_STORE:
|
||||
*ret = "/etc/credstore";
|
||||
return 0;
|
||||
|
||||
case SD_PATH_SYSTEM_CREDENTIAL_STORE_ENCRYPTED:
|
||||
*ret = "/etc/credstore.encrypted";
|
||||
return 0;
|
||||
|
||||
case SD_PATH_USER_CREDENTIAL_STORE:
|
||||
r = xdg_user_config_dir("credstore", buffer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = *buffer;
|
||||
return 0;
|
||||
|
||||
case SD_PATH_USER_CREDENTIAL_STORE_ENCRYPTED:
|
||||
r = xdg_user_config_dir("credstore.encrypted", buffer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = *buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
@ -366,12 +395,12 @@ static int get_path_alloc(uint64_t type, const char *suffix, char **ret) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (suffix) {
|
||||
if (!isempty(suffix)) {
|
||||
char *suffixed = path_join(p, suffix);
|
||||
if (!suffixed)
|
||||
return -ENOMEM;
|
||||
|
||||
path_simplify(suffixed);
|
||||
path_simplify_full(suffixed, PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
|
||||
|
||||
free_and_replace(buffer, suffixed);
|
||||
} else if (!buffer) {
|
||||
@ -601,8 +630,55 @@ static int get_search(uint64_t type, char ***ret) {
|
||||
case SD_PATH_SYSTEMD_SEARCH_NETWORK:
|
||||
return strv_from_nulstr(ret, NETWORK_DIRS_NULSTR);
|
||||
|
||||
case SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE:
|
||||
case SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE_ENCRYPTED: {
|
||||
const char *suffix =
|
||||
type == SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE_ENCRYPTED ? "credstore.encrypted" : "credstore";
|
||||
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
FOREACH_STRING(d, CONF_PATHS("")) {
|
||||
char *j = path_join(d, suffix);
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_consume(&l, TAKE_PTR(j));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case SD_PATH_USER_SEARCH_CREDENTIAL_STORE:
|
||||
case SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED: {
|
||||
const char *suffix =
|
||||
type == SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED ? "credstore.encrypted" : "credstore";
|
||||
|
||||
static const uint64_t dirs[] = {
|
||||
SD_PATH_USER_CONFIGURATION,
|
||||
SD_PATH_USER_RUNTIME,
|
||||
SD_PATH_USER_LIBRARY_PRIVATE,
|
||||
};
|
||||
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
FOREACH_ELEMENT(d, dirs) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
r = sd_path_lookup(*d, suffix, &p);
|
||||
if (r == -ENXIO)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_consume(&l, TAKE_PTR(p));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(l);
|
||||
return 0;
|
||||
}}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@ -637,7 +713,7 @@ _public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***ret)
|
||||
if (!path_extend(i, suffix))
|
||||
return -ENOMEM;
|
||||
|
||||
path_simplify(*i);
|
||||
path_simplify_full(*i, PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(l);
|
||||
|
@ -3215,7 +3215,13 @@ _public_ int sd_varlink_server_new(sd_varlink_server **ret, sd_varlink_server_fl
|
||||
int r;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
assert_return((flags & ~(SD_VARLINK_SERVER_ROOT_ONLY|SD_VARLINK_SERVER_MYSELF_ONLY|SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA|SD_VARLINK_SERVER_INPUT_SENSITIVE)) == 0, -EINVAL);
|
||||
assert_return((flags & ~(SD_VARLINK_SERVER_ROOT_ONLY|
|
||||
SD_VARLINK_SERVER_MYSELF_ONLY|
|
||||
SD_VARLINK_SERVER_ACCOUNT_UID|
|
||||
SD_VARLINK_SERVER_INHERIT_USERDATA|
|
||||
SD_VARLINK_SERVER_INPUT_SENSITIVE|
|
||||
SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT|
|
||||
SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT)) == 0, -EINVAL);
|
||||
|
||||
s = new(sd_varlink_server, 1);
|
||||
if (!s)
|
||||
@ -3425,6 +3431,9 @@ _public_ int sd_varlink_server_add_connection_pair(
|
||||
if (asprintf(&desc, "%s-%i-%i", varlink_server_description(server), input_fd, output_fd) >= 0)
|
||||
v->description = TAKE_PTR(desc);
|
||||
|
||||
(void) sd_varlink_set_allow_fd_passing_input(v, FLAGS_SET(server->flags, SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT));
|
||||
(void) sd_varlink_set_allow_fd_passing_output(v, FLAGS_SET(server->flags, SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT));
|
||||
|
||||
/* Link up the server and the connection, and take reference in both directions. Note that the
|
||||
* reference on the connection is left dangling. It will be dropped when the connection is closed,
|
||||
* which happens in varlink_close(), including in the event loop quit callback. */
|
||||
|
@ -85,6 +85,8 @@ static int get_sender_session(
|
||||
const char *name;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
/* Acquire the sender's session. This first checks if the sending process is inside a session itself,
|
||||
* and returns that. If not and 'consult_display' is true, this returns the display session of the
|
||||
* owning user of the caller. */
|
||||
@ -827,189 +829,25 @@ static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bu
|
||||
return sd_bus_send(NULL, reply, NULL);
|
||||
}
|
||||
|
||||
static int create_session(
|
||||
sd_bus_message *message,
|
||||
void *userdata,
|
||||
sd_bus_error *error,
|
||||
uid_t uid,
|
||||
pid_t leader_pid,
|
||||
int leader_pidfd,
|
||||
const char *service,
|
||||
const char *type,
|
||||
const char *class,
|
||||
const char *desktop,
|
||||
const char *cseat,
|
||||
uint32_t vtnr,
|
||||
const char *tty,
|
||||
const char *display,
|
||||
int remote,
|
||||
const char *remote_user,
|
||||
const char *remote_host,
|
||||
uint64_t flags) {
|
||||
static int manager_choose_session_id(
|
||||
Manager *m,
|
||||
PidRef *leader,
|
||||
char **ret_id) {
|
||||
|
||||
_cleanup_(pidref_done) PidRef leader = PIDREF_NULL;
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
_cleanup_free_ char *id = NULL;
|
||||
Session *session = NULL;
|
||||
uint32_t audit_id = 0;
|
||||
User *user = NULL;
|
||||
Seat *seat = NULL;
|
||||
SessionType t;
|
||||
SessionClass c;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
|
||||
if (!uid_is_valid(uid))
|
||||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID");
|
||||
|
||||
if (flags != 0)
|
||||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be zero.");
|
||||
|
||||
if (leader_pidfd >= 0)
|
||||
r = pidref_set_pidfd(&leader, leader_pidfd);
|
||||
else if (leader_pid == 0)
|
||||
r = bus_query_sender_pidref(message, &leader);
|
||||
else {
|
||||
if (leader_pid < 0)
|
||||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Leader PID is not valid");
|
||||
|
||||
r = pidref_set_pid(&leader, leader_pid);
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (leader.pid == 1 || pidref_is_self(&leader))
|
||||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
|
||||
|
||||
if (isempty(type))
|
||||
t = _SESSION_TYPE_INVALID;
|
||||
else {
|
||||
t = session_type_from_string(type);
|
||||
if (t < 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Invalid session type %s", type);
|
||||
}
|
||||
|
||||
if (isempty(class))
|
||||
c = _SESSION_CLASS_INVALID;
|
||||
else {
|
||||
c = session_class_from_string(class);
|
||||
if (c < 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Invalid session class %s", class);
|
||||
}
|
||||
|
||||
if (isempty(desktop))
|
||||
desktop = NULL;
|
||||
else {
|
||||
if (!string_is_safe(desktop))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Invalid desktop string %s", desktop);
|
||||
}
|
||||
|
||||
if (isempty(cseat))
|
||||
seat = NULL;
|
||||
else {
|
||||
seat = hashmap_get(m->seats, cseat);
|
||||
if (!seat)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT,
|
||||
"No seat '%s' known", cseat);
|
||||
}
|
||||
|
||||
if (tty_is_vc(tty)) {
|
||||
int v;
|
||||
|
||||
if (!seat)
|
||||
seat = m->seat0;
|
||||
else if (seat != m->seat0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"TTY %s is virtual console but seat %s is not seat0", tty, seat->id);
|
||||
|
||||
v = vtnr_from_tty(tty);
|
||||
if (v <= 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Cannot determine VT number from virtual console TTY %s", tty);
|
||||
|
||||
if (vtnr == 0)
|
||||
vtnr = (uint32_t) v;
|
||||
else if (vtnr != (uint32_t) v)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Specified TTY and VT number do not match");
|
||||
|
||||
} else if (tty_is_console(tty)) {
|
||||
|
||||
if (!seat)
|
||||
seat = m->seat0;
|
||||
else if (seat != m->seat0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Console TTY specified but seat is not seat0");
|
||||
|
||||
if (vtnr != 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Console TTY specified but VT number is not 0");
|
||||
}
|
||||
|
||||
if (seat) {
|
||||
if (seat_has_vts(seat)) {
|
||||
if (vtnr <= 0 || vtnr > 63)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"VT number out of range");
|
||||
} else {
|
||||
if (vtnr != 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Seat has no VTs but VT number not 0");
|
||||
}
|
||||
}
|
||||
|
||||
if (t == _SESSION_TYPE_INVALID) {
|
||||
if (!isempty(display))
|
||||
t = SESSION_X11;
|
||||
else if (!isempty(tty))
|
||||
t = SESSION_TTY;
|
||||
else
|
||||
t = SESSION_UNSPECIFIED;
|
||||
}
|
||||
|
||||
if (c == _SESSION_CLASS_INVALID) {
|
||||
if (t == SESSION_UNSPECIFIED)
|
||||
c = SESSION_BACKGROUND;
|
||||
else
|
||||
c = SESSION_USER;
|
||||
}
|
||||
|
||||
/* Check if we are already in a logind session, and if so refuse. */
|
||||
r = manager_get_session_by_pidref(m, &leader, /* ret_session= */ NULL);
|
||||
if (r < 0)
|
||||
return log_debug_errno(
|
||||
r,
|
||||
"Failed to check if process " PID_FMT " is already in a session: %m",
|
||||
leader.pid);
|
||||
if (r > 0)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY,
|
||||
"Already running in a session or user slice");
|
||||
|
||||
/* Old gdm and lightdm start the user-session on the same VT as the greeter session. But they destroy
|
||||
* the greeter session after the user-session and want the user-session to take over the VT. We need
|
||||
* to support this for backwards-compatibility, so make sure we allow new sessions on a VT that a
|
||||
* greeter is running on. Furthermore, to allow re-logins, we have to allow a greeter to take over a
|
||||
* used VT for the exact same reasons. */
|
||||
if (c != SESSION_GREETER &&
|
||||
vtnr > 0 &&
|
||||
vtnr < MALLOC_ELEMENTSOF(m->seat0->positions) &&
|
||||
m->seat0->positions[vtnr] &&
|
||||
m->seat0->positions[vtnr]->class != SESSION_GREETER)
|
||||
return sd_bus_error_set(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session");
|
||||
|
||||
if (hashmap_size(m->sessions) >= m->sessions_max)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED,
|
||||
"Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.",
|
||||
m->sessions_max);
|
||||
|
||||
(void) audit_session_from_pid(&leader, &audit_id);
|
||||
if (audit_session_is_valid(audit_id)) {
|
||||
/* Keep our session IDs and the audit session IDs in sync */
|
||||
assert(m);
|
||||
assert(pidref_is_set(leader));
|
||||
assert(ret_id);
|
||||
|
||||
/* Try to keep our session IDs and the audit session IDs in sync */
|
||||
_cleanup_free_ char *id = NULL;
|
||||
uint32_t audit_id = AUDIT_SESSION_INVALID;
|
||||
r = audit_session_from_pid(leader, &audit_id);
|
||||
if (r < 0) {
|
||||
if (r != -ENODATA)
|
||||
log_debug_errno(r, "Failed to read audit session ID of process " PID_FMT ", ignoring: %m", leader->pid);
|
||||
} else {
|
||||
if (asprintf(&id, "%"PRIu32, audit_id) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -1017,12 +855,11 @@ static int create_session(
|
||||
* not trust the audit data and let's better register a new ID */
|
||||
if (hashmap_contains(m->sessions, id)) {
|
||||
log_warning("Existing logind session ID %s used by new audit session, ignoring.", id);
|
||||
audit_id = AUDIT_SESSION_INVALID;
|
||||
id = mfree(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (!id) {
|
||||
if (!id)
|
||||
do {
|
||||
id = mfree(id);
|
||||
|
||||
@ -1030,15 +867,97 @@ static int create_session(
|
||||
return -ENOMEM;
|
||||
|
||||
} while (hashmap_contains(m->sessions, id));
|
||||
}
|
||||
|
||||
/* The generated names should not clash with 'auto' or 'self' */
|
||||
assert(!SESSION_IS_SELF(id));
|
||||
assert(!SESSION_IS_AUTO(id));
|
||||
|
||||
*ret_id = TAKE_PTR(id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int manager_create_session(
|
||||
Manager *m,
|
||||
uid_t uid,
|
||||
PidRef *leader, /* consumed */
|
||||
const char *service,
|
||||
SessionType type,
|
||||
SessionClass class,
|
||||
const char *desktop,
|
||||
Seat *seat,
|
||||
unsigned vtnr,
|
||||
const char *tty,
|
||||
const char *display,
|
||||
bool remote,
|
||||
const char *remote_user,
|
||||
const char *remote_host,
|
||||
Session **ret_session) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(uid_is_valid(uid));
|
||||
assert(pidref_is_set(leader));
|
||||
assert(ret_session);
|
||||
|
||||
/* Returns:
|
||||
* -EBUSY → client is already in a session
|
||||
* -EADDRNOTAVAIL → VT is already taken
|
||||
* -EUSERS → limit of sessions reached
|
||||
*/
|
||||
|
||||
if (type == _SESSION_TYPE_INVALID) {
|
||||
if (!isempty(display))
|
||||
type = SESSION_X11;
|
||||
else if (!isempty(tty))
|
||||
type = SESSION_TTY;
|
||||
else
|
||||
type = SESSION_UNSPECIFIED;
|
||||
}
|
||||
|
||||
if (class == _SESSION_CLASS_INVALID) {
|
||||
if (type == SESSION_UNSPECIFIED)
|
||||
class = SESSION_BACKGROUND;
|
||||
else
|
||||
class = SESSION_USER;
|
||||
}
|
||||
|
||||
/* Check if we are already in a logind session, and if so refuse. */
|
||||
r = manager_get_session_by_pidref(m, leader, /* ret_session= */ NULL);
|
||||
if (r < 0)
|
||||
return log_debug_errno(
|
||||
r,
|
||||
"Failed to check if process " PID_FMT " is already in a session: %m",
|
||||
leader->pid);
|
||||
if (r > 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "Client is already in a session.");
|
||||
|
||||
/* Old gdm and lightdm start the user-session on the same VT as the greeter session. But they destroy
|
||||
* the greeter session after the user-session and want the user-session to take over the VT. We need
|
||||
* to support this for backwards-compatibility, so make sure we allow new sessions on a VT that a
|
||||
* greeter is running on. Furthermore, to allow re-logins, we have to allow a greeter to take over a
|
||||
* used VT for the exact same reasons. */
|
||||
if (class != SESSION_GREETER &&
|
||||
vtnr > 0 &&
|
||||
vtnr < MALLOC_ELEMENTSOF(m->seat0->positions) &&
|
||||
m->seat0->positions[vtnr] &&
|
||||
m->seat0->positions[vtnr]->class != SESSION_GREETER)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EADDRNOTAVAIL), "VT already occupied by a session.");
|
||||
|
||||
if (hashmap_size(m->sessions) >= m->sessions_max)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EUSERS), "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max);
|
||||
|
||||
_cleanup_free_ char *id = NULL;
|
||||
r = manager_choose_session_id(m, leader, &id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* If we are not watching utmp already, try again */
|
||||
manager_reconnect_utmp(m);
|
||||
|
||||
User *user = NULL;
|
||||
Session *session = NULL;
|
||||
|
||||
r = manager_add_user_by_uid(m, uid, &user);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
@ -1048,21 +967,21 @@ static int create_session(
|
||||
goto fail;
|
||||
|
||||
session_set_user(session, user);
|
||||
r = session_set_leader_consume(session, TAKE_PIDREF(leader));
|
||||
r = session_set_leader_consume(session, TAKE_PIDREF(*leader));
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
session->original_type = session->type = t;
|
||||
session->original_type = session->type = type;
|
||||
session->remote = remote;
|
||||
session->vtnr = vtnr;
|
||||
session->class = c;
|
||||
session->class = class;
|
||||
|
||||
/* Once the first session that is of a pinning class shows up we'll change the GC mode for the user
|
||||
* from USER_GC_BY_ANY to USER_GC_BY_PIN, so that the user goes away once the last pinning session
|
||||
* goes away. Background: we want that user@.service – when started manually – remains around (which
|
||||
* itself is a non-pinning session), but gets stopped when the last pinning session goes away. */
|
||||
|
||||
if (SESSION_CLASS_PIN_USER(c))
|
||||
if (SESSION_CLASS_PIN_USER(class))
|
||||
user->gc_mode = USER_GC_BY_PIN;
|
||||
|
||||
if (!isempty(tty)) {
|
||||
@ -1109,14 +1028,184 @@ static int create_session(
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*ret_session = session;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (session)
|
||||
session_add_to_gc_queue(session);
|
||||
|
||||
if (user)
|
||||
user_add_to_gc_queue(user);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int manager_create_session_by_bus(
|
||||
Manager *m,
|
||||
sd_bus_message *message,
|
||||
sd_bus_error *error,
|
||||
uid_t uid,
|
||||
pid_t leader_pid,
|
||||
int leader_pidfd,
|
||||
const char *service,
|
||||
const char *type,
|
||||
const char *class,
|
||||
const char *desktop,
|
||||
const char *cseat,
|
||||
uint32_t vtnr,
|
||||
const char *tty,
|
||||
const char *display,
|
||||
int remote,
|
||||
const char *remote_user,
|
||||
const char *remote_host,
|
||||
uint64_t flags) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(message);
|
||||
|
||||
if (!uid_is_valid(uid))
|
||||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID");
|
||||
|
||||
if (flags != 0)
|
||||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be zero.");
|
||||
|
||||
_cleanup_(pidref_done) PidRef leader = PIDREF_NULL;
|
||||
if (leader_pidfd >= 0)
|
||||
r = pidref_set_pidfd(&leader, leader_pidfd);
|
||||
else if (leader_pid == 0)
|
||||
r = bus_query_sender_pidref(message, &leader);
|
||||
else {
|
||||
if (leader_pid < 0)
|
||||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Leader PID is not valid");
|
||||
|
||||
r = pidref_set_pid(&leader, leader_pid);
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (leader.pid == 1 || pidref_is_self(&leader))
|
||||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
|
||||
|
||||
SessionType t;
|
||||
if (isempty(type))
|
||||
t = _SESSION_TYPE_INVALID;
|
||||
else {
|
||||
t = session_type_from_string(type);
|
||||
if (t < 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Invalid session type %s", type);
|
||||
}
|
||||
|
||||
SessionClass c;
|
||||
if (isempty(class))
|
||||
c = _SESSION_CLASS_INVALID;
|
||||
else {
|
||||
c = session_class_from_string(class);
|
||||
if (c < 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Invalid session class %s", class);
|
||||
}
|
||||
|
||||
if (isempty(desktop))
|
||||
desktop = NULL;
|
||||
else {
|
||||
if (!string_is_safe(desktop))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Invalid desktop string %s", desktop);
|
||||
}
|
||||
|
||||
Seat *seat = NULL;
|
||||
if (isempty(cseat))
|
||||
seat = NULL;
|
||||
else {
|
||||
seat = hashmap_get(m->seats, cseat);
|
||||
if (!seat)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT,
|
||||
"No seat '%s' known", cseat);
|
||||
}
|
||||
|
||||
if (isempty(tty))
|
||||
tty = NULL;
|
||||
else if (tty_is_vc(tty)) {
|
||||
int v;
|
||||
|
||||
if (!seat)
|
||||
seat = m->seat0;
|
||||
else if (seat != m->seat0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"TTY %s is virtual console but seat %s is not seat0", tty, seat->id);
|
||||
|
||||
v = vtnr_from_tty(tty);
|
||||
if (v <= 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Cannot determine VT number from virtual console TTY %s", tty);
|
||||
|
||||
if (vtnr == 0)
|
||||
vtnr = (uint32_t) v;
|
||||
else if (vtnr != (uint32_t) v)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Specified TTY and VT number do not match");
|
||||
|
||||
} else if (tty_is_console(tty)) {
|
||||
|
||||
if (!seat)
|
||||
seat = m->seat0;
|
||||
else if (seat != m->seat0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Console TTY specified but seat is not seat0");
|
||||
|
||||
if (vtnr != 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Console TTY specified but VT number is not 0");
|
||||
}
|
||||
|
||||
if (seat) {
|
||||
if (seat_has_vts(seat)) {
|
||||
if (!vtnr_is_valid(vtnr))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"VT number out of range");
|
||||
} else {
|
||||
if (vtnr != 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Seat has no VTs but VT number not 0");
|
||||
}
|
||||
}
|
||||
|
||||
Session *session;
|
||||
r = manager_create_session(
|
||||
m,
|
||||
uid,
|
||||
&leader,
|
||||
service,
|
||||
t,
|
||||
c,
|
||||
desktop,
|
||||
seat,
|
||||
vtnr,
|
||||
tty,
|
||||
display,
|
||||
remote,
|
||||
remote_user,
|
||||
remote_host,
|
||||
&session);
|
||||
if (r == -EBUSY)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already running in a session or user slice");
|
||||
if (r == -EADDRNOTAVAIL)
|
||||
return sd_bus_error_set(error, BUS_ERROR_SESSION_BUSY, "Virtual terminal already occupied by a session");
|
||||
if (r == -EUSERS)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_enter_container(message, 'a', "(sv)");
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = session_start(session, message, error);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = sd_bus_message_exit_container(message);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
@ -1130,7 +1219,7 @@ static int create_session(
|
||||
* all is complete - or wait again. */
|
||||
r = session_send_create_reply(session, /* error= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
goto fail;
|
||||
|
||||
return 1;
|
||||
|
||||
@ -1138,9 +1227,6 @@ fail:
|
||||
if (session)
|
||||
session_add_to_gc_queue(session);
|
||||
|
||||
if (user)
|
||||
user_add_to_gc_queue(user);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -1175,9 +1261,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return create_session(
|
||||
message,
|
||||
return manager_create_session_by_bus(
|
||||
userdata,
|
||||
message,
|
||||
error,
|
||||
uid,
|
||||
leader_pid,
|
||||
@ -1223,9 +1309,9 @@ static int method_create_session_pidfd(sd_bus_message *message, void *userdata,
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return create_session(
|
||||
message,
|
||||
return manager_create_session_by_bus(
|
||||
userdata,
|
||||
message,
|
||||
error,
|
||||
uid,
|
||||
/* leader_pid = */ 0,
|
||||
|
@ -47,4 +47,21 @@ int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *erro
|
||||
|
||||
void manager_load_scheduled_shutdown(Manager *m);
|
||||
|
||||
int manager_create_session(
|
||||
Manager *m,
|
||||
uid_t uid,
|
||||
PidRef *leader,
|
||||
const char *service,
|
||||
SessionType type,
|
||||
SessionClass class,
|
||||
const char *desktop,
|
||||
Seat *seat,
|
||||
unsigned vtnr,
|
||||
const char *tty,
|
||||
const char *display,
|
||||
bool remote,
|
||||
const char *remote_user,
|
||||
const char *remote_host,
|
||||
Session **ret_session);
|
||||
|
||||
extern const BusObjectImplementation manager_object;
|
||||
|
@ -899,62 +899,40 @@ int session_send_lock_all(Manager *m, bool lock) {
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool session_job_pending(Session *s) {
|
||||
assert(s);
|
||||
assert(s->user);
|
||||
|
||||
/* Check if we have some jobs enqueued and not finished yet. Each time we get JobRemoved signal about
|
||||
* relevant units, session_send_create_reply and hence us is called (see match_job_removed).
|
||||
* Note that we don't care about job result here. */
|
||||
|
||||
return s->scope_job ||
|
||||
s->user->runtime_dir_job ||
|
||||
(SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) && s->user->service_manager_job);
|
||||
}
|
||||
|
||||
int session_send_create_reply(Session *s, sd_bus_error *error) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
|
||||
_cleanup_close_ int fifo_fd = -EBADF;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
int session_send_create_reply_bus(Session *s, const sd_bus_error *error) {
|
||||
assert(s);
|
||||
|
||||
/* This is called after the session scope and the user service were successfully created, and finishes where
|
||||
* bus_manager_create_session() left off. */
|
||||
/* This is called after the session scope and the user service were successfully created, and
|
||||
* finishes where manager_create_session() left off. */
|
||||
|
||||
if (!s->create_message)
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *c = TAKE_PTR(s->create_message);
|
||||
if (!c)
|
||||
return 0;
|
||||
|
||||
/* If error occurred, return it immediately. Otherwise let's wait for all jobs to finish before
|
||||
* continuing. */
|
||||
if (!sd_bus_error_is_set(error) && session_job_pending(s))
|
||||
return 0;
|
||||
|
||||
c = TAKE_PTR(s->create_message);
|
||||
if (error)
|
||||
if (sd_bus_error_is_set(error))
|
||||
return sd_bus_reply_method_error(c, error);
|
||||
|
||||
fifo_fd = session_create_fifo(s);
|
||||
_cleanup_close_ int fifo_fd = session_create_fifo(s);
|
||||
if (fifo_fd < 0)
|
||||
return fifo_fd;
|
||||
|
||||
/* Update the session state file before we notify the client about the result. */
|
||||
session_save(s);
|
||||
|
||||
p = session_bus_path(s);
|
||||
_cleanup_free_ char *p = session_bus_path(s);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
log_debug("Sending reply about created session: "
|
||||
"id=%s object_path=%s uid=%u runtime_path=%s "
|
||||
log_debug("Sending D-Bus reply about created session: "
|
||||
"id=%s object_path=%s uid=" UID_FMT " runtime_path=%s "
|
||||
"session_fd=%d seat=%s vtnr=%u",
|
||||
s->id,
|
||||
p,
|
||||
(uint32_t) s->user->user_record->uid,
|
||||
s->user->user_record->uid,
|
||||
s->user->runtime_path,
|
||||
fifo_fd,
|
||||
s->seat ? s->seat->id : "",
|
||||
(uint32_t) s->vtnr);
|
||||
s->vtnr);
|
||||
|
||||
return sd_bus_reply_method_return(
|
||||
c, "soshusub",
|
||||
@ -968,7 +946,7 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
|
||||
false);
|
||||
}
|
||||
|
||||
int session_send_upgrade_reply(Session *s, sd_bus_error *error) {
|
||||
int session_send_upgrade_reply(Session *s, const sd_bus_error *error) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
|
||||
assert(s);
|
||||
|
||||
|
@ -15,8 +15,8 @@ int session_send_changed(Session *s, const char *properties, ...) _sentinel_;
|
||||
int session_send_lock(Session *s, bool lock);
|
||||
int session_send_lock_all(Manager *m, bool lock);
|
||||
|
||||
int session_send_create_reply(Session *s, sd_bus_error *error);
|
||||
int session_send_upgrade_reply(Session *s, sd_bus_error *error);
|
||||
int session_send_create_reply_bus(Session *s, const sd_bus_error *error);
|
||||
int session_send_upgrade_reply(Session *s, const sd_bus_error *error);
|
||||
|
||||
int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "logind-session-dbus.h"
|
||||
#include "logind-session.h"
|
||||
#include "logind-user-dbus.h"
|
||||
#include "logind-varlink.h"
|
||||
#include "mkdir-label.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
@ -192,6 +193,8 @@ Session* session_free(Session *s) {
|
||||
sd_bus_message_unref(s->create_message);
|
||||
sd_bus_message_unref(s->upgrade_message);
|
||||
|
||||
sd_varlink_unref(s->create_link);
|
||||
|
||||
free(s->tty);
|
||||
free(s->display);
|
||||
free(s->remote_host);
|
||||
@ -1648,6 +1651,35 @@ void session_drop_controller(Session *s) {
|
||||
session_restore_vt(s);
|
||||
}
|
||||
|
||||
bool session_job_pending(Session *s) {
|
||||
assert(s);
|
||||
assert(s->user);
|
||||
|
||||
/* Check if we have some jobs enqueued and not finished yet. Each time we get JobRemoved signal about
|
||||
* relevant units, session_send_create_reply and hence us is called (see match_job_removed).
|
||||
* Note that we don't care about job result here. */
|
||||
|
||||
return s->scope_job ||
|
||||
s->user->runtime_dir_job ||
|
||||
(SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) && s->user->service_manager_job);
|
||||
}
|
||||
|
||||
int session_send_create_reply(Session *s, const sd_bus_error *error) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
/* If error occurred, return it immediately. Otherwise let's wait for all jobs to finish before
|
||||
* continuing. */
|
||||
if (!sd_bus_error_is_set(error) && session_job_pending(s))
|
||||
return 0;
|
||||
|
||||
r = 0;
|
||||
RET_GATHER(r, session_send_create_reply_bus(s, error));
|
||||
RET_GATHER(r, session_send_create_reply_varlink(s, error));
|
||||
return r;
|
||||
}
|
||||
|
||||
static const char* const session_state_table[_SESSION_STATE_MAX] = {
|
||||
[SESSION_OPENING] = "opening",
|
||||
[SESSION_ONLINE] = "online",
|
||||
|
@ -150,6 +150,8 @@ struct Session {
|
||||
sd_bus_message *create_message; /* The D-Bus message used to create the session, which we haven't responded to yet */
|
||||
sd_bus_message *upgrade_message; /* The D-Bus message used to upgrade the session class user-incomplete → user, which we haven't responded to yet */
|
||||
|
||||
sd_varlink *create_link; /* The Varlink connection used to create session, which we haven't responded to yet */
|
||||
|
||||
/* Set up when a client requested to release the session via the bus */
|
||||
sd_event_source *timer_event_source;
|
||||
|
||||
@ -216,6 +218,10 @@ bool session_is_controller(Session *s, const char *sender);
|
||||
int session_set_controller(Session *s, const char *sender, bool force, bool prepare);
|
||||
void session_drop_controller(Session *s);
|
||||
|
||||
bool session_job_pending(Session *s);
|
||||
|
||||
int session_send_create_reply(Session *s, const sd_bus_error *error);
|
||||
|
||||
static inline bool SESSION_IS_SELF(const char *name) {
|
||||
return isempty(name) || streq(name, "self");
|
||||
}
|
||||
|
383
src/login/logind-varlink.c
Normal file
383
src/login/logind-varlink.c
Normal file
@ -0,0 +1,383 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "cgroup-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "json-util.h"
|
||||
#include "logind.h"
|
||||
#include "logind-dbus.h"
|
||||
#include "logind-session-dbus.h"
|
||||
#include "logind-varlink.h"
|
||||
#include "terminal-util.h"
|
||||
#include "user-util.h"
|
||||
#include "varlink-io.systemd.Login.h"
|
||||
#include "varlink-util.h"
|
||||
|
||||
static int manager_varlink_get_session_by_peer(
|
||||
Manager *m,
|
||||
sd_varlink *link,
|
||||
bool consult_display,
|
||||
Session **ret) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(link);
|
||||
assert(ret);
|
||||
|
||||
/* Determines the session of the peer. If the peer is not part of a session, but consult_display is
|
||||
* true, then will return the display session of the peer's owning user */
|
||||
|
||||
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
|
||||
r = varlink_get_peer_pidref(link, &pidref);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire peer PID: %m");
|
||||
|
||||
Session *session = NULL;
|
||||
_cleanup_free_ char *name = NULL;
|
||||
r = cg_pidref_get_session(&pidref, &name);
|
||||
if (r < 0) {
|
||||
if (!consult_display)
|
||||
log_debug_errno(r, "Failed to acquire session of peer, giving up: %m");
|
||||
else {
|
||||
log_debug_errno(r, "Failed to acquire session of peer, trying to find owner UID: %m");
|
||||
|
||||
uid_t uid;
|
||||
r = cg_pidref_get_owner_uid(&pidref, &uid);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to acquire owning UID of peer, giving up: %m");
|
||||
else {
|
||||
User *user = hashmap_get(m->users, UID_TO_PTR(uid));
|
||||
if (user)
|
||||
session = user->display;
|
||||
}
|
||||
}
|
||||
} else
|
||||
session = hashmap_get(m->sessions, name);
|
||||
|
||||
if (!session)
|
||||
return sd_varlink_error(link, "io.systemd.Login.NoSuchSession", /* parameters= */ NULL);
|
||||
|
||||
*ret = session;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manager_varlink_get_session_by_name(
|
||||
Manager *m,
|
||||
sd_varlink *link,
|
||||
const char *name,
|
||||
Session **ret) {
|
||||
|
||||
assert(m);
|
||||
assert(link);
|
||||
assert(ret);
|
||||
|
||||
/* Resolves a session name to a session object. Supports resolving the special names "self" and "auto". */
|
||||
|
||||
if (SESSION_IS_SELF(name))
|
||||
return manager_varlink_get_session_by_peer(m, link, /* consult_display= */ false, ret);
|
||||
if (SESSION_IS_AUTO(name))
|
||||
return manager_varlink_get_session_by_peer(m, link, /* consult_display= */ true, ret);
|
||||
|
||||
Session *session = hashmap_get(m->sessions, name);
|
||||
if (!session)
|
||||
return sd_varlink_error(link, "io.systemd.Login.NoSuchSession", /* parameters= */ NULL);
|
||||
|
||||
*ret = session;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int session_send_create_reply_varlink(Session *s, const sd_bus_error *error) {
|
||||
assert(s);
|
||||
|
||||
/* This is called after the session scope and the user service were successfully created, and
|
||||
* finishes where manager_create_session() left off. */
|
||||
|
||||
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = TAKE_PTR(s->create_link);
|
||||
if (!vl)
|
||||
return 0;
|
||||
|
||||
if (sd_bus_error_is_set(error))
|
||||
return sd_varlink_error(vl, "io.systemd.Login.UnitAllocationFailed", /* parameters= */ NULL);
|
||||
|
||||
_cleanup_close_ int fifo_fd = session_create_fifo(s);
|
||||
if (fifo_fd < 0)
|
||||
return fifo_fd;
|
||||
|
||||
/* Update the session state file before we notify the client about the result. */
|
||||
session_save(s);
|
||||
|
||||
log_debug("Sending Varlink reply about created session: "
|
||||
"id=%s uid=" UID_FMT " runtime_path=%s "
|
||||
"session_fd=%d seat=%s vtnr=%u",
|
||||
s->id,
|
||||
s->user->user_record->uid,
|
||||
s->user->runtime_path,
|
||||
fifo_fd,
|
||||
s->seat ? s->seat->id : "",
|
||||
s->vtnr);
|
||||
|
||||
int fifo_fd_idx = sd_varlink_push_fd(vl, fifo_fd);
|
||||
if (fifo_fd_idx < 0) {
|
||||
log_error_errno(fifo_fd_idx, "Failed to push FIFO fd to Varlink: %m");
|
||||
return sd_varlink_error_errno(vl, fifo_fd_idx);
|
||||
}
|
||||
|
||||
TAKE_FD(fifo_fd);
|
||||
|
||||
return sd_varlink_replybo(
|
||||
vl,
|
||||
SD_JSON_BUILD_PAIR_STRING("Id", s->id),
|
||||
SD_JSON_BUILD_PAIR_STRING("RuntimePath", s->user->runtime_path),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("SessionFileDescriptor", fifo_fd_idx),
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("UID", s->user->user_record->uid),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(!!s->seat, "Seat", SD_JSON_BUILD_STRING(s->seat ? s->seat->id : NULL)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(s->vtnr > 0, "VTNr", SD_JSON_BUILD_UNSIGNED(s->vtnr)));
|
||||
}
|
||||
|
||||
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_session_class, SessionClass, session_class_from_string);
|
||||
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_session_type, SessionType, session_type_from_string);
|
||||
|
||||
typedef struct CreateSessionParameters {
|
||||
uid_t uid;
|
||||
PidRef pid;
|
||||
const char *service;
|
||||
SessionType type;
|
||||
SessionClass class;
|
||||
const char *desktop;
|
||||
const char *seat;
|
||||
unsigned vtnr;
|
||||
const char *tty;
|
||||
const char *display;
|
||||
int remote;
|
||||
const char *remote_user;
|
||||
const char *remote_host;
|
||||
} CreateSessionParameters;
|
||||
|
||||
static void create_session_parameters_done(CreateSessionParameters *p) {
|
||||
pidref_done(&p->pid);
|
||||
}
|
||||
|
||||
static int vl_method_create_session(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "UID", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(CreateSessionParameters, uid), SD_JSON_MANDATORY },
|
||||
{ "PID", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_pidref, offsetof(CreateSessionParameters, pid), SD_JSON_RELAX },
|
||||
{ "Service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, service), 0 },
|
||||
{ "Type", SD_JSON_VARIANT_STRING, json_dispatch_session_type, offsetof(CreateSessionParameters, type), SD_JSON_MANDATORY },
|
||||
{ "Class", SD_JSON_VARIANT_STRING, json_dispatch_session_class, offsetof(CreateSessionParameters, class), SD_JSON_MANDATORY },
|
||||
{ "Desktop", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, desktop), SD_JSON_STRICT },
|
||||
{ "Seat", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, seat), 0 },
|
||||
{ "VTNr", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, offsetof(CreateSessionParameters, vtnr), 0 },
|
||||
{ "TTY", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, tty), 0 },
|
||||
{ "Display", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, display), 0 },
|
||||
{ "Remote", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(CreateSessionParameters, remote), 0 },
|
||||
{ "RemoteUser", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, remote_user), 0 },
|
||||
{ "RemoteHost", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, remote_host), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
_cleanup_(create_session_parameters_done) CreateSessionParameters p = {
|
||||
.uid = UID_INVALID,
|
||||
.pid = PIDREF_NULL,
|
||||
.class = _SESSION_CLASS_INVALID,
|
||||
.type = _SESSION_TYPE_INVALID,
|
||||
.remote = -1,
|
||||
};
|
||||
|
||||
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
Seat *seat = NULL;
|
||||
if (p.seat) {
|
||||
seat = hashmap_get(m->seats, p.seat);
|
||||
if (!seat)
|
||||
return sd_varlink_replyb(link, "io.systemd.Login.NoSuchSeat", /* parameters= */ NULL);
|
||||
}
|
||||
|
||||
if (p.tty) {
|
||||
if (tty_is_vc(p.tty)) {
|
||||
if (!seat)
|
||||
seat = m->seat0;
|
||||
else if (seat != m->seat0)
|
||||
return sd_varlink_error_invalid_parameter_name(link, "Seat");
|
||||
|
||||
int v = vtnr_from_tty(p.tty);
|
||||
if (v <= 0)
|
||||
return sd_varlink_error_invalid_parameter_name(link, "TTY");
|
||||
|
||||
if (p.vtnr == 0)
|
||||
p.vtnr = v;
|
||||
else if (p.vtnr != (unsigned) v)
|
||||
return sd_varlink_error_invalid_parameter_name(link, "VTNr");
|
||||
|
||||
} else if (tty_is_console(p.tty)) {
|
||||
if (!seat)
|
||||
seat = m->seat0;
|
||||
else if (seat != m->seat0)
|
||||
return sd_varlink_error_invalid_parameter_name(link, "Seat");
|
||||
|
||||
if (p.vtnr != 0)
|
||||
return sd_varlink_error_invalid_parameter_name(link, "VTNr");
|
||||
}
|
||||
}
|
||||
|
||||
if (seat) {
|
||||
if (seat_has_vts(seat)) {
|
||||
if (!vtnr_is_valid(p.vtnr))
|
||||
return sd_varlink_error_invalid_parameter_name(link, "VTNr");
|
||||
} else {
|
||||
if (p.vtnr != 0)
|
||||
return sd_varlink_error_invalid_parameter_name(link, "VTNr");
|
||||
}
|
||||
}
|
||||
|
||||
if (p.remote < 0)
|
||||
p.remote = p.remote_user || p.remote_host;
|
||||
|
||||
/* Before we continue processing this, let's ensure the peer is privileged */
|
||||
uid_t peer_uid;
|
||||
r = sd_varlink_get_peer_uid(link, &peer_uid);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to get peer UID: %m");
|
||||
if (peer_uid != 0)
|
||||
return sd_varlink_error(link, SD_VARLINK_ERROR_PERMISSION_DENIED, /* parameters= */ NULL);
|
||||
|
||||
if (!pidref_is_set(&p.pid)) {
|
||||
r = varlink_get_peer_pidref(link, &p.pid);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to get peer pidref: %m");
|
||||
}
|
||||
|
||||
Session *session;
|
||||
r = manager_create_session(
|
||||
m,
|
||||
p.uid,
|
||||
&p.pid,
|
||||
p.service,
|
||||
p.type,
|
||||
p.class,
|
||||
p.desktop,
|
||||
seat,
|
||||
p.vtnr,
|
||||
p.tty,
|
||||
p.display,
|
||||
p.remote,
|
||||
p.remote_user,
|
||||
p.remote_host,
|
||||
&session);
|
||||
if (r == -EBUSY)
|
||||
return sd_varlink_error(link, "io.systemd.Login.AlreadySessionMember", /* parameters= */ NULL);
|
||||
if (r == -EADDRNOTAVAIL)
|
||||
return sd_varlink_error(link, "io.systemd.Login.VirtualTerminalAlreadyTaken", /* parameters= */ NULL);
|
||||
if (r == -EUSERS)
|
||||
return sd_varlink_error(link, "io.systemd.Login.TooManySessions", /* parameters= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = session_start(session, /* properties= */ NULL, /* error= */ NULL);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
session->create_link = sd_varlink_ref(link);
|
||||
|
||||
/* Let's check if this is complete now */
|
||||
r = session_send_create_reply(session, /* error= */ NULL);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
if (session)
|
||||
session_add_to_gc_queue(session);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int vl_method_release_session(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
struct {
|
||||
const char *id;
|
||||
} p;
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "Id", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, id), SD_JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
||||
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
Session *session;
|
||||
r = manager_varlink_get_session_by_name(m, link, p.id, &session);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
Session *peer_session;
|
||||
r = manager_varlink_get_session_by_peer(m, link, /* consult_display= */ false, &peer_session);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (session != peer_session)
|
||||
return sd_varlink_error(link, SD_VARLINK_ERROR_PERMISSION_DENIED, /* parameters= */ NULL);
|
||||
|
||||
r = session_release(session);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_varlink_replyb(link, NULL);
|
||||
}
|
||||
|
||||
int manager_varlink_init(Manager *m) {
|
||||
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (m->varlink_server)
|
||||
return 0;
|
||||
|
||||
r = sd_varlink_server_new(
|
||||
&s,
|
||||
SD_VARLINK_SERVER_ACCOUNT_UID|
|
||||
SD_VARLINK_SERVER_INHERIT_USERDATA|
|
||||
SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate varlink server object: %m");
|
||||
|
||||
sd_varlink_server_set_userdata(s, m);
|
||||
|
||||
r = sd_varlink_server_add_interface(s, &vl_interface_io_systemd_Login);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add Login interface to varlink server: %m");
|
||||
|
||||
r = sd_varlink_server_bind_method_many(
|
||||
s,
|
||||
"io.systemd.Login.CreateSession", vl_method_create_session,
|
||||
"io.systemd.Login.ReleaseSession", vl_method_release_session);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to register varlink methods: %m");
|
||||
|
||||
r = sd_varlink_server_listen_address(s, "/run/systemd/io.systemd.Login", 0666);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind to varlink socket: %m");
|
||||
|
||||
r = sd_varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
|
||||
|
||||
m->varlink_server = TAKE_PTR(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void manager_varlink_done(Manager *m) {
|
||||
assert(m);
|
||||
|
||||
m->varlink_server = sd_varlink_server_unref(m->varlink_server);
|
||||
}
|
12
src/login/logind-varlink.h
Normal file
12
src/login/logind-varlink.h
Normal file
@ -0,0 +1,12 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "sd-bus.h"
|
||||
|
||||
#include "logind.h"
|
||||
#include "logind-session.h"
|
||||
|
||||
int manager_varlink_init(Manager *m);
|
||||
void manager_varlink_done(Manager *m);
|
||||
|
||||
int session_send_create_reply_varlink(Session *s, const sd_bus_error *error);
|
@ -24,11 +24,12 @@
|
||||
#include "fd-util.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "logind.h"
|
||||
#include "logind-dbus.h"
|
||||
#include "logind-seat-dbus.h"
|
||||
#include "logind-session-dbus.h"
|
||||
#include "logind-user-dbus.h"
|
||||
#include "logind.h"
|
||||
#include "logind-varlink.h"
|
||||
#include "main-func.h"
|
||||
#include "mkdir-label.h"
|
||||
#include "parse-util.h"
|
||||
@ -154,6 +155,8 @@ static Manager* manager_free(Manager *m) {
|
||||
|
||||
hashmap_free(m->polkit_registry);
|
||||
|
||||
manager_varlink_done(m);
|
||||
|
||||
sd_bus_flush_close_unref(m->bus);
|
||||
sd_event_unref(m->event);
|
||||
|
||||
@ -1115,6 +1118,10 @@ static int manager_startup(Manager *m) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_varlink_init(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Instantiate magic seat 0 */
|
||||
r = manager_add_seat(m, "seat0", &m->seat0);
|
||||
if (r < 0)
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "sd-bus.h"
|
||||
#include "sd-device.h"
|
||||
#include "sd-event.h"
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "calendarspec.h"
|
||||
#include "conf-parser.h"
|
||||
@ -147,6 +148,8 @@ struct Manager {
|
||||
CalendarSpec *maintenance_time;
|
||||
|
||||
dual_timestamp init_ts;
|
||||
|
||||
sd_varlink_server *varlink_server;
|
||||
};
|
||||
|
||||
void manager_reset_config(Manager *m);
|
||||
|
@ -26,6 +26,7 @@ liblogind_core_sources = files(
|
||||
'logind-session.c',
|
||||
'logind-user-dbus.c',
|
||||
'logind-user.c',
|
||||
'logind-varlink.c',
|
||||
'logind-wall.c',
|
||||
)
|
||||
|
||||
|
@ -18,6 +18,9 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-bus.h"
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "audit-util.h"
|
||||
#include "bus-common-errors.h"
|
||||
@ -35,6 +38,7 @@
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "hostname-util.h"
|
||||
#include "json-util.h"
|
||||
#include "locale-util.h"
|
||||
#include "login-util.h"
|
||||
#include "macro.h"
|
||||
@ -1022,6 +1026,16 @@ static void session_context_mangle(
|
||||
c->remote = !isempty(c->remote_host) && !is_localhost(c->remote_host);
|
||||
}
|
||||
|
||||
static bool can_use_varlink(const SessionContext *c) {
|
||||
/* Since PID 1 currently doesn't do Varlink right now, we cannot directly set properties for the
|
||||
* scope, for now. */
|
||||
return !c->memory_max &&
|
||||
!c->runtime_max_sec &&
|
||||
!c->tasks_max &&
|
||||
!c->cpu_weight &&
|
||||
!c->io_weight;
|
||||
}
|
||||
|
||||
static int register_session(
|
||||
pam_handle_t *handle,
|
||||
SessionContext *c,
|
||||
@ -1029,13 +1043,6 @@ static int register_session(
|
||||
bool debug,
|
||||
char **ret_seat) {
|
||||
|
||||
/* Let's release the D-Bus connection once this function exits, after all the session might live
|
||||
* quite a long time, and we are not going to process the bus connection in that time, so let's
|
||||
* better close before the daemon kicks us off because we are not processing anything. */
|
||||
_cleanup_(pam_bus_data_disconnectp) PamBusData *d = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
int r;
|
||||
|
||||
assert(handle);
|
||||
@ -1044,13 +1051,10 @@ static int register_session(
|
||||
assert(ret_seat);
|
||||
|
||||
/* Make most of this a NOP on non-logind systems */
|
||||
if (!logind_running())
|
||||
goto skip;
|
||||
|
||||
/* Talk to logind over the message bus */
|
||||
r = pam_acquire_bus_connection(handle, "pam-systemd", debug, &bus, &d);
|
||||
if (r != PAM_SUCCESS)
|
||||
return r;
|
||||
if (!logind_running()) {
|
||||
*ret_seat = NULL;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
pam_debug_syslog(handle, debug,
|
||||
"Asking logind to create session: "
|
||||
@ -1065,68 +1069,187 @@ static int register_session(
|
||||
"memory_max=%s tasks_max=%s cpu_weight=%s io_weight=%s runtime_max_sec=%s",
|
||||
strna(c->memory_max), strna(c->tasks_max), strna(c->cpu_weight), strna(c->io_weight), strna(c->runtime_max_sec));
|
||||
|
||||
r = create_session_message(
|
||||
bus,
|
||||
handle,
|
||||
ur,
|
||||
c,
|
||||
/* avoid_pidfd = */ false,
|
||||
&m);
|
||||
if (r < 0)
|
||||
return pam_bus_log_create_error(handle, r);
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; /* the following variables point into this message, hence pin it for longer */
|
||||
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; /* similar */
|
||||
const char *id = NULL, *object_path = NULL, *runtime_path = NULL, *real_seat = NULL;
|
||||
int session_fd = -EBADF, existing = false;
|
||||
uint32_t original_uid = UID_INVALID, real_vtnr = 0;
|
||||
|
||||
r = sd_bus_call(bus, m, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
|
||||
if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
|
||||
sd_bus_error_free(&error);
|
||||
pam_debug_syslog(handle, debug,
|
||||
"CreateSessionWithPIDFD() API is not available, retrying with CreateSession().");
|
||||
bool done = false;
|
||||
if (can_use_varlink(c)) {
|
||||
|
||||
m = sd_bus_message_unref(m);
|
||||
r = create_session_message(bus,
|
||||
handle,
|
||||
ur,
|
||||
c,
|
||||
/* avoid_pidfd = */ true,
|
||||
&m);
|
||||
r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.Login");
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to connect to logind via Varlink, falling back to D-Bus: %m");
|
||||
else {
|
||||
r = sd_varlink_set_allow_fd_passing_input(vl, true);
|
||||
if (r < 0)
|
||||
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to enable input fd passing on Varlink socket: %m");
|
||||
|
||||
r = sd_varlink_set_allow_fd_passing_output(vl, true);
|
||||
if (r < 0)
|
||||
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to enable output fd passing on Varlink socket: %m");
|
||||
|
||||
r = sd_varlink_set_relative_timeout(vl, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC);
|
||||
if (r < 0)
|
||||
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to set relative timeout on Varlink socket: %m");
|
||||
|
||||
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
|
||||
r = pidref_set_self(&pidref);
|
||||
if (r < 0)
|
||||
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to acquire PID reference on ourselves: %m");
|
||||
|
||||
sd_json_variant *vreply = NULL;
|
||||
const char *error_id = NULL;
|
||||
r = sd_varlink_callbo(
|
||||
vl,
|
||||
"io.systemd.Login.CreateSession",
|
||||
&vreply,
|
||||
&error_id,
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("UID", ur->uid),
|
||||
JSON_BUILD_PAIR_PIDREF("PID", &pidref),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("Service", c->service),
|
||||
SD_JSON_BUILD_PAIR("Type", JSON_BUILD_STRING_UNDERSCORIFY(c->type)),
|
||||
SD_JSON_BUILD_PAIR("Class", JSON_BUILD_STRING_UNDERSCORIFY(c->class)),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("Desktop", c->desktop),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("Seat", c->seat),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(c->vtnr > 0, "VTNr", SD_JSON_BUILD_UNSIGNED(c->vtnr)),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("TTY", c->tty),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("Display", c->display),
|
||||
SD_JSON_BUILD_PAIR_BOOLEAN("Remote", c->remote),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("RemoteUser", c->remote_user),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("RemoteHost", c->remote_host));
|
||||
if (r < 0)
|
||||
return pam_syslog_errno(handle, LOG_ERR, r,
|
||||
"Failed to register session: %s", error_id);
|
||||
if (streq_ptr(error_id, "io.systemd.Login.AlreadySessionMember")) {
|
||||
/* We are already in a session, don't do anything */
|
||||
pam_debug_syslog(handle, debug, "Not creating session: %s", error_id);
|
||||
*ret_seat = NULL;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
if (error_id)
|
||||
return pam_syslog_errno(handle, LOG_ERR, sd_varlink_error_to_errno(error_id, vreply),
|
||||
"Failed to issue CreateSession() varlink call: %s", error_id);
|
||||
|
||||
struct {
|
||||
const char *id;
|
||||
const char *runtime_path;
|
||||
unsigned session_fd_idx;
|
||||
uid_t uid;
|
||||
const char *seat;
|
||||
unsigned vtnr;
|
||||
bool existing;
|
||||
} p = {
|
||||
.session_fd_idx = UINT_MAX,
|
||||
.uid = UID_INVALID,
|
||||
};
|
||||
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "Id", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, id), SD_JSON_MANDATORY },
|
||||
{ "RuntimePath", SD_JSON_VARIANT_STRING, json_dispatch_const_path, voffsetof(p, runtime_path), SD_JSON_MANDATORY },
|
||||
{ "SessionFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, session_fd_idx), SD_JSON_MANDATORY },
|
||||
{ "UID", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, voffsetof(p, uid), SD_JSON_MANDATORY },
|
||||
{ "Seat", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, seat), 0 },
|
||||
{ "VTNr", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, voffsetof(p, vtnr), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
r = sd_json_dispatch(vreply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
|
||||
if (r < 0)
|
||||
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to parse CreateSession() reply: %m");
|
||||
|
||||
session_fd = sd_varlink_peek_fd(vl, p.session_fd_idx);
|
||||
if (session_fd < 0)
|
||||
return pam_syslog_errno(handle, LOG_ERR, session_fd, "Failed to extract session fd from CreateSession() reply: %m");
|
||||
|
||||
id = p.id;
|
||||
runtime_path = p.runtime_path;
|
||||
original_uid = p.uid;
|
||||
real_seat = p.seat;
|
||||
real_vtnr = p.vtnr;
|
||||
existing = false; /* Even on D-Bus logind only returns false these days */
|
||||
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
/* Let's release the D-Bus connection once we are done here, after all the session might live
|
||||
* quite a long time, and we are not going to process the bus connection in that time, so
|
||||
* let's better close before the daemon kicks us off because we are not processing
|
||||
* anything. */
|
||||
_cleanup_(pam_bus_data_disconnectp) PamBusData *d = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
|
||||
/* Talk to logind over the message bus */
|
||||
r = pam_acquire_bus_connection(handle, "pam-systemd", debug, &bus, &d);
|
||||
if (r != PAM_SUCCESS)
|
||||
return r;
|
||||
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||
r = create_session_message(
|
||||
bus,
|
||||
handle,
|
||||
ur,
|
||||
c,
|
||||
/* avoid_pidfd = */ false,
|
||||
&m);
|
||||
if (r < 0)
|
||||
return pam_bus_log_create_error(handle, r);
|
||||
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
r = sd_bus_call(bus, m, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
|
||||
}
|
||||
if (r < 0) {
|
||||
if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) {
|
||||
/* We are already in a session, don't do anything */
|
||||
if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
|
||||
sd_bus_error_free(&error);
|
||||
pam_debug_syslog(handle, debug,
|
||||
"Not creating session: %s", bus_error_message(&error, r));
|
||||
goto skip;
|
||||
"CreateSessionWithPIDFD() API is not available, retrying with CreateSession().");
|
||||
|
||||
m = sd_bus_message_unref(m);
|
||||
r = create_session_message(bus,
|
||||
handle,
|
||||
ur,
|
||||
c,
|
||||
/* avoid_pidfd = */ true,
|
||||
&m);
|
||||
if (r < 0)
|
||||
return pam_bus_log_create_error(handle, r);
|
||||
|
||||
r = sd_bus_call(bus, m, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
|
||||
}
|
||||
if (r < 0) {
|
||||
if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) {
|
||||
/* We are already in a session, don't do anything */
|
||||
pam_debug_syslog(handle, debug,
|
||||
"Not creating session: %s", bus_error_message(&error, r));
|
||||
*ret_seat = NULL;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
pam_syslog(handle, LOG_ERR,
|
||||
"Failed to create session: %s", bus_error_message(&error, r));
|
||||
return PAM_SESSION_ERR;
|
||||
}
|
||||
|
||||
pam_syslog(handle, LOG_ERR,
|
||||
"Failed to create session: %s", bus_error_message(&error, r));
|
||||
return PAM_SESSION_ERR;
|
||||
r = sd_bus_message_read(
|
||||
reply,
|
||||
"soshusub",
|
||||
&id,
|
||||
&object_path,
|
||||
&runtime_path,
|
||||
&session_fd,
|
||||
&original_uid,
|
||||
&real_seat,
|
||||
&real_vtnr,
|
||||
&existing);
|
||||
if (r < 0)
|
||||
return pam_bus_log_parse_error(handle, r);
|
||||
}
|
||||
|
||||
const char *id, *object_path, *runtime_path, *real_seat;
|
||||
int session_fd = -EBADF, existing;
|
||||
uint32_t original_uid, real_vtnr;
|
||||
r = sd_bus_message_read(
|
||||
reply,
|
||||
"soshusub",
|
||||
&id,
|
||||
&object_path,
|
||||
&runtime_path,
|
||||
&session_fd,
|
||||
&original_uid,
|
||||
&real_seat,
|
||||
&real_vtnr,
|
||||
&existing);
|
||||
if (r < 0)
|
||||
return pam_bus_log_parse_error(handle, r);
|
||||
|
||||
pam_debug_syslog(handle, debug,
|
||||
"Reply from logind: "
|
||||
"id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u",
|
||||
id, object_path, runtime_path, session_fd, real_seat, real_vtnr, original_uid);
|
||||
id, strna(object_path), runtime_path, session_fd, real_seat, real_vtnr, original_uid);
|
||||
|
||||
/* Please update manager_default_environment() in core/manager.c accordingly if more session envvars
|
||||
* shall be added. */
|
||||
@ -1191,18 +1314,17 @@ static int register_session(
|
||||
|
||||
/* Everything worked, hence let's patch in the data we learned. Since 'real_set' points into the
|
||||
* D-Bus message, let's copy it and return it as a buffer */
|
||||
char *rs = strdup(real_seat);
|
||||
if (!rs)
|
||||
return pam_log_oom(handle);
|
||||
char *rs = NULL;
|
||||
if (real_seat) {
|
||||
rs = strdup(real_seat);
|
||||
if (!rs)
|
||||
return pam_log_oom(handle);
|
||||
}
|
||||
|
||||
c->seat = *ret_seat = rs;
|
||||
c->vtnr = real_vtnr;
|
||||
|
||||
return PAM_SUCCESS;
|
||||
|
||||
skip:
|
||||
*ret_seat = NULL;
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
static int import_shell_credentials(pam_handle_t *handle) {
|
||||
@ -1337,20 +1459,47 @@ _public_ PAM_EXTERN int pam_sm_close_session(
|
||||
|
||||
id = pam_getenv(handle, "XDG_SESSION_ID");
|
||||
if (id && !existing) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
|
||||
bool done = false;
|
||||
|
||||
/* Before we go and close the FIFO we need to tell logind that this is a clean session
|
||||
* shutdown, so that it doesn't just go and slaughter us immediately after closing the fd */
|
||||
|
||||
r = pam_acquire_bus_connection(handle, "pam-systemd", debug, &bus, NULL);
|
||||
if (r != PAM_SUCCESS)
|
||||
return r;
|
||||
|
||||
r = bus_call_method(bus, bus_login_mgr, "ReleaseSession", &error, NULL, "s", id);
|
||||
r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.Login");
|
||||
if (r < 0)
|
||||
return pam_syslog_pam_error(handle, LOG_ERR, PAM_SESSION_ERR,
|
||||
"Failed to release session: %s", bus_error_message(&error, r));
|
||||
log_debug_errno(r, "Failed to connect to logind via Varlink, falling back to D-Bus: %m");
|
||||
else {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *vreply = NULL;
|
||||
const char *error_id = NULL;
|
||||
r = sd_varlink_callbo(
|
||||
vl,
|
||||
"io.systemd.Login.ReleaseSession",
|
||||
/* ret_reply= */ NULL,
|
||||
&error_id,
|
||||
SD_JSON_BUILD_PAIR_STRING("Id", id));
|
||||
if (r < 0)
|
||||
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to register session: %s", error_id);
|
||||
if (error_id)
|
||||
return pam_syslog_errno(handle, LOG_ERR, sd_varlink_error_to_errno(error_id, vreply),
|
||||
"Failed to issue ReleaseSession() varlink call: %s", error_id);
|
||||
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(pam_bus_data_disconnectp) PamBusData *d = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
|
||||
/* Before we go and close the FIFO we need to tell logind that this is a clean session
|
||||
* shutdown, so that it doesn't just go and slaughter us immediately after closing the fd */
|
||||
|
||||
r = pam_acquire_bus_connection(handle, "pam-systemd", debug, &bus, &d);
|
||||
if (r != PAM_SUCCESS)
|
||||
return r;
|
||||
|
||||
r = bus_call_method(bus, bus_login_mgr, "ReleaseSession", &error, NULL, "s", id);
|
||||
if (r < 0)
|
||||
return pam_syslog_pam_error(handle, LOG_ERR, PAM_SESSION_ERR,
|
||||
"Failed to release session: %s", bus_error_message(&error, r));
|
||||
}
|
||||
}
|
||||
|
||||
/* Note that we are knowingly leaking the FIFO fd here. This way, logind can watch us die. If we
|
||||
|
@ -178,7 +178,7 @@ int bus_image_method_clone(
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
|
||||
if (r == 0) {
|
||||
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
|
||||
r = image_clone(image, new_name, read_only);
|
||||
r = image_clone(image, new_name, read_only, m->runtime_scope);
|
||||
report_errno_and_exit(errno_pipe_fd[1], r);
|
||||
}
|
||||
|
||||
@ -402,6 +402,7 @@ char* image_bus_path(const char *name) {
|
||||
static int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
|
||||
_cleanup_hashmap_free_ Hashmap *images = NULL;
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
Image *image;
|
||||
int r;
|
||||
|
||||
@ -413,7 +414,7 @@ static int image_node_enumerator(sd_bus *bus, const char *path, void *userdata,
|
||||
if (!images)
|
||||
return -ENOMEM;
|
||||
|
||||
r = image_discover(IMAGE_MACHINE, NULL, images);
|
||||
r = image_discover(m->runtime_scope, IMAGE_MACHINE, NULL, images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -148,7 +148,7 @@ int vl_method_clone_image(sd_varlink *link, sd_json_variant *parameters, sd_varl
|
||||
return log_debug_errno(r, "Failed to fork: %m");
|
||||
if (r == 0) {
|
||||
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
|
||||
r = image_clone(image, p.new_name, p.read_only > 0);
|
||||
r = image_clone(image, p.new_name, p.read_only > 0, manager->runtime_scope);
|
||||
report_errno_and_exit(errno_pipe_fd[1], r);
|
||||
}
|
||||
|
||||
|
@ -1188,35 +1188,29 @@ static int bind_mount(int argc, char *argv[], void *userdata) {
|
||||
}
|
||||
|
||||
static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
|
||||
PTYForward ** forward = (PTYForward**) userdata;
|
||||
PTYForward *forward = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(forward);
|
||||
|
||||
if (*forward) {
|
||||
/* If the forwarder is already initialized, tell it to
|
||||
* exit on the next vhangup(), so that we still flush
|
||||
* out what might be queued and exit then. */
|
||||
|
||||
r = pty_forward_set_ignore_vhangup(*forward, false);
|
||||
if (r >= 0)
|
||||
return 0;
|
||||
/* Tell the forwarder to exit on the next vhangup(), so that we still flush out what might be queued
|
||||
* and exit then. */
|
||||
|
||||
r = pty_forward_set_ignore_vhangup(forward, false);
|
||||
if (r < 0) {
|
||||
/* On error, quit immediately. */
|
||||
log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
|
||||
(void) sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* On error, or when the forwarder is not initialized yet, quit immediately */
|
||||
sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_forward(sd_event *event, PTYForward **forward, int master, PTYForwardFlags flags, const char *name) {
|
||||
char last_char = 0;
|
||||
bool machine_died;
|
||||
static int process_forward(sd_event *event, sd_bus_slot *machine_removed_slot, int master, PTYForwardFlags flags, const char *name) {
|
||||
int r;
|
||||
|
||||
assert(event);
|
||||
assert(machine_removed_slot);
|
||||
assert(master >= 0);
|
||||
assert(name);
|
||||
|
||||
@ -1231,24 +1225,21 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, PT
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enable SIGINT/SITERM handling: %m");
|
||||
|
||||
r = pty_forward_new(event, master, flags, forward);
|
||||
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
|
||||
r = pty_forward_new(event, master, flags, &forward);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create PTY forwarder: %m");
|
||||
|
||||
/* No userdata should not set previously. */
|
||||
assert_se(!sd_bus_slot_set_userdata(machine_removed_slot, forward));
|
||||
|
||||
r = sd_event_loop(event);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to run event loop: %m");
|
||||
|
||||
pty_forward_get_last_char(*forward, &last_char);
|
||||
|
||||
machine_died =
|
||||
bool machine_died =
|
||||
(flags & PTY_FORWARD_IGNORE_VHANGUP) &&
|
||||
pty_forward_get_ignore_vhangup(*forward) == 0;
|
||||
|
||||
*forward = pty_forward_free(*forward);
|
||||
|
||||
if (last_char != '\n')
|
||||
fputc('\n', stdout);
|
||||
pty_forward_get_ignore_vhangup(forward) == 0;
|
||||
|
||||
if (!arg_quiet) {
|
||||
if (machine_died)
|
||||
@ -1300,7 +1291,6 @@ static int parse_machine_uid(const char *spec, const char **machine, char **uid)
|
||||
static int login_machine(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
|
||||
_cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
|
||||
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||
int master = -1, r;
|
||||
@ -1334,7 +1324,7 @@ static int login_machine(int argc, char *argv[], void *userdata) {
|
||||
"member='MachineRemoved',"
|
||||
"arg0='", machine, "'");
|
||||
|
||||
r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward);
|
||||
r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to request machine removal match: %m");
|
||||
|
||||
@ -1346,13 +1336,12 @@ static int login_machine(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
return process_forward(event, &forward, master, PTY_FORWARD_IGNORE_VHANGUP, machine);
|
||||
return process_forward(event, slot, master, PTY_FORWARD_IGNORE_VHANGUP, machine);
|
||||
}
|
||||
|
||||
static int shell_machine(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
|
||||
_cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
|
||||
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||
int master = -1, r;
|
||||
@ -1396,7 +1385,7 @@ static int shell_machine(int argc, char *argv[], void *userdata) {
|
||||
"member='MachineRemoved',"
|
||||
"arg0='", machine, "'");
|
||||
|
||||
r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward);
|
||||
r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to request machine removal match: %m");
|
||||
|
||||
@ -1426,7 +1415,7 @@ static int shell_machine(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
return process_forward(event, &forward, master, 0, machine);
|
||||
return process_forward(event, slot, master, /* flags = */ 0, machine);
|
||||
}
|
||||
|
||||
static int normalize_nspawn_filename(const char *name, char **ret_file) {
|
||||
|
@ -440,7 +440,7 @@ int manager_acquire_image(Manager *m, const char *name, Image **ret) {
|
||||
return log_debug_errno(r, "Failed to enable source: %m") ;
|
||||
|
||||
_cleanup_(image_unrefp) Image *image = NULL;
|
||||
r = image_find(IMAGE_MACHINE, name, NULL, &image);
|
||||
r = image_find(m->runtime_scope, IMAGE_MACHINE, name, NULL, &image);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to find image: %m");
|
||||
|
||||
@ -467,7 +467,7 @@ int rename_image_and_update_cache(Manager *m, Image *image, const char* new_name
|
||||
/* The image is cached with its name, hence it is necessary to remove from the cache before renaming. */
|
||||
assert_se(hashmap_remove_value(m->image_cache, image->name, image));
|
||||
|
||||
r = image_rename(image, new_name);
|
||||
r = image_rename(image, new_name, m->runtime_scope);
|
||||
if (r < 0) {
|
||||
image = image_unref(image);
|
||||
return r;
|
||||
|
@ -123,7 +123,7 @@ static int method_get_image(sd_bus_message *message, void *userdata, sd_bus_erro
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = image_find(IMAGE_MACHINE, name, NULL, NULL);
|
||||
r = image_find(m->runtime_scope, IMAGE_MACHINE, name, NULL, NULL);
|
||||
if (r == -ENOENT)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
|
||||
if (r < 0)
|
||||
@ -476,7 +476,7 @@ static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_er
|
||||
if (!images)
|
||||
return -ENOMEM;
|
||||
|
||||
r = image_discover(IMAGE_MACHINE, NULL, images);
|
||||
r = image_discover(m->runtime_scope, IMAGE_MACHINE, NULL, images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -753,7 +753,7 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err
|
||||
goto child_fail;
|
||||
}
|
||||
|
||||
r = image_discover(IMAGE_MACHINE, NULL, images);
|
||||
r = image_discover(m->runtime_scope, IMAGE_MACHINE, NULL, images);
|
||||
if (r < 0)
|
||||
goto child_fail;
|
||||
|
||||
|
@ -641,6 +641,7 @@ static int list_image_one_and_maybe_read_metadata(sd_varlink *link, Image *image
|
||||
}
|
||||
|
||||
static int vl_method_list_images(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
struct params {
|
||||
const char *image_name;
|
||||
AcquireMetadata acquire_metadata;
|
||||
@ -667,7 +668,7 @@ static int vl_method_list_images(sd_varlink *link, sd_json_variant *parameters,
|
||||
if (!image_name_is_valid(p.image_name))
|
||||
return sd_varlink_error_invalid_parameter_name(link, "name");
|
||||
|
||||
r = image_find(IMAGE_MACHINE, p.image_name, /* root = */ NULL, &found);
|
||||
r = image_find(m->runtime_scope, IMAGE_MACHINE, p.image_name, /* root = */ NULL, &found);
|
||||
if (r == -ENOENT)
|
||||
return sd_varlink_error(link, "io.systemd.MachineImage.NoSuchImage", NULL);
|
||||
if (r < 0)
|
||||
@ -683,7 +684,7 @@ static int vl_method_list_images(sd_varlink *link, sd_json_variant *parameters,
|
||||
if (!images)
|
||||
return -ENOMEM;
|
||||
|
||||
r = image_discover(IMAGE_MACHINE, /* root = */ NULL, images);
|
||||
r = image_discover(m->runtime_scope, IMAGE_MACHINE, /* root = */ NULL, images);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to discover images: %m");
|
||||
|
||||
|
@ -40,10 +40,14 @@ static int manager_new(Manager **ret) {
|
||||
|
||||
assert(ret);
|
||||
|
||||
m = new0(Manager, 1);
|
||||
m = new(Manager, 1);
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
|
||||
*m = (Manager) {
|
||||
.runtime_scope = RUNTIME_SCOPE_SYSTEM,
|
||||
};
|
||||
|
||||
m->machines = hashmap_new(&machine_hash_ops);
|
||||
if (!m->machines)
|
||||
return -ENOMEM;
|
||||
|
@ -42,6 +42,8 @@ struct Manager {
|
||||
|
||||
sd_varlink_server *varlink_userdb_server;
|
||||
sd_varlink_server *varlink_machine_server;
|
||||
|
||||
RuntimeScope runtime_scope; /* for now: always RUNTIME_SCOPE_SYSTEM */
|
||||
};
|
||||
|
||||
int manager_add_machine(Manager *m, const char *name, Machine **ret);
|
||||
|
@ -252,7 +252,7 @@ static void link_free_engines(Link *link) {
|
||||
static Link *link_free(Link *link) {
|
||||
assert(link);
|
||||
|
||||
(void) sysctl_clear_link_shadows(link);
|
||||
(void) link_clear_sysctl_shadows(link);
|
||||
|
||||
link_ntp_settings_clear(link);
|
||||
link_dns_settings_clear(link);
|
||||
|
@ -666,7 +666,7 @@ Manager* manager_free(Manager *m) {
|
||||
if (!m)
|
||||
return NULL;
|
||||
|
||||
sysctl_remove_monitor(m);
|
||||
manager_remove_sysctl_monitor(m);
|
||||
|
||||
free(m->state_file);
|
||||
|
||||
@ -742,7 +742,7 @@ int manager_start(Manager *m) {
|
||||
|
||||
log_debug("Starting...");
|
||||
|
||||
(void) sysctl_add_monitor(m);
|
||||
(void) manager_install_sysctl_monitor(m);
|
||||
|
||||
/* Loading BPF programs requires CAP_SYS_ADMIN and CAP_BPF.
|
||||
* Drop the capabilities here, regardless if the load succeeds or not. */
|
||||
|
@ -47,7 +47,7 @@ static int sysctl_event_handler(void *ctx, void *data, size_t data_sz) {
|
||||
* so do it only in case of a fatal error like a version mismatch. */
|
||||
if (we->version != 1)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unexpected sysctl event, disabling sysctl monitoring: %d", we->version);
|
||||
"Unexpected sysctl event, disabling sysctl monitoring: %d", we->version);
|
||||
|
||||
if (we->errorcode != 0) {
|
||||
log_warning_errno(we->errorcode, "Sysctl monitor BPF returned error: %m");
|
||||
@ -56,7 +56,7 @@ static int sysctl_event_handler(void *ctx, void *data, size_t data_sz) {
|
||||
|
||||
path = path_join("/proc/sys", we->path);
|
||||
if (!path) {
|
||||
log_oom();
|
||||
log_oom_warning();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ static int on_ringbuf_io(sd_event_source *s, int fd, uint32_t revents, void *use
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sysctl_add_monitor(Manager *manager) {
|
||||
int manager_install_sysctl_monitor(Manager *manager) {
|
||||
_cleanup_(sysctl_monitor_bpf_freep) struct sysctl_monitor_bpf *obj = NULL;
|
||||
_cleanup_(bpf_link_freep) struct bpf_link *sysctl_link = NULL;
|
||||
_cleanup_(bpf_ring_buffer_freep) struct ring_buffer *sysctl_buffer = NULL;
|
||||
@ -102,10 +102,10 @@ int sysctl_add_monitor(Manager *manager) {
|
||||
assert(manager);
|
||||
|
||||
r = dlopen_bpf();
|
||||
if (r < 0) {
|
||||
log_info_errno(r, "sysctl monitor disabled, as BPF support is not available.");
|
||||
return 0;
|
||||
}
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
return log_debug_errno(r, "sysctl monitor disabled, as BPF support is not available.");
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to load libbpf, not installing sysctl monitor: %m");
|
||||
|
||||
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup);
|
||||
if (r < 0)
|
||||
@ -113,13 +113,12 @@ int sysctl_add_monitor(Manager *manager) {
|
||||
|
||||
root_cgroup_fd = cg_path_open(SYSTEMD_CGROUP_CONTROLLER, "/");
|
||||
if (root_cgroup_fd < 0)
|
||||
return log_warning_errno(root_cgroup_fd, "Failed to open cgroup, ignoring: %m.");
|
||||
return log_warning_errno(root_cgroup_fd, "Failed to open cgroup, ignoring: %m");
|
||||
|
||||
obj = sysctl_monitor_bpf__open_and_load();
|
||||
if (!obj) {
|
||||
log_info_errno(errno, "Unable to load sysctl monitor BPF program, ignoring: %m.");
|
||||
return 0;
|
||||
}
|
||||
if (!obj)
|
||||
return log_full_errno(errno == EINVAL ? LOG_DEBUG : LOG_INFO, errno,
|
||||
"Unable to load sysctl monitor BPF program, ignoring: %m");
|
||||
|
||||
cgroup_fd = cg_path_open(SYSTEMD_CGROUP_CONTROLLER, cgroup);
|
||||
if (cgroup_fd < 0)
|
||||
@ -130,10 +129,8 @@ int sysctl_add_monitor(Manager *manager) {
|
||||
|
||||
sysctl_link = sym_bpf_program__attach_cgroup(obj->progs.sysctl_monitor, root_cgroup_fd);
|
||||
r = bpf_get_error_translated(sysctl_link);
|
||||
if (r < 0) {
|
||||
log_info_errno(r, "Unable to attach sysctl monitor BPF program to cgroup, ignoring: %m.");
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Unable to attach sysctl monitor BPF program to cgroup, ignoring: %m");
|
||||
|
||||
fd = sym_bpf_map__fd(obj->maps.written_sysctls);
|
||||
if (fd < 0)
|
||||
@ -160,7 +157,7 @@ int sysctl_add_monitor(Manager *manager) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sysctl_remove_monitor(Manager *manager) {
|
||||
void manager_remove_sysctl_monitor(Manager *manager) {
|
||||
assert(manager);
|
||||
|
||||
manager->sysctl_event_source = sd_event_source_disable_unref(manager->sysctl_event_source);
|
||||
@ -171,7 +168,7 @@ void sysctl_remove_monitor(Manager *manager) {
|
||||
manager->sysctl_shadow = hashmap_free(manager->sysctl_shadow);
|
||||
}
|
||||
|
||||
int sysctl_clear_link_shadows(Link *link) {
|
||||
int link_clear_sysctl_shadows(Link *link) {
|
||||
_cleanup_free_ char *ipv4 = NULL, *ipv6 = NULL;
|
||||
char *key = NULL, *value = NULL;
|
||||
|
||||
@ -188,7 +185,7 @@ int sysctl_clear_link_shadows(Link *link) {
|
||||
|
||||
HASHMAP_FOREACH_KEY(value, key, link->manager->sysctl_shadow)
|
||||
if (path_startswith(key, ipv4) || path_startswith(key, ipv6)) {
|
||||
assert_se(hashmap_remove_value(link->manager->sysctl_shadow, key, value) == value);
|
||||
assert_se(hashmap_remove_value(link->manager->sysctl_shadow, key, value));
|
||||
free(key);
|
||||
free(value);
|
||||
}
|
||||
|
@ -28,13 +28,13 @@ typedef enum IPReversePathFilter {
|
||||
} IPReversePathFilter;
|
||||
|
||||
#if HAVE_VMLINUX_H
|
||||
int sysctl_add_monitor(Manager *manager);
|
||||
void sysctl_remove_monitor(Manager *manager);
|
||||
int sysctl_clear_link_shadows(Link *link);
|
||||
int manager_install_sysctl_monitor(Manager *manager);
|
||||
void manager_remove_sysctl_monitor(Manager *manager);
|
||||
int link_clear_sysctl_shadows(Link *link);
|
||||
#else
|
||||
static inline int sysctl_add_monitor(Manager *manager) { return 0; }
|
||||
static inline void sysctl_remove_monitor(Manager *manager) { }
|
||||
static inline int sysctl_clear_link_shadows(Link *link) { return 0; }
|
||||
static inline int manager_install_sysctl_monitor(Manager *manager) { return 0; }
|
||||
static inline void manager_remove_sysctl_monitor(Manager *manager) { }
|
||||
static inline int link_clear_sysctl_shadows(Link *link) { return 0; }
|
||||
#endif
|
||||
|
||||
void manager_set_sysctl(Manager *manager);
|
||||
|
@ -475,7 +475,8 @@ int mount_sysfs(const char *dest, MountSettingsMask mount_settings) {
|
||||
if (!full)
|
||||
return log_oom();
|
||||
|
||||
(void) mkdir(full, 0755);
|
||||
if (mkdir(full, 0755) < 0 && errno != EEXIST)
|
||||
return log_error_errno(errno, "Failed to create directory '%s': %m", full);
|
||||
|
||||
if (FLAGS_SET(mount_settings, MOUNT_APPLY_APIVFS_RO))
|
||||
extra_flags |= MS_RDONLY;
|
||||
@ -1405,9 +1406,11 @@ done:
|
||||
#define NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS "/run/host/proc"
|
||||
#define NSPAWN_PRIVATE_FULLY_VISIBLE_SYSFS "/run/host/sys"
|
||||
|
||||
int pin_fully_visible_fs(void) {
|
||||
int pin_fully_visible_api_fs(void) {
|
||||
int r;
|
||||
|
||||
log_debug("Pinning fully visible API FS");
|
||||
|
||||
(void) mkdir_p(NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS, 0755);
|
||||
(void) mkdir_p(NSPAWN_PRIVATE_FULLY_VISIBLE_SYSFS, 0755);
|
||||
|
||||
@ -1422,7 +1425,7 @@ int pin_fully_visible_fs(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_wipe_fully_visible_fs(void) {
|
||||
static int do_wipe_fully_visible_api_fs(void) {
|
||||
if (umount2(NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS, MNT_DETACH) < 0)
|
||||
return log_error_errno(errno, "Failed to unmount temporary proc: %m");
|
||||
|
||||
@ -1438,10 +1441,12 @@ static int do_wipe_fully_visible_fs(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wipe_fully_visible_fs(int mntns_fd) {
|
||||
int wipe_fully_visible_api_fs(int mntns_fd) {
|
||||
_cleanup_close_ int orig_mntns_fd = -EBADF;
|
||||
int r, rr;
|
||||
|
||||
log_debug("Wiping fully visible API FS");
|
||||
|
||||
r = namespace_open(0,
|
||||
/* ret_pidns_fd = */ NULL,
|
||||
&orig_mntns_fd,
|
||||
@ -1459,7 +1464,7 @@ int wipe_fully_visible_fs(int mntns_fd) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enter mount namespace: %m");
|
||||
|
||||
rr = do_wipe_fully_visible_fs();
|
||||
rr = do_wipe_fully_visible_api_fs();
|
||||
|
||||
r = namespace_enter(/* pidns_fd = */ -EBADF,
|
||||
orig_mntns_fd,
|
||||
|
@ -73,5 +73,6 @@ int pivot_root_parse(char **pivot_root_new, char **pivot_root_old, const char *s
|
||||
int setup_pivot_root(const char *directory, const char *pivot_root_new, const char *pivot_root_old);
|
||||
|
||||
int tmpfs_patch_options(const char *options,uid_t uid_shift, const char *selinux_apifs_context, char **ret);
|
||||
int pin_fully_visible_fs(void);
|
||||
int wipe_fully_visible_fs(int mntns_fd);
|
||||
|
||||
int pin_fully_visible_api_fs(void);
|
||||
int wipe_fully_visible_api_fs(int mntns_fd);
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "copy.h"
|
||||
#include "cpu-set-util.h"
|
||||
#include "dev-setup.h"
|
||||
#include "devnum-util.h"
|
||||
#include "discover-image.h"
|
||||
#include "dissect-image.h"
|
||||
#include "env-util.h"
|
||||
@ -1250,7 +1251,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_uid_range = UINT32_C(0x10000);
|
||||
|
||||
} else if (streq(optarg, "identity")) {
|
||||
/* identity: User namespaces on, UID range is map the 0…0xFFFF range to
|
||||
/* identity: User namespaces on, UID range is map of the 0…0xFFFF range to
|
||||
* itself, i.e. we don't actually map anything, but do take benefit of
|
||||
* isolation of capability sets. */
|
||||
arg_userns_mode = USER_NAMESPACE_FIXED;
|
||||
@ -2323,7 +2324,7 @@ static int copy_devnode_one(const char *dest, const char *node, bool ignore_mkno
|
||||
return log_error_errno(r, "Failed to create '%s': %m", dn);
|
||||
|
||||
_cleanup_free_ char *sl = NULL;
|
||||
if (asprintf(&sl, "%s/%u:%u", dn, major(st.st_rdev), minor(st.st_rdev)) < 0)
|
||||
if (asprintf(&sl, "%s/" DEVNUM_FORMAT_STR, dn, DEVNUM_FORMAT_VAL(st.st_rdev)) < 0)
|
||||
return log_oom();
|
||||
|
||||
_cleanup_free_ char *prefixed = path_join(dest, sl);
|
||||
@ -2847,7 +2848,7 @@ static int reset_audit_loginuid(void) {
|
||||
if (!arg_privileged)
|
||||
return 0;
|
||||
|
||||
r = read_one_line_file("/proc/self/loginuid", &p);
|
||||
r = read_virtual_file("/proc/self/loginuid", SIZE_MAX, &p, /* ret_size= */ NULL);
|
||||
if (r == -ENOENT)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
@ -3166,7 +3167,8 @@ static int determine_names(void) {
|
||||
if (arg_machine) {
|
||||
_cleanup_(image_unrefp) Image *i = NULL;
|
||||
|
||||
r = image_find(IMAGE_MACHINE, arg_machine, NULL, &i);
|
||||
r = image_find(arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER,
|
||||
IMAGE_MACHINE, arg_machine, NULL, &i);
|
||||
if (r == -ENOENT)
|
||||
return log_error_errno(r, "No image for machine '%s'.", arg_machine);
|
||||
if (r < 0)
|
||||
@ -3254,6 +3256,7 @@ static int chase_and_update(char **p, unsigned flags) {
|
||||
}
|
||||
|
||||
static int determine_uid_shift(const char *directory) {
|
||||
assert(directory);
|
||||
|
||||
if (arg_userns_mode == USER_NAMESPACE_NO) {
|
||||
arg_uid_shift = 0;
|
||||
@ -3932,7 +3935,7 @@ static int outer_child(
|
||||
|
||||
_cleanup_(bind_user_context_freep) BindUserContext *bind_user_context = NULL;
|
||||
_cleanup_strv_free_ char **os_release_pairs = NULL;
|
||||
_cleanup_close_ int fd = -EBADF, mntns_fd = -EBADF;
|
||||
_cleanup_close_ int mntns_fd = -EBADF;
|
||||
bool idmap = false, enable_fuse;
|
||||
const char *p;
|
||||
pid_t pid;
|
||||
@ -4325,6 +4328,7 @@ static int outer_child(
|
||||
* visible. Hence there we do it the other way round: we first allocate a new set of namespaces
|
||||
* (and fork for it) for which we then mount sysfs/procfs, and only then switch root. */
|
||||
|
||||
_cleanup_close_ int notify_fd = -EBADF;
|
||||
if (arg_privileged) {
|
||||
/* Mark everything as shared so our mounts get propagated down. This is required to make new
|
||||
* bind mounts available in systemd services inside the container that create a new mount
|
||||
@ -4355,16 +4359,16 @@ static int outer_child(
|
||||
* Note, the inner child wouldn't be able to unmount the instances on its own since
|
||||
* it doesn't own the originating mount namespace. IOW, the outer child needs to do
|
||||
* this. */
|
||||
r = pin_fully_visible_fs();
|
||||
r = pin_fully_visible_api_fs();
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
fd = setup_notify_child(NULL);
|
||||
notify_fd = setup_notify_child(NULL);
|
||||
} else
|
||||
fd = setup_notify_child(directory);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
notify_fd = setup_notify_child(directory);
|
||||
if (notify_fd < 0)
|
||||
return notify_fd;
|
||||
|
||||
pid = raw_clone(SIGCHLD|CLONE_NEWNS|
|
||||
arg_clone_ns_flags |
|
||||
@ -4429,7 +4433,7 @@ static int outer_child(
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Short write while sending machine ID.");
|
||||
|
||||
l = send_one_fd(fd_outer_socket, fd, 0);
|
||||
l = send_one_fd(fd_outer_socket, notify_fd, 0);
|
||||
if (l < 0)
|
||||
return log_error_errno(l, "Failed to send notify fd: %m");
|
||||
|
||||
@ -5623,7 +5627,7 @@ static int run_container(
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Child died too early.");
|
||||
|
||||
if (arg_userns_mode != USER_NAMESPACE_NO) {
|
||||
r = wipe_fully_visible_fs(mntns_fd);
|
||||
r = wipe_fully_visible_api_fs(mntns_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
mntns_fd = safe_close(mntns_fd);
|
||||
@ -5740,16 +5744,6 @@ static int run_container(
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to run event loop: %m");
|
||||
|
||||
if (forward) {
|
||||
char last_char = 0;
|
||||
|
||||
(void) pty_forward_get_last_char(forward, &last_char);
|
||||
forward = pty_forward_free(forward);
|
||||
|
||||
if (!arg_quiet && last_char != '\n')
|
||||
putc('\n', stdout);
|
||||
}
|
||||
|
||||
/* Kill if it is not dead yet anyway */
|
||||
if (!arg_register && !arg_keep_unit && bus)
|
||||
terminate_scope(bus, arg_machine);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "main-func.h"
|
||||
#include "pager.h"
|
||||
#include "pretty-print.h"
|
||||
#include "sort-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
static const char *arg_suffix = NULL;
|
||||
@ -101,27 +102,50 @@ static const char* const path_table[_SD_PATH_MAX] = {
|
||||
[SD_PATH_SYSTEMD_USER_ENVIRONMENT_GENERATOR] = "systemd-user-environment-generator",
|
||||
[SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR] = "systemd-search-system-environment-generator",
|
||||
[SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR] = "systemd-search-user-environment-generator",
|
||||
|
||||
[SD_PATH_SYSTEM_CREDENTIAL_STORE] = "system-credential-store",
|
||||
[SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE] = "system-search-credential-store",
|
||||
[SD_PATH_SYSTEM_CREDENTIAL_STORE_ENCRYPTED] = "system-credential-store-encrypted",
|
||||
[SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE_ENCRYPTED] = "system-search-credential-store-encrypted",
|
||||
[SD_PATH_USER_CREDENTIAL_STORE] = "user-credential-store",
|
||||
[SD_PATH_USER_SEARCH_CREDENTIAL_STORE] = "user-search-credential-store",
|
||||
[SD_PATH_USER_CREDENTIAL_STORE_ENCRYPTED] = "user-credential-store-encrypted",
|
||||
[SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED] = "user-search-credential-store-encrypted",
|
||||
|
||||
};
|
||||
|
||||
static int order_cmp(const size_t *a, const size_t *b) {
|
||||
assert(*a < ELEMENTSOF(path_table));
|
||||
assert(*b < ELEMENTSOF(path_table));
|
||||
return strcmp(path_table[*a], path_table[*b]);
|
||||
}
|
||||
|
||||
static int list_paths(void) {
|
||||
int r = 0;
|
||||
int ret = 0, r;
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(path_table); i++) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int q;
|
||||
size_t order[ELEMENTSOF(path_table)];
|
||||
|
||||
q = sd_path_lookup(i, arg_suffix, &p);
|
||||
if (q < 0) {
|
||||
log_full_errno(q == -ENXIO ? LOG_DEBUG : LOG_ERR,
|
||||
q, "Failed to query %s: %m", path_table[i]);
|
||||
if (q != -ENXIO)
|
||||
RET_GATHER(r, q);
|
||||
for (size_t i = 0; i < ELEMENTSOF(order); i++)
|
||||
order[i] = i;
|
||||
|
||||
typesafe_qsort(order, ELEMENTSOF(order), order_cmp);
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(order); i++) {
|
||||
size_t j = order[i];
|
||||
const char *t = ASSERT_PTR(path_table[j]);
|
||||
|
||||
_cleanup_free_ char *p = NULL;
|
||||
r = sd_path_lookup(j, arg_suffix, &p);
|
||||
if (r < 0) {
|
||||
log_full_errno(r == -ENXIO ? LOG_DEBUG : LOG_ERR, r, "Failed to query %s, proceeding: %m", t);
|
||||
if (r != -ENXIO)
|
||||
RET_GATHER(ret, r);
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("%s%s:%s %s\n", ansi_highlight(), path_table[i], ansi_normal(), p);
|
||||
printf("%s%s:%s %s\n", ansi_highlight(), t, ansi_normal(), p);
|
||||
}
|
||||
|
||||
return r;
|
||||
@ -154,14 +178,16 @@ static int help(void) {
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
printf("%s [OPTIONS...] [NAME...]\n\n"
|
||||
"Show system and user paths.\n\n"
|
||||
printf("%s [OPTIONS...] [NAME...]\n"
|
||||
"\n%sShow system and user paths.%s\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" --suffix=SUFFIX Suffix to append to paths\n"
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
ansi_normal(),
|
||||
link);
|
||||
|
||||
return 0;
|
||||
@ -224,10 +250,11 @@ static int run(int argc, char* argv[]) {
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (argc > optind)
|
||||
if (argc > optind) {
|
||||
r = 0;
|
||||
for (int i = optind; i < argc; i++)
|
||||
RET_GATHER(r, print_path(argv[i]));
|
||||
else
|
||||
} else
|
||||
r = list_paths();
|
||||
|
||||
return r;
|
||||
|
@ -173,6 +173,7 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(portable_metadata_hash_ops, char,
|
||||
PortableMetadata, portable_metadata_unref);
|
||||
|
||||
static int extract_now(
|
||||
RuntimeScope scope,
|
||||
const char *where,
|
||||
char **matches,
|
||||
const char *image_name,
|
||||
@ -199,6 +200,7 @@ static int extract_now(
|
||||
* parent. To handle both cases in one call this function also gets a 'socket_fd' parameter, which when >= 0 is
|
||||
* used to send the data to the parent. */
|
||||
|
||||
assert(scope < _RUNTIME_SCOPE_MAX);
|
||||
assert(where);
|
||||
|
||||
/* First, find os-release/extension-release and send it upstream (or just save it). */
|
||||
@ -248,7 +250,7 @@ static int extract_now(
|
||||
/* Then, send unit file data to the parent (or/and add it to the hashmap). For that we use our usual unit
|
||||
* discovery logic. Note that we force looking inside of /lib/systemd/system/ for units too, as the
|
||||
* image might have a legacy split-usr layout. */
|
||||
r = lookup_paths_init(&paths, RUNTIME_SCOPE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, where);
|
||||
r = lookup_paths_init(&paths, scope, LOOKUP_PATHS_SPLIT_USR, where);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to acquire lookup paths: %m");
|
||||
|
||||
@ -348,6 +350,7 @@ static int extract_now(
|
||||
}
|
||||
|
||||
static int portable_extract_by_path(
|
||||
RuntimeScope scope,
|
||||
const char *path,
|
||||
bool path_is_extension,
|
||||
bool relax_extension_release_check,
|
||||
@ -381,7 +384,7 @@ static int portable_extract_by_path(
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract image name from path '%s': %m", path);
|
||||
|
||||
r = extract_now(path, matches, image_name, path_is_extension, /* relax_extension_release_check= */ false, -1, &os_release, &unit_files);
|
||||
r = extract_now(scope, path, matches, image_name, path_is_extension, /* relax_extension_release_check= */ false, -1, &os_release, &unit_files);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -458,7 +461,7 @@ static int portable_extract_by_path(
|
||||
goto child_finish;
|
||||
}
|
||||
|
||||
r = extract_now(tmpdir, matches, m->image_name, path_is_extension, relax_extension_release_check, seq[1], NULL, NULL);
|
||||
r = extract_now(scope, tmpdir, matches, m->image_name, path_is_extension, relax_extension_release_check, seq[1], NULL, NULL);
|
||||
|
||||
child_finish:
|
||||
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
@ -549,6 +552,7 @@ static int portable_extract_by_path(
|
||||
}
|
||||
|
||||
static int extract_image_and_extensions(
|
||||
RuntimeScope scope,
|
||||
const char *name_or_path,
|
||||
char **matches,
|
||||
char **extension_image_paths,
|
||||
@ -595,7 +599,7 @@ static int extract_image_and_extensions(
|
||||
name_or_path = result.path;
|
||||
}
|
||||
|
||||
r = image_find_harder(IMAGE_PORTABLE, name_or_path, /* root= */ NULL, &image);
|
||||
r = image_find_harder(scope, IMAGE_PORTABLE, name_or_path, /* root= */ NULL, &image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -633,7 +637,7 @@ static int extract_image_and_extensions(
|
||||
path = ext_result.path;
|
||||
}
|
||||
|
||||
r = image_find_harder(IMAGE_PORTABLE, path, NULL, &new);
|
||||
r = image_find_harder(scope, IMAGE_PORTABLE, path, NULL, &new);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -645,6 +649,7 @@ static int extract_image_and_extensions(
|
||||
}
|
||||
|
||||
r = portable_extract_by_path(
|
||||
scope,
|
||||
image->path,
|
||||
/* path_is_extension= */ false,
|
||||
/* relax_extension_release_check= */ false,
|
||||
@ -687,6 +692,7 @@ static int extract_image_and_extensions(
|
||||
const char *e;
|
||||
|
||||
r = portable_extract_by_path(
|
||||
scope,
|
||||
ext->path,
|
||||
/* path_is_extension= */ true,
|
||||
relax_extension_release_check,
|
||||
@ -754,6 +760,7 @@ static int extract_image_and_extensions(
|
||||
}
|
||||
|
||||
int portable_extract(
|
||||
RuntimeScope scope,
|
||||
const char *name_or_path,
|
||||
char **matches,
|
||||
char **extension_image_paths,
|
||||
@ -775,6 +782,7 @@ int portable_extract(
|
||||
assert(name_or_path);
|
||||
|
||||
r = extract_image_and_extensions(
|
||||
scope,
|
||||
name_or_path,
|
||||
matches,
|
||||
extension_image_paths,
|
||||
@ -1426,6 +1434,7 @@ static int image_target_path(
|
||||
}
|
||||
|
||||
static int install_image(
|
||||
RuntimeScope scope,
|
||||
const char *image_path,
|
||||
PortableFlags flags,
|
||||
PortableChange **changes,
|
||||
@ -1434,13 +1443,14 @@ static int install_image(
|
||||
_cleanup_free_ char *target = NULL;
|
||||
int r;
|
||||
|
||||
assert(scope < _RUNTIME_SCOPE_MAX);
|
||||
assert(image_path);
|
||||
|
||||
/* If the image is outside of the image search also link it into it, so that it can be found with
|
||||
* short image names and is listed among the images. If we are operating in mixed mode, the image is
|
||||
* copied instead. */
|
||||
|
||||
if (image_in_search_path(IMAGE_PORTABLE, NULL, image_path))
|
||||
if (image_in_search_path(scope, IMAGE_PORTABLE, NULL, image_path))
|
||||
return 0;
|
||||
|
||||
r = image_target_path(image_path, flags, &target);
|
||||
@ -1485,6 +1495,7 @@ static int install_image(
|
||||
}
|
||||
|
||||
static int install_image_and_extensions(
|
||||
RuntimeScope scope,
|
||||
const Image *image,
|
||||
OrderedHashmap *extension_images,
|
||||
PortableFlags flags,
|
||||
@ -1497,12 +1508,12 @@ static int install_image_and_extensions(
|
||||
assert(image);
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(ext, extension_images) {
|
||||
r = install_image(ext->path, flags, changes, n_changes);
|
||||
r = install_image(scope, ext->path, flags, changes, n_changes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = install_image(image->path, flags, changes, n_changes);
|
||||
r = install_image(scope, image->path, flags, changes, n_changes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1595,6 +1606,7 @@ static void log_portable_verb(
|
||||
}
|
||||
|
||||
int portable_attach(
|
||||
RuntimeScope scope,
|
||||
sd_bus *bus,
|
||||
const char *name_or_path,
|
||||
char **matches,
|
||||
@ -1615,7 +1627,10 @@ int portable_attach(
|
||||
PortableMetadata *item;
|
||||
int r;
|
||||
|
||||
assert(scope < _RUNTIME_SCOPE_MAX);
|
||||
|
||||
r = extract_image_and_extensions(
|
||||
scope,
|
||||
name_or_path,
|
||||
matches,
|
||||
extension_image_paths,
|
||||
@ -1672,13 +1687,13 @@ int portable_attach(
|
||||
strempty(extensions_joined));
|
||||
}
|
||||
|
||||
r = lookup_paths_init(&paths, RUNTIME_SCOPE_SYSTEM, /* flags= */ 0, NULL);
|
||||
r = lookup_paths_init(&paths, scope, /* flags= */ 0, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!FLAGS_SET(flags, PORTABLE_REATTACH) && !FLAGS_SET(flags, PORTABLE_FORCE_ATTACH))
|
||||
HASHMAP_FOREACH(item, unit_files) {
|
||||
r = unit_file_exists(RUNTIME_SCOPE_SYSTEM, &paths, item->name);
|
||||
r = unit_file_exists(scope, &paths, item->name);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to determine whether unit '%s' exists on the host: %m", item->name);
|
||||
if (r > 0)
|
||||
@ -1700,7 +1715,7 @@ int portable_attach(
|
||||
|
||||
/* We don't care too much for the image symlink/copy, it's just a convenience thing, it's not necessary for
|
||||
* proper operation otherwise. */
|
||||
(void) install_image_and_extensions(image, extension_images, flags, changes, n_changes);
|
||||
(void) install_image_and_extensions(scope, image, extension_images, flags, changes, n_changes);
|
||||
|
||||
log_portable_verb(
|
||||
"attached",
|
||||
@ -1844,6 +1859,7 @@ static int test_chroot_dropin(
|
||||
}
|
||||
|
||||
int portable_detach(
|
||||
RuntimeScope scope,
|
||||
sd_bus *bus,
|
||||
const char *name_or_path,
|
||||
char **extension_image_paths,
|
||||
@ -1857,12 +1873,12 @@ int portable_detach(
|
||||
_cleanup_free_ char *extensions = NULL;
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
const char *where, *item;
|
||||
int ret = 0;
|
||||
int r;
|
||||
int r, ret = 0;
|
||||
|
||||
assert(scope < _RUNTIME_SCOPE_MAX);
|
||||
assert(name_or_path);
|
||||
|
||||
r = lookup_paths_init(&paths, RUNTIME_SCOPE_SYSTEM, /* flags= */ 0, NULL);
|
||||
r = lookup_paths_init(&paths, scope, /* flags= */ 0, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1930,7 +1946,7 @@ int portable_detach(
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
if (path_is_absolute(image) && !image_in_search_path(IMAGE_PORTABLE, NULL, image)) {
|
||||
if (path_is_absolute(image) && !image_in_search_path(scope, IMAGE_PORTABLE, NULL, image)) {
|
||||
r = set_ensure_consume(&markers, &path_hash_ops_free, TAKE_PTR(image));
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -2031,6 +2047,7 @@ not_found:
|
||||
}
|
||||
|
||||
static int portable_get_state_internal(
|
||||
RuntimeScope scope,
|
||||
sd_bus *bus,
|
||||
const char *name_or_path,
|
||||
char **extension_image_paths,
|
||||
@ -2045,10 +2062,11 @@ static int portable_get_state_internal(
|
||||
const char *where;
|
||||
int r;
|
||||
|
||||
assert(scope < _RUNTIME_SCOPE_MAX);
|
||||
assert(name_or_path);
|
||||
assert(ret);
|
||||
|
||||
r = lookup_paths_init(&paths, RUNTIME_SCOPE_SYSTEM, /* flags= */ 0, NULL);
|
||||
r = lookup_paths_init(&paths, scope, /* flags= */ 0, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -2084,7 +2102,7 @@ static int portable_get_state_internal(
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
r = unit_file_lookup_state(RUNTIME_SCOPE_SYSTEM, &paths, de->d_name, &state);
|
||||
r = unit_file_lookup_state(scope, &paths, de->d_name, &state);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to determine unit file state of '%s': %m", de->d_name);
|
||||
if (!IN_SET(state, UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_LINKED, UNIT_FILE_LINKED_RUNTIME))
|
||||
@ -2109,6 +2127,7 @@ static int portable_get_state_internal(
|
||||
}
|
||||
|
||||
int portable_get_state(
|
||||
RuntimeScope scope,
|
||||
sd_bus *bus,
|
||||
const char *name_or_path,
|
||||
char **extension_image_paths,
|
||||
@ -2125,12 +2144,19 @@ int portable_get_state(
|
||||
/* We look for matching units twice: once in the regular directories, and once in the runtime directories — but
|
||||
* the latter only if we didn't find anything in the former. */
|
||||
|
||||
r = portable_get_state_internal(bus, name_or_path, extension_image_paths, flags & ~PORTABLE_RUNTIME, &state, error);
|
||||
r = portable_get_state_internal(
|
||||
scope,
|
||||
bus,
|
||||
name_or_path,
|
||||
extension_image_paths,
|
||||
flags & ~PORTABLE_RUNTIME,
|
||||
&state,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (state == PORTABLE_DETACHED) {
|
||||
r = portable_get_state_internal(bus, name_or_path, extension_image_paths, flags | PORTABLE_RUNTIME, &state, error);
|
||||
r = portable_get_state_internal(scope, bus, name_or_path, extension_image_paths, flags | PORTABLE_RUNTIME, &state, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "dissect-image.h"
|
||||
#include "hashmap.h"
|
||||
#include "macro.h"
|
||||
#include "runtime-scope.h"
|
||||
#include "set.h"
|
||||
#include "string-util.h"
|
||||
|
||||
@ -69,12 +70,12 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(PortableMetadata*, portable_metadata_unref);
|
||||
|
||||
int portable_metadata_hashmap_to_sorted_array(Hashmap *unit_files, PortableMetadata ***ret);
|
||||
|
||||
int portable_extract(const char *image, char **matches, char **extension_image_paths, const ImagePolicy *image_policy, PortableFlags flags, PortableMetadata **ret_os_release, OrderedHashmap **ret_extension_releases, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error);
|
||||
int portable_extract(RuntimeScope scope, const char *image, char **matches, char **extension_image_paths, const ImagePolicy *image_policy, PortableFlags flags, PortableMetadata **ret_os_release, OrderedHashmap **ret_extension_releases, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error);
|
||||
|
||||
int portable_attach(sd_bus *bus, const char *name_or_path, char **matches, const char *profile, char **extension_images, const ImagePolicy* image_policy, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
|
||||
int portable_detach(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
|
||||
int portable_attach(RuntimeScope scope, sd_bus *bus, const char *name_or_path, char **matches, const char *profile, char **extension_images, const ImagePolicy* image_policy, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
|
||||
int portable_detach(RuntimeScope scope, sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
|
||||
|
||||
int portable_get_state(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableState *ret, sd_bus_error *error);
|
||||
int portable_get_state(RuntimeScope scope, sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableState *ret, sd_bus_error *error);
|
||||
|
||||
int portable_get_profiles(char ***ret);
|
||||
|
||||
|
@ -165,6 +165,7 @@ static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_er
|
||||
return r;
|
||||
|
||||
r = portable_get_state(
|
||||
m->runtime_scope,
|
||||
sd_bus_message_get_bus(message),
|
||||
image->path,
|
||||
NULL,
|
||||
@ -225,6 +226,7 @@ static int method_get_image_metadata(sd_bus_message *message, void *userdata, sd
|
||||
|
||||
static int method_get_image_state(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
_cleanup_strv_free_ char **extension_images = NULL;
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
const char *name_or_path;
|
||||
PortableState state;
|
||||
int r;
|
||||
@ -254,6 +256,7 @@ static int method_get_image_state(sd_bus_message *message, void *userdata, sd_bu
|
||||
}
|
||||
|
||||
r = portable_get_state(
|
||||
m->runtime_scope,
|
||||
sd_bus_message_get_bus(message),
|
||||
name_or_path,
|
||||
extension_images,
|
||||
@ -330,6 +333,7 @@ static int method_detach_image(sd_bus_message *message, void *userdata, sd_bus_e
|
||||
return 1; /* Will call us back */
|
||||
|
||||
r = portable_detach(
|
||||
m->runtime_scope,
|
||||
sd_bus_message_get_bus(message),
|
||||
name_or_path,
|
||||
extension_images,
|
||||
|
@ -114,10 +114,8 @@ int bus_image_common_get_metadata(
|
||||
assert(name_or_path || image);
|
||||
assert(message);
|
||||
|
||||
if (!m) {
|
||||
assert(image);
|
||||
m = image->userdata;
|
||||
}
|
||||
if (!m)
|
||||
m = ASSERT_PTR(ASSERT_PTR(image)->userdata);
|
||||
|
||||
bool have_exti = sd_bus_message_is_method_call(message, NULL, "GetImageMetadataWithExtensions") ||
|
||||
sd_bus_message_is_method_call(message, NULL, "GetMetadataWithExtensions");
|
||||
@ -160,6 +158,7 @@ int bus_image_common_get_metadata(
|
||||
return 1;
|
||||
|
||||
r = portable_extract(
|
||||
m->runtime_scope,
|
||||
image->path,
|
||||
matches,
|
||||
extension_images,
|
||||
@ -264,6 +263,7 @@ static int bus_image_method_get_state(
|
||||
|
||||
_cleanup_strv_free_ char **extension_images = NULL;
|
||||
Image *image = ASSERT_PTR(userdata);
|
||||
Manager *m = ASSERT_PTR(image->userdata);
|
||||
PortableState state;
|
||||
int r;
|
||||
|
||||
@ -288,6 +288,7 @@ static int bus_image_method_get_state(
|
||||
}
|
||||
|
||||
r = portable_get_state(
|
||||
m->runtime_scope,
|
||||
sd_bus_message_get_bus(message),
|
||||
image->path,
|
||||
extension_images,
|
||||
@ -385,6 +386,7 @@ int bus_image_common_attach(
|
||||
return 1;
|
||||
|
||||
r = portable_attach(
|
||||
m->runtime_scope,
|
||||
sd_bus_message_get_bus(message),
|
||||
image->path,
|
||||
matches,
|
||||
@ -463,6 +465,7 @@ static int bus_image_method_detach(
|
||||
return 1; /* Will call us back */
|
||||
|
||||
r = portable_detach(
|
||||
m->runtime_scope,
|
||||
sd_bus_message_get_bus(message),
|
||||
image->path,
|
||||
extension_images,
|
||||
@ -513,6 +516,7 @@ int bus_image_common_remove(
|
||||
return 1; /* Will call us back */
|
||||
|
||||
r = portable_get_state(
|
||||
m->runtime_scope,
|
||||
sd_bus_message_get_bus(message),
|
||||
image->path,
|
||||
NULL,
|
||||
@ -716,6 +720,7 @@ int bus_image_common_reattach(
|
||||
return 1;
|
||||
|
||||
r = portable_detach(
|
||||
m->runtime_scope,
|
||||
sd_bus_message_get_bus(message),
|
||||
image->path,
|
||||
extension_images,
|
||||
@ -727,6 +732,7 @@ int bus_image_common_reattach(
|
||||
return r;
|
||||
|
||||
r = portable_attach(
|
||||
m->runtime_scope,
|
||||
sd_bus_message_get_bus(message),
|
||||
image->path,
|
||||
matches,
|
||||
@ -1039,7 +1045,7 @@ int bus_image_acquire(
|
||||
if (image_name_is_valid(name_or_path)) {
|
||||
|
||||
/* If it's a short name, let's search for it */
|
||||
r = image_find(IMAGE_PORTABLE, name_or_path, NULL, &loaded);
|
||||
r = image_find(m->runtime_scope, IMAGE_PORTABLE, name_or_path, NULL, &loaded);
|
||||
if (r == -ENOENT)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PORTABLE_IMAGE,
|
||||
"No image '%s' found.", name_or_path);
|
||||
|
@ -91,7 +91,7 @@ int manager_image_cache_discover(Manager *m, Hashmap *images, sd_bus_error *erro
|
||||
/* A wrapper around image_discover() (for finding images in search path) and portable_discover_attached() (for
|
||||
* finding attached images). */
|
||||
|
||||
r = image_discover(IMAGE_PORTABLE, NULL, images);
|
||||
r = image_discover(m->runtime_scope, IMAGE_PORTABLE, NULL, images);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -28,10 +28,14 @@ static int manager_new(Manager **ret) {
|
||||
|
||||
assert(ret);
|
||||
|
||||
m = new0(Manager, 1);
|
||||
m = new(Manager, 1);
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
|
||||
*m = (Manager) {
|
||||
.runtime_scope = RUNTIME_SCOPE_SYSTEM,
|
||||
};
|
||||
|
||||
r = sd_event_default(&m->event);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "bus-object.h"
|
||||
#include "hashmap.h"
|
||||
#include "list.h"
|
||||
#include "runtime-scope.h"
|
||||
|
||||
typedef struct Manager Manager;
|
||||
|
||||
@ -23,6 +24,8 @@ struct Manager {
|
||||
|
||||
LIST_HEAD(Operation, operations);
|
||||
unsigned n_operations;
|
||||
|
||||
RuntimeScope runtime_scope; /* for now always RUNTIME_SCOPE_SYSTEM */
|
||||
};
|
||||
|
||||
extern const BusObjectImplementation manager_object;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "resolved-dns-query.h"
|
||||
#include "resolved-dns-synthesize.h"
|
||||
#include "resolved-etc-hosts.h"
|
||||
#include "resolved-timeouts.h"
|
||||
#include "string-util.h"
|
||||
|
||||
#define QUERIES_MAX 2048
|
||||
@ -48,6 +49,8 @@ static void dns_query_candidate_stop(DnsQueryCandidate *c) {
|
||||
|
||||
assert(c);
|
||||
|
||||
(void) event_source_disable(c->timeout_event_source);
|
||||
|
||||
/* Detach all the DnsTransactions attached to this query */
|
||||
|
||||
while ((t = set_steal_first(c->transactions))) {
|
||||
@ -62,6 +65,8 @@ static void dns_query_candidate_abandon(DnsQueryCandidate *c) {
|
||||
|
||||
assert(c);
|
||||
|
||||
(void) event_source_disable(c->timeout_event_source);
|
||||
|
||||
/* Abandon all the DnsTransactions attached to this query */
|
||||
|
||||
while ((t = set_steal_first(c->transactions))) {
|
||||
@ -94,6 +99,8 @@ static DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c) {
|
||||
if (!c)
|
||||
return NULL;
|
||||
|
||||
c->timeout_event_source = sd_event_source_disable_unref(c->timeout_event_source);
|
||||
|
||||
dns_query_candidate_stop(c);
|
||||
dns_query_candidate_unlink(c);
|
||||
|
||||
@ -312,6 +319,30 @@ fail:
|
||||
return r;
|
||||
}
|
||||
|
||||
static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c);
|
||||
|
||||
static int on_candidate_timeout(sd_event_source *s, usec_t usec, void *userdata) {
|
||||
DnsQueryCandidate *c = userdata;
|
||||
|
||||
assert(s);
|
||||
assert(c);
|
||||
|
||||
log_debug("Accepting incomplete query candidate after expedited timeout on partial success.");
|
||||
dns_query_accept(c->query, c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool dns_query_candidate_has_partially_succeeded(DnsQueryCandidate *c) {
|
||||
DnsTransaction *t;
|
||||
|
||||
SET_FOREACH(t, c->transactions)
|
||||
if (t->state == DNS_TRANSACTION_SUCCESS)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void dns_query_candidate_notify(DnsQueryCandidate *c) {
|
||||
DnsTransactionState state;
|
||||
int r;
|
||||
@ -323,11 +354,24 @@ void dns_query_candidate_notify(DnsQueryCandidate *c) {
|
||||
|
||||
state = dns_query_candidate_state(c);
|
||||
|
||||
if (DNS_TRANSACTION_IS_LIVE(state))
|
||||
if (DNS_TRANSACTION_IS_LIVE(state)) {
|
||||
if (dns_query_candidate_has_partially_succeeded(c))
|
||||
(void) event_reset_time_relative(
|
||||
c->query->manager->event,
|
||||
&c->timeout_event_source,
|
||||
CLOCK_BOOTTIME,
|
||||
CANDIDATE_EXPEDITED_TIMEOUT_USEC, /* accuracy_usec= */ 0,
|
||||
on_candidate_timeout, c,
|
||||
/* priority= */ 0, "candidate-timeout",
|
||||
/* force_reset= */ false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (state != DNS_TRANSACTION_SUCCESS && c->search_domain) {
|
||||
|
||||
(void) event_source_disable(c->timeout_event_source);
|
||||
|
||||
r = dns_query_candidate_next_search_domain(c);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
@ -25,6 +25,7 @@ struct DnsQueryCandidate {
|
||||
DnsSearchDomain *search_domain;
|
||||
|
||||
Set *transactions;
|
||||
sd_event_source *timeout_event_source;
|
||||
|
||||
LIST_FIELDS(DnsQueryCandidate, candidates_by_query);
|
||||
LIST_FIELDS(DnsQueryCandidate, candidates_by_scope);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "resolved-dns-zone.h"
|
||||
#include "resolved-llmnr.h"
|
||||
#include "resolved-mdns.h"
|
||||
#include "resolved-timeouts.h"
|
||||
#include "socket-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user