normalized unit file path, which is particularly useful for symlinked
unit files.
- The new %R specifier resolves to the pretty hostname
+ The new %q specifier resolves to the pretty hostname
(i.e. PRETTY_HOSTNAME= from /etc/machine-info).
The new %d specifier resolves to the credentials directory of a
≥ 4.10 for cgroup-bpf egress and ingress hooks
≥ 4.15 for cgroup-bpf device hook and cpu controller in cgroup v2
≥ 4.17 for cgroup-bpf socket address hooks
+ ≥ 4.20 for PSI (used by systemd-oomd)
≥ 5.3 for bounded loops in BPF program
≥ 5.4 for signed Verity images
≥ 5.7 for BPF links and the BPF LSM hook
python-jinja2
python-lxml (optional, required to build the indices)
python >= 3.5
- meson >= 0.53.2 (>= 0.54.0 is required to build with 'meson compile')
+ meson >= 0.53.2
ninja
gcc, awk, sed, grep, and similar tools
clang >= 10.0, llvm >= 10.0 (optional, required to build BPF programs
polkit (optional)
To build in directory build/:
- meson setup build/ && meson compile -C build/
+ meson setup build/ && ninja -C build/
Any configuration options can be specified as -Darg=value... arguments
to meson. After the build directory is initially configured, meson will
their current values.
Useful commands:
- meson compile -v -C build/ some/target
+ ninja -C build -v some/target
meson test -C build/
sudo meson install -C build/
DESTDIR=... meson install -C build/
A tarball can be created with:
- git archive --format=tar --prefix=systemd-222/ v222 | xz > systemd-222.tar.xz
+ v=250 && git archive --prefix=systemd-$v/ v$v | zstd >systemd-$v.tar.zstd
When systemd-hostnamed is used, it is strongly recommended to
install nss-myhostname to ensure that, in a world of
Features:
+* systemd-fstab-generator: support addition mount specifications via kernel
+ cmdline. Usecase: invoke a VM, and mount a host homedir into it via
+ virtio-fs.
+
+* for vendor-built signed initrds:
+ - make sysext run in the initrd
+ - sysext should pick up sysext images from /.extra/ in the initrd, and insist
+ on verification if in secureboot mode
+ - kernel-install should be able to install pre-built unified kernel images in
+ type #2 drop-in dir in the ESP.
+ - kernel-install should be able install encrypted creds automatically for
+ machine id, root pw, rootfs uuid, resume partition uuid, and place next to
+ EFI kernel, for sd-stub to pick them up. These creds should be locked to
+ the TPM, and bind to the right PCR the kernel is measured to.
+ - kernel-install should be able to pick up initrd sysexts automatically and
+ place them next to EFI kernel, for sd-stub to pick them up.
+ - systemd-fstab-generator should look for rootfs device to mount in creds
+ - pid 1 should look for machine ID in creds
+ - systemd-resume-generator should look for resume partition uuid in creds
+ - sd-stub: automatically pick up microcode from ESP (/loader/microcode/*)
+ and synthesize initrd from it, and measure it. Signing is not necessary, as
+ microcode does that on its own. Pass as first initrd to kernel.
+ - systemd-creds should have a fallback logic that uses neither TPM nor the
+ system key in /var for encryption and instead some fixed key. This should
+ be opt in (since it provides no security properties) but be used by
+ kernel-install when encrypting the creds it generates on systems that lack
+ a TPM, so that we can have very similar codepaths on TPM and TPM-less
+ systems. i.e. --with-key=tpm-graceful or so.
+
+* Add a new service type very similar to Type=notify, that goes one step
+ further and extends the protocol to cover reloads. Specifically, SIGHUP will
+ become the official way to reload, and daemon has to respond with sd_notify()
+ to report when it starts reloading, and when it is complete reloading. Care
+ must be taken to remove races from this model. I.e. PID 1 needs to take
+ CLOCK_MONOTONIC, then send SIGHUP, then wait for at least one RELOADING=1
+ message that comes with a newer timestamp, then wait for a READY=1 message.
+ while we are at it, also maybe extend the logic to require handling of some
+ specific SIGRT signal for setting debug log level, that carries the level via
+ the sigqueue() data parameter. With that we extended with minimal logic the
+ service runtime logic quite substantially.
+
* get_color_mode() should probably check the $COLORTERM environment variable
which most terminal environments appear to set.
* mirroring this: maybe support binding to AV_VSOCK in Type=notify services,
then passing $NOTIFY_SOCKET and $NOTIFY_GUESTCID with PID1's cid (typically
fixed to "2", i.e. the official host cid) and the expected guest cid, for the
- two sides of the channe. The latter env var could then be used in an
+ two sides of the channel. The latter env var could then be used in an
appropriate qemu cmdline. That way qemu payloads could talk sd_notify()
directly to host service manager.
sd_device object, so that data passed into sd_device_new_from_devnum() can
also be queried.
-* udevadm: a new "tree" verb that shows tree of devices as syspath hierarchy,
- along with their properties. uninitialized devices should be greyed out.
-
* bootctl: show whether UEFI audit mode is available
-* dissect: rework how we access partitions: instead of letting the kernel probe
- partition tables asynchronously, just pass the stuff we parsed in userspace
- to the kernel via BLKPG_ADD_PARTITION. Benefit: we don't have to wait for
- kernel/netlink/udev, but can run this synchronously without chance of losing
- events or similar.
-
* sd-event: optionally, if per-event source rate limit is hit, downgrade
priority, but leave enabled, and once ratelimit window is over, upgrade
priority again. That way we can combat event source starvation without
credential logic and drops them into /run where nss-systemd can pick them up,
similar to /run/host/userdb/. Usecase: drop a root user JSON record there,
and use it in the initrd to log in as root with locally selected password,
- for debugging purposes.
+ for debugging purposes. Other usecase: boot into qemu with regular user
+ mounted from host. maybe put this in systemd-user-sessions.service?
* drop dependency on libcap, replace by direct syscalls based on
CapabilityQuintet we already have. (This likely allows us drop drop libcap
to be taken that the resulting logic ends up in RAM, i.e. is copied out of
on-disk storage.
-* sd-stub: automatically pick up microcode from ESP and synthesize initrd from
- it, and measure it. Signing is not necessary, as microcode does that on its
- own. Pass as first initrd to kernel.
-
* userdbd: implement an additional varlink service socket that provides the
host user db in restricted form, then allow this to be bind mounted into
sandboxed environments that want the host database in minimal form. All
https://github.com/dvdhrm/docs/blob/master/drm-howto/modeset.c for an example
for doing that.
-* pass systemd-detect-virt result to generators as env var. Modifying behaviour
- based on whether we are virtualized or not is a pretty common thing, hence
- maybe just pass that info along for free in an env var. We cache the result
- anyway, so it's basically free.
-
* introduce /dev/disk/root/* symlinks that allow referencing partitions on the
disk the rootfs is on in a reasonably secure way. (or maybe: add
/dev/gpt-auto-{home,srv,boot,…} similar in style to /dev/gpt-auto-root as we
specified, synthesize a definition automatically if we can: enlarge last
partition on disk, but only if it is marked for growing and not read-only.
-* systemd-repart: read LUKS encryption key from $CREDENTIALS_PATH
+* systemd-repart: read LUKS encryption key from $CREDENTIALS_DIRECTORY
* systemd-repart: add a switch to factory reset the partition table without
immediately applying the new configuration again. i.e. --factory-reset=leave
`./tools/find-tabs.sh recpatch` to fix them. (Again, grain of salt, foreign
headers should usually be left unmodified.)
-6. Use `meson compile -C build check-api-docs` to compare the list of exported
- symbols of `libsystemd.so` and `libudev.so` with the list of man pages. Symbols
+6. Use `ninja -C build check-api-docs` to compare the list of exported symbols
+ of `libsystemd.so` and `libudev.so` with the list of man pages. Symbols
lacking documentation are highlighted.
-7. Use `meson compile -C build update-hwdb` to automatically download and import the
- PCI, USB and OUI databases into hwdb.
+7. Use `ninja -C build update-hwdb` and `ninja -C build update-hwdb-autosuspend`
+ to automatically download and import the PCI, USB, and OUI databases and the
+ autosuspend quirks into the hwdb.
-8. Use `meson compile -C build update-man-rules` to update the meson rules for
- building man pages automatically from the docbook XML files included in
- `man/`.
+8. Use `ninja -C build update-man-rules` to update the meson rules for building
+ man pages automatically from the docbook XML files included in `man/`.
-9. There are multiple CI systems in use that run on every github PR submission.
+9. There are multiple CI systems in use that run on every github pull request
+ submission or update.
10. [Coverity](https://scan.coverity.com/) is analyzing systemd `main` branch
in regular intervals. The reports are available
or whenever they change if it wants to integrate with `systemd-logind`'s
APIs.
-`systemd-udevd`:
+`systemd-udevd` and sd-device library:
* `$NET_NAMING_SCHEME=` — if set, takes a network naming scheme (i.e. one of
"v238", "v239", "v240"…, or the special value "latest") as parameter. If
prefixed with `:` in which case the kernel command line option takes
precedence, if it is specified as well.
+* `$SYSTEMD_DEVICE_VERIFY_SYSFS` — if set to "0", disables verification that
+ devices sysfs path are actually backed by sysfs. Relaxing this verification
+ is useful for testing purposes.
+
`nss-systemd`:
* `$SYSTEMD_NSS_BYPASS_SYNTHETIC=1` — if set, `nss-systemd` won't synthesize
# available there or from the github repository otherwise)
$ 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
-$ meson build # configure the build
-$ meson compile -C build # build it locally, see if everything compiles fine
-$ meson test -C build # run some simple regression tests
-$ sudo mkosi # build a test image
-$ sudo mkosi boot # boot up the test image
-$ git add -p # interactively put together your patch
-$ git commit # commit it
-$ git push -u <REMOTE> # where REMOTE is your "fork" on GitHub
+$ 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
+$ meson build # configure the build
+$ ninja -C build # build it locally, see if everything compiles fine
+$ meson test -C build # run some simple regression tests
+$ sudo mkosi # build a test image
+$ sudo mkosi boot # boot up the test 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"
# Steps to a Successful Release
1. Add all items to NEWS
-2. Update the contributors list in NEWS (`meson compile -C build git-contrib`)
+2. Update the contributors list in NEWS (`ninja -C build git-contrib`)
3. Update the time and place in NEWS
-4. Update hwdb (`meson compile -C build update-hwdb update-hwdb-autosuspend`)
-5. [RC1] Update version and library numbers in `meson.build`
-6. Check dbus docs with `meson compile -C build update-dbus-docs`
-7. Tag the release: `version=vXXX-rcY && git tag -s "${version}" -m "systemd ${version}"`
-8. Do `meson compile -C build`
-9. Make sure that the version string and package string match: `build/systemctl --version`
-10. Upload the documentation: `meson compile -C build doc-sync`
-11. [FINAL] Close the github milestone and open a new one (https://github.com/systemd/systemd/milestones)
-12. "Draft" a new release on github (https://github.com/systemd/systemd/releases/new), mark "This is a pre-release" if appropriate.
-13. Check that announcement to systemd-devel, with a copy&paste from NEWS, was sent. This should happen automatically.
-14. Update IRC topic (`/msg chanserv TOPIC #systemd Version NNN released`)
-15. [FINAL] Push commits to stable, create an empty -stable branch: `git push systemd-stable --atomic origin/main:main origin/main:refs/heads/${version}-stable`, and change the default branch to latest release (https://github.com/systemd/systemd-stable/settings/branches).
+4. Update hwdb (`ninja -C build update-hwdb`, `ninja -C build update-hwdb-autosuspend`, commit separately).
+5. Update syscall numbers (`ninja -C build update-syscall-tables update-syscall-headers`).
+6. [RC1] Update version and library numbers in `meson.build`
+7. Check dbus docs with `ninja -C build update-dbus-docs`
+8. Tag the release: `version=vXXX-rcY && git tag -s "${version}" -m "systemd ${version}"`
+9. Do `ninja -C build`
+10. Make sure that the version string and package string match: `build/systemctl --version`
+11. Upload the documentation: `ninja -C build doc-sync`
+12. [FINAL] Close the github milestone and open a new one (https://github.com/systemd/systemd/milestones)
+13. "Draft" a new release on github (https://github.com/systemd/systemd/releases/new), mark "This is a pre-release" if appropriate.
+14. Check that announcement to systemd-devel, with a copy&paste from NEWS, was sent. This should happen automatically.
+15. Update IRC topic (`/msg chanserv TOPIC #systemd Version NNN released`)
+16. [FINAL] Push commits to stable, create an empty -stable branch: `git push systemd-stable --atomic origin/main:main origin/main:refs/heads/${version}-stable`, and change the default branch to latest release (https://github.com/systemd/systemd-stable/settings/branches).
initial template:
```
-$ meson compile -C build/ systemd-pot
+$ ninja -C build/ systemd-pot
```
This will generate file `po/systemd.pot` in the source tree.
Start by updating the `*.po` files from the latest template:
```
-$ meson compile -C build/ systemd-update-po
+$ ninja -C build/ systemd-update-po
```
This will touch all the `*.po` files, so you'll want to pay attention when
You can recompile the `*.po` files using the following command:
```
-$ meson compile -C build/ systemd-gmo
+$ ninja -C build/ systemd-gmo
```
The resulting files will be saved in the `build/po/` directory.
<refsect1>
<title>Description</title>
- <para><filename>libudev.h</filename> provides APIs to introspect
- and enumerate devices on the local system.</para>
+ <para><filename>libudev.h</filename> provides an API to introspect and enumerate devices on the local
+ system. This library is supported, but should not be used in new projects. Please see
+ <citerefentry><refentrytitle>sd-device</refentrytitle><manvolnum>3</manvolnum></citerefentry> for an
+ equivalent replacement with a more modern API.</para>
<para>All functions require a libudev context to operate. This
context can be create via
change between the build and the deployment phases, it is possible to relax this check: if exactly one
file whose name matches <literal><filename>extension-release.*</filename></literal> is present in this
directory, and the file is tagged with a <varname>user.extension-release.strict</varname>
- <citerefentry><refentrytitle>xattr</refentrytitle><manvolnum>7</manvolnum></citerefentry> set to the
+ <citerefentry project='man-pages'><refentrytitle>xattr</refentrytitle><manvolnum>7</manvolnum></citerefentry> set to the
string <literal>0</literal>, it will be used instead.</para>
<para>The rest of this document that talks about <filename>os-release</filename> should be understood
<example>
<title>Reading <filename>os-release</filename> in
- <citerefentry><refentrytitle>sh</refentrytitle><manvolnum>1</manvolnum></citerefentry></title>
+ <citerefentry project='man-pages'><refentrytitle>sh</refentrytitle><manvolnum>1</manvolnum></citerefentry></title>
<programlisting><xi:include href="check-os-release.sh" parse="text" /></programlisting>
</example>
<example>
<title>Reading <filename>os-release</filename> in
- <citerefentry><refentrytitle>python</refentrytitle><manvolnum>1</manvolnum></citerefentry> (versions >= 3.10)</title>
+ <citerefentry project='die-net'><refentrytitle>python</refentrytitle><manvolnum>1</manvolnum></citerefentry> (versions >= 3.10)</title>
<programlisting><xi:include href="check-os-release-simple.py" parse="text" /></programlisting>
<example>
<title>Reading <filename>os-release</filename> in
- <citerefentry><refentrytitle>python</refentrytitle><manvolnum>1</manvolnum></citerefentry> (any version)</title>
+ <citerefentry project='die-net'><refentrytitle>python</refentrytitle><manvolnum>1</manvolnum></citerefentry> (any version)</title>
<programlisting><xi:include href="check-os-release.py" parse="text" /></programlisting>
'SD_NOTICE',
'SD_WARNING'],
''],
+ ['sd-device', '3', [], ''],
['sd-event', '3', [], ''],
['sd-hwdb', '3', [], ''],
['sd-id128',
'sd_device_get_sysname',
'sd_device_get_sysnum'],
''],
+ ['sd_device_ref', '3', ['sd_device_unref', 'sd_device_unrefp'], ''],
['sd_event_add_child',
'3',
['sd_event_add_child_pidfd',
--- /dev/null
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="sd-device" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd-device</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd-device</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd-device</refname>
+ <refpurpose>API for enumerating and introspecting local devices</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include <systemd/sd-device.h></funcsynopsisinfo>
+ </funcsynopsis>
+
+ <cmdsynopsis>
+ <command>pkg-config --cflags --libs libsystemd</command>
+ </cmdsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><filename>sd-device.h</filename> provides an API to introspect and enumerate devices on the local
+ system. It provides a programmatic interface to the database of devices and their properties mananaged by
+ <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+ This API is a replacement for
+ <citerefentry><refentrytitle>libudev</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
+ <filename>libudev.h</filename>.</para>
+
+ <para>See
+ <literallayout><citerefentry><refentrytitle>sd_device_get_syspath</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_device_ref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+</literallayout>
+ for more information about the functions available.</para>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>udevadm</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
<citerefentry><refentrytitle>sd_bus_message_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
</para>
- <para><function>sd_bus_message_ref()</function> increases the reference counter of
+ <para><function>sd_bus_message_ref()</function> increases the internal reference counter of
<parameter>m</parameter> by one.</para>
- <para><function>sd_bus_message_unref()</function> decreases the reference counter of
+ <para><function>sd_bus_message_unref()</function> decreases the internal reference counter of
<parameter>m</parameter> by one. Once the reference count has dropped to zero, message object is
- destroyed and cannot be used anymore, so further calls to
- <function>sd_bus_message_ref()</function> or <function>sd_bus_message_unref()</function> are
- illegal.</para>
+ destroyed and cannot be used anymore, so further calls to <function>sd_bus_message_ref()</function> or
+ <function>sd_bus_message_unref()</function> are illegal.</para>
<para><function>sd_bus_message_unrefp()</function> is similar to
<function>sd_bus_message_unref()</function> but takes a pointer to a
pointer to an <type>sd_bus</type> object. This call is useful in
conjunction with GCC's and LLVM's <ulink
url="https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html">Clean-up
- Variable Attribute</ulink>. Note that this function is defined as
+ Variable Attribute</ulink>. Note that this function is defined as an
inline function. Use a declaration like the following, in order to
allocate a bus object that is freed automatically as the code
block is left:</para>
…
}</programlisting>
- <para><function>sd_bus_ref()</function> and <function>sd_bus_unref()</function>
- execute no operation if the passed in bus object address is
- <constant>NULL</constant>. <function>sd_bus_unrefp()</function> will first
- dereference its argument, which must not be <constant>NULL</constant>, and will
- execute no operation if <emphasis>that</emphasis> is <constant>NULL</constant>.
- </para>
+ <para><function>sd_bus_ref()</function> and <function>sd_bus_unref()</function> execute no operation if
+ the argument is <constant>NULL</constant>. <function>sd_bus_unrefp()</function> will first dereference
+ its argument, which must not be <constant>NULL</constant>, and will execute no operation if
+ <emphasis>that</emphasis> is <constant>NULL</constant>.</para>
<para><function>sd_bus_close_unref()</function> is similar to <function>sd_bus_unref()</function>, but
first executes
<refsect1>
<title>Description</title>
- <para><function>sd_bus_slot_ref()</function> increases the reference counter of
+ <para><function>sd_bus_slot_ref()</function> increases the internal reference counter of
<parameter>slot</parameter> by one.</para>
- <para><function>sd_bus_slot_unref()</function> decreases the reference counter of
+ <para><function>sd_bus_slot_unref()</function> decreases the internal reference counter of
<parameter>slot</parameter> by one. Once the reference count has dropped to zero, slot object is
destroyed and cannot be used anymore, so further calls to <function>sd_bus_slot_ref()</function>
or <function>sd_bus_slot_unref()</function> are illegal.</para>
--- /dev/null
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="sd_device_ref" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <refentryinfo>
+ <title>sd_device_ref</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_device_ref</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_device_ref</refname>
+ <refname>sd_device_unref</refname>
+ <refname>sd_device_unrefp</refname>
+
+ <refpurpose>Create or destroy references to a device object</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include <systemd/sd-device.h></funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>sd_device* <function>sd_device_ref</function></funcdef>
+ <paramdef>sd_device *<parameter>device</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>sd_device* <function>sd_device_unref</function></funcdef>
+ <paramdef>sd_device *<parameter>device</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>void <function>sd_device_unrefp</function></funcdef>
+ <paramdef>sd_device **<parameter>device</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+
+ <para><function>sd_device_ref()</function> increases the internal reference counter of
+ <parameter>device</parameter> by one.</para>
+
+ <para><function>sd_device_unref()</function> decreases the internal reference counter of
+ <parameter>device</parameter> by one. Once the reference count has dropped to zero,
+ <parameter>device</parameter> is destroyed and cannot be used anymore, so further calls to
+ <function>sd_device_ref()</function> or <function>sd_device_unref()</function> are illegal.</para>
+
+ <para><function>sd_device_unrefp()</function> is similar to <function>sd_device_unref()</function> but
+ takes a pointer to a pointer to an <type>sd_device</type> object. This call is useful in conjunction with
+ GCC's and LLVM's <ulink url="https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html">Clean-up
+ Variable Attribute</ulink>. Note that this function is defined as an inline function. Use a declaration
+ like the following, in order to allocate a device object that is freed automatically as the code block is
+ left:</para>
+
+ <programlisting>{
+ __attribute__((cleanup(sd_device_unrefp))) sd_device *device = NULL;
+ int r;
+ …
+ r = sd_device_new_from_syspath(&device, "…");
+ if (r < 0)
+ fprintf(stderr, "Failed to allocate device: %s\n", strerror(-r));
+ …
+}</programlisting>
+
+ <para><function>sd_device_ref()</function> and <function>sd_device_unref()</function> execute no
+ operation if the argument is <constant>NULL</constant>. <function>sd_device_unrefp()</function> will
+ first dereference its argument, which must not be <constant>NULL</constant>, and will execute no
+ operation if <emphasis>that</emphasis> is <constant>NULL</constant>.</para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para><function>sd_device_ref()</function> always returns the argument, and
+ <function>sd_device_unref()</function> always returns <constant>NULL</constant>.
+ </para>
+ </refsect1>
+</refentry>
<refsect1>
<title>Description</title>
- <para><function>sd_event_source_unref()</function> may be used to
- decrement by one the reference counter of the event source object
- specified as <parameter>source</parameter>. The reference counter
- is initially set to one, when the event source is created with calls
- such as
- <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- or
+ <para><function>sd_event_source_unref()</function> may be used to decrement by one the internal reference
+ counter of the event source object specified as <parameter>source</parameter>. The reference counter is
+ initially set to one, when the event source is created with calls such as
+ <citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry> or
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>. When
- the reference counter reaches zero it is removed from its event loop
- object and destroyed.</para>
+ the reference counter reaches zero, the object is detached from the event loop object and destroyed.
+ </para>
<para><function>sd_event_source_unrefp()</function> is similar to
<function>sd_event_source_unref()</function> but takes a pointer to a
Variable Attribute</ulink>. Note that this function is defined as
inline function.</para>
- <para><function>sd_event_source_ref()</function> may be used
- to increase by one the reference counter of the event source object
- specified as <parameter>source</parameter>.</para>
+ <para><function>sd_event_source_ref()</function> may be used to increase by one the internal reference
+ counter of the event source object specified as <parameter>source</parameter>.</para>
<para><function>sd_event_source_unref()</function>,
<function>sd_bus_creds_unrefp()</function> and
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<tbody>
+ <!-- note: units also use the following deprecated specifiers that are not documented:
+ %c, %r, %R. Do not reuse. -->
+
<row id='a'>
<entry><literal>%a</literal></entry>
<entry>Architecture</entry>
<entry>Short host name</entry>
<entry>The hostname of the running system, truncated at the first dot to remove any domain component.</entry>
</row>
- <row id='R'>
- <entry><literal>%R</literal></entry>
- <entry>Pretty host name</entry>
- <entry>The pretty hostname of the running system, as read from the <varname>PRETTY_HOSTNAME=</varname> field of <filename>/etc/machine-info</filename>. If not set, resolves to the short hostname. See <citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information.</entry>
- </row>
<row id='m'>
<entry><literal>%m</literal></entry>
<entry>Machine ID</entry>
<entry>Operating system ID</entry>
<entry>The operating system identifier of the running system, as read from the <varname>ID=</varname> field of <filename>/etc/os-release</filename>. See <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information.</entry>
</row>
+ <row id='q'>
+ <entry><literal>%q</literal></entry>
+ <entry>Pretty host name</entry>
+ <entry>The pretty hostname of the running system, as read from the <varname>PRETTY_HOSTNAME=</varname> field of <filename>/etc/machine-info</filename>. If not set, resolves to the short hostname. See <citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information.</entry>
+ </row>
<row id='T'>
<entry><literal>%T</literal></entry>
<entry>Directory for temporary files</entry>
its configuration. The lockout mechanism is a global property of the TPM,
<command>systemd-cryptenroll</command> does not control or configure the lockout mechanism. You may
use tpm2-tss tools to inspect or configure the dictionary attack lockout, with
- <citerefentry><refentrytitle>tpm2_getcap</refentrytitle><manvolnum>1</manvolnum></citerefentry> and
- <citerefentry><refentrytitle>tpm2_dictionarylockout</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ <citerefentry project='mankier'><refentrytitle>tpm2_getcap</refentrytitle><manvolnum>1</manvolnum></citerefentry> and
+ <citerefentry project='mankier'><refentrytitle>tpm2_dictionarylockout</refentrytitle><manvolnum>1</manvolnum></citerefentry>
commands, respectively.</para></listitem>
</varlistentry>
protected block devices. It should be instantiated for each device that requires integrity
protection.</para>
- <para>At early boot and when the system manager configuration is reloaded, entries from /etc/integritytab are converted into
- <filename>systemd-integritysetup@.service</filename> units by
+ <para>At early boot and when the system manager configuration is reloaded, entries from
+ <citerefentry><refentrytitle>integritytab</refentrytitle><manvolnum>5</manvolnum></citerefentry> are
+ converted into <filename>systemd-integritysetup@.service</filename> units by
<citerefentry><refentrytitle>systemd-integritysetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para><filename>systemd-integritysetup@.service</filename> calls <command>systemd-integritysetup</command>.</para>
<listitem><para>Create a block device <replaceable>volume</replaceable> using
<replaceable>device</replaceable>. See
- <citerefentry><refentrytitle>systemd-integritytab</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>integritytab</refentrytitle><manvolnum>5</manvolnum></citerefentry>
and
<ulink url="https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/dm-integrity.html">
Kernel dm-integrity</ulink> documentation for details.
<example>
<title>Allowing access to the tty</title>
- <para>The following command invokes <citerefentry><refentrytitle>bash</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ <para>The following command invokes <citerefentry project='die-net'><refentrytitle>bash</refentrytitle><manvolnum>1</manvolnum></citerefentry>
as a service passing its standard input, output and error to the calling TTY.</para>
<programlisting># systemd-run -t --send-sighup bash</programlisting>
<title>Assembling Kernel Images</title>
<para>In order to assemble an UEFI PE kernel image from various components as described above, use an
- <citerefentry><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry> command line
+ <citerefentry project='man-pages'><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry> command line
like this:</para>
<programlisting>objcopy \
UEFI boot stub.</para>
<para>To then sign the resulting image for UEFI SecureBoot use an
- <citerefentry><refentrytitle>sbsign</refentrytitle><manvolnum>1</manvolnum></citerefentry> command like
+ <citerefentry project='archlinux'><refentrytitle>sbsign</refentrytitle><manvolnum>1</manvolnum></citerefentry> command like
the following:</para>
<programlisting>sbsign \
<citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>,
<ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>,
- <citerefentry><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>sbsign</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ <citerefentry project='man-pages'><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry project='archlinux'><refentrytitle>sbsign</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>
for the first time, it may not be used for changing the password or shell of an account that already
exists.</para>
- <para>Use <citerefentry><refentrytitle>mkpasswd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ <para>Use <citerefentry project='man-pages'><refentrytitle>mkpasswd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for generating UNIX password hashes from the command line.</para>
</refsect1>
<citerefentry><refentrytitle>sysusers.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<ulink url="https://systemd.io/UIDS-GIDS">Users, Groups, UIDs and GIDs on systemd systems</ulink>,
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>mkpasswd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ <citerefentry project='man-pages'><refentrytitle>mkpasswd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
directories listed above.
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> will execute
these binaries very early at bootup and at configuration reload time — before unit files are
- loaded. Their main purpose is to convert configuration that is not native to the service manager into
- dynamically generated unit files, symlinks or unit file drop-ins, so that they can extend the unit file
- hierarchy the service manager subsequently loads and operates on.</para>
-
- <para>Each generator is called with three directory paths that are to be used for
- generator output. In these three directories, generators may dynamically generate
- unit files (regular ones, instances, as well as templates), unit file
- <filename>.d/</filename> drop-ins, and create symbolic links to unit files to add
- additional dependencies, create aliases, or instantiate existing templates. Those
- directories are included in the unit load path of
- <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- allowing generated configuration to extend or override existing
- definitions.</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 vendor configuration in
- <filename>/usr/</filename>, and <filename>…/generator.late</filename> has priority
- lower than all other configuration. See the next section and the discussion of
- unit load paths and unit overriding in
+ loaded. Their main purpose is to convert configuration and execution context parameters that are not
+ native to the service manager into dynamically generated unit files, symlinks or unit file drop-ins, so
+ that they can extend the unit file hierarchy the service manager subsequently loads and operates
+ on.</para>
+
+ <para>Each generator is called with three directory paths that are to be used for generator output. In
+ these three directories, generators may dynamically generate unit files (regular ones, instances, as well
+ as templates), unit file <filename>.d/</filename> drop-ins, and create symbolic links to unit files to
+ add additional dependencies, create aliases, or instantiate existing templates. Those directories are
+ included in the unit load path of
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, allowing
+ generated configuration to extend or override existing definitions.</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
+ vendor configuration in <filename>/usr/</filename>, and <filename>…/generator.late</filename> has
+ priority lower than all other configuration. See the next section and the discussion of unit load paths
+ and unit overriding in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
- <para>Generators are loaded from a set of paths determined during
- compilation, as listed above. System and user generators are loaded
- from directories with names ending in
- <filename>system-generators/</filename> and
- <filename>user-generators/</filename>, respectively. Generators
- found in directories listed earlier override the ones with the
- same name in directories lower in the list. A symlink to
- <filename>/dev/null</filename> or an empty file can be used to
- mask a generator, thereby preventing it from running. Please note
- that the order of the two directories with the highest priority is
- reversed with respect to the unit load path, and generators in
- <filename>/run/</filename> overwrite those in
- <filename>/etc/</filename>.</para>
-
- <para>After installing new generators or updating the
- configuration, <command>systemctl daemon-reload</command> may be
- executed. This will delete the previous configuration created by
- generators, re-run all generators, and cause
- <command>systemd</command> to reload units from disk. See
- <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- for more information.
+ <para>Generators are loaded from a set of paths determined during compilation, as listed above. System
+ and user generators are loaded from directories with names ending in
+ <filename>system-generators/</filename> and <filename>user-generators/</filename>,
+ respectively. Generators found in directories listed earlier override the ones with the same name in
+ directories lower in the list. A symlink to <filename>/dev/null</filename> or an empty file can be used
+ to mask a generator, thereby preventing it from running. Please note that the order of the two
+ directories with the highest priority is reversed with respect to the unit load path, and generators in
+ <filename>/run/</filename> overwrite those in <filename>/etc/</filename>.</para>
+
+ <para>After installing new generators or updating the configuration, <command>systemctl
+ daemon-reload</command> may be executed. This will delete the previous configuration created by
+ generators, re-run all generators, and cause <command>systemd</command> to reload units from disk. See
+ <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for more
+ information.
</para>
</refsect1>
<refsect1>
<title>Output directories</title>
- <para>Generators are invoked with three arguments: paths to directories where
- generators can place their generated unit files or symlinks. By default those
- paths are runtime directories that are included in the search path of
- <command>systemd</command>, but a generator may be called with different paths
- for debugging purposes.</para>
+ <para>Generators are invoked with three arguments: paths to directories where generators can place their
+ generated unit files or symlinks. By default those paths are runtime directories that are included in the
+ search path of <command>systemd</command>, but a generator may be called with different paths for
+ debugging purposes.</para>
<orderedlist>
<listitem>
<para><parameter>normal-dir</parameter></para>
- <para>In normal use this is <filename>/run/systemd/generator</filename> in
- case of the system generators and
- <filename>$XDG_RUNTIME_DIR/generator</filename> in case of the user
- generators. Unit files placed in this directory take precedence over vendor
- unit configuration but not over native user/administrator unit configuration.
+ <para>In normal use this is <filename>/run/systemd/generator</filename> in case of the system
+ generators and <filename>$XDG_RUNTIME_DIR/generator</filename> in case of the user generators. Unit
+ files placed in this directory take precedence over vendor unit configuration but not over native
+ user/administrator unit configuration.
</para>
</listitem>
<listitem>
<para><parameter>early-dir</parameter></para>
- <para>In normal use this is <filename>/run/systemd/generator.early</filename>
- in case of the system generators and
- <filename>$XDG_RUNTIME_DIR/generator.early</filename> in case of the user
- generators. Unit files placed in this directory override unit files in
- <filename>/usr/</filename>, <filename>/run/</filename> and
- <filename>/etc/</filename>. This means that unit files placed in this
- directory take precedence over all normal configuration, both vendor and
- user/administrator.</para>
+ <para>In normal use this is <filename>/run/systemd/generator.early</filename> in case of the system
+ generators and <filename>$XDG_RUNTIME_DIR/generator.early</filename> in case of the user
+ generators. Unit files placed in this directory override unit files in <filename>/usr/</filename>,
+ <filename>/run/</filename> and <filename>/etc/</filename>. This means that unit files placed in this
+ directory take precedence over all normal configuration, both vendor and user/administrator.</para>
</listitem>
<listitem>
<para><parameter>late-dir</parameter></para>
- <para>In normal use this is <filename>/run/systemd/generator.late</filename>
- in case of the system generators and
- <filename>$XDG_RUNTIME_DIR/generator.late</filename> in case of the user
- generators. This directory may be used to extend the unit file tree without
- overriding any other unit files. Any native configuration files supplied by
- the vendor or user/administrator take precedence.</para>
+ <para>In normal use this is <filename>/run/systemd/generator.late</filename> in case of the system
+ generators and <filename>$XDG_RUNTIME_DIR/generator.late</filename> in case of the user
+ generators. This directory may be used to extend the unit file tree without overriding any other unit
+ files. Any native configuration files supplied by the vendor or user/administrator take
+ precedence.</para>
</listitem>
</orderedlist>
</refsect1>
+ <refsect1>
+ <title>Environment</title>
+
+ <para>The service manager sets a number of environment variables when invoking generator
+ executables. They carry information about the execution context of the generator, in order to simplify
+ conditionalizing generators to specific environments. The following environment variables are set:</para>
+
+ <variablelist class='environment-variables'>
+ <varlistentry>
+ <term><varname>$SYSTEMD_SCOPE</varname></term>
+
+ <listitem><para>If the generator is invoked from the system service manager this variable is set to
+ <literal>system</literal>; if invoked from the per-user service manager it is set to
+ <literal>user</literal>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>$SYSTEMD_IN_INITRD</varname></term>
+
+ <listitem><para>If the generator is run as part of an initial RAM file system (initrd) this is set to
+ <literal>1</literal>. If it is run from the regular host (i.e. after the transition from initrd to
+ host) it is set to <literal>0</literal>. This environment variable is only set for system
+ generators.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>$SYSTEMD_FIRST_BOOT</varname></term>
+
+ <listitem><para>If this boot-up cycle is considered a "first boot", this is set to
+ <literal>1</literal>; if it is a subsequent, regular boot it is set to <literal>0</literal>. For
+ details see the documentation of <varname>ConditionFirstBoot=</varname> in
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
+ environment variable is only set for system generators.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>$SYSTEMD_VIRTUALIZATION</varname></term>
+
+ <listitem><para>If the service manager is run in a virtualized environment,
+ <varname>$SYSTEMD_VIRTUALIZATION</varname> is set to a pair of strings, separated by a colon. The
+ first string is either <literal>vm</literal> or <literal>container</literal>, categorizing the type
+ of virtualization. The second string identifies the implementation of the virtualization
+ technology. If no virtualization is detected this variable will not be set. This data is identical to
+ what
+ <citerefentry><refentrytitle>systemd-detect-virt</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ detects and reports, and uses the same vocabulary of virtualization implementation
+ identifiers.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>$SYSTEMD_ARCHITECTURE</varname></term>
+
+ <listitem><para>This variable is set to a short identifier of the reported architecture of the
+ system. For details about defined values, see documentation of
+ <varname>ConditionArchitecture=</varname> in
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
<refsect1>
<title>Notes about writing generators</title>
<itemizedlist>
<listitem>
- <para>All generators are executed in parallel. That means all executables are
- started at the very same time and need to be able to cope with this
- parallelism.
+ <para>All generators are executed in parallel. That means all executables are started at the very
+ same time and need to be able to cope with this parallelism.
</para>
</listitem>
</listitem>
<listitem>
- <para>Units written by generators are removed when the configuration is
- reloaded. That means the lifetime of the generated units is closely bound to
- the reload cycles of <command>systemd</command> itself.</para>
+ <para>Units written by generators are removed when the configuration is reloaded. That means the
+ lifetime of the generated units is closely bound to the reload cycles of <command>systemd</command>
+ itself.</para>
</listitem>
<listitem>
<para>Since
<citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- is not available (see above), log messages have to be written to
- <filename>/dev/kmsg</filename> instead.</para>
+ is not available (see above), log messages have to be written to <filename>/dev/kmsg</filename>
+ instead.</para>
</listitem>
<listitem>
</listitem>
<listitem>
- <para>Generators may write out dynamic unit files or just hook unit files
- into other units with the usual <filename>.wants/</filename> or
- <filename>.requires/</filename> symlinks. Often, it is nicer to simply
- instantiate a template unit file from <filename>/usr/</filename> with a
- generator instead of writing out entirely dynamic unit files. Of course, this
- works only if a single parameter is to be used.</para>
+ <para>Generators may write out dynamic unit files or just hook unit files into other units with the
+ usual <filename>.wants/</filename> or <filename>.requires/</filename> symlinks. Often, it is nicer to
+ simply instantiate a template unit file from <filename>/usr/</filename> with a generator instead of
+ writing out entirely dynamic unit files. Of course, this works only if a single parameter is to be
+ used.</para>
</listitem>
<listitem>
- <para>If you are careful, you can implement generators in shell scripts. We
- do recommend C code however, since generators are executed synchronously and
- hence delay the entire boot if they are slow.</para>
+ <para>If you are careful, you can implement generators in shell scripts. We do recommend C code
+ however, since generators are executed synchronously and hence delay the entire boot if they are
+ slow.</para>
</listitem>
<listitem>
- <para>Regarding overriding semantics: there are two rules we try to follow
- when thinking about the overriding semantics:</para>
+ <para>Regarding overriding semantics: there are two rules we try to follow when thinking about the
+ overriding semantics:</para>
<orderedlist numeration="lowerroman">
<listitem>
- <para>User configuration should override vendor configuration. This
- (mostly) means that stuff from <filename>/etc/</filename> should override
- stuff from <filename>/usr/</filename>.</para>
+ <para>User configuration should override vendor configuration. This (mostly) means that stuff
+ from <filename>/etc/</filename> should override stuff from <filename>/usr/</filename>.</para>
</listitem>
<listitem>
- <para>Native configuration should override non-native configuration. This
- (mostly) means that stuff you generate should never override native unit
- files for the same purpose.</para>
+ <para>Native configuration should override non-native configuration. This (mostly) means that
+ stuff you generate should never override native unit files for the same purpose.</para>
</listitem>
</orderedlist>
- <para>Of these two rules the first rule is probably the more important one
- and breaks the second one sometimes. Hence, when deciding whether to use
- argv[1], argv[2], or argv[3], your default choice should probably be
- argv[1].</para>
+ <para>Of these two rules the first rule is probably the more important one and breaks the second one
+ sometimes. Hence, when deciding whether to use argv[1], argv[2], or argv[3], your default choice
+ should probably be argv[1].</para>
</listitem>
<listitem>
- <para>Instead of heading off now and writing all kind of generators for
- legacy configuration file formats, please think twice! It is often a better
- idea to just deprecate old stuff instead of keeping it artificially alive.
+ <para>Instead of heading off now and writing all kind of generators for legacy configuration file
+ formats, please think twice! It is often a better idea to just deprecate old stuff instead of keeping
+ it artificially alive.
</para>
</listitem>
</itemizedlist>
<title>systemd-fstab-generator</title>
<para><citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- converts <filename>/etc/fstab</filename> into native mount units. It uses
- argv[1] as location to place the generated unit files in order to allow the
- user to override <filename>/etc/fstab</filename> with their own native unit
- files, but also to ensure that <filename>/etc/fstab</filename> overrides any
+ converts <filename>/etc/fstab</filename> into native mount units. It uses argv[1] as location to place
+ the generated unit files in order to allow the user to override <filename>/etc/fstab</filename> with
+ their own native unit files, but also to ensure that <filename>/etc/fstab</filename> overrides any
vendor default from <filename>/usr/</filename>.</para>
- <para>After editing <filename>/etc/fstab</filename>, the user should invoke
- <command>systemctl daemon-reload</command>. This will re-run all generators and
- cause <command>systemd</command> to reload units from disk. To actually mount
- new directories added to <filename>fstab</filename>, <command>systemctl start
- <replaceable>/path/to/mountpoint</replaceable></command> or <command>systemctl
+ <para>After editing <filename>/etc/fstab</filename>, the user should invoke <command>systemctl
+ daemon-reload</command>. This will re-run all generators and cause <command>systemd</command> to reload
+ units from disk. To actually mount new directories added to <filename>fstab</filename>,
+ <command>systemctl start <replaceable>/path/to/mountpoint</replaceable></command> or <command>systemctl
start local-fs.target</command> may be used.</para>
</example>
<title>systemd-system-update-generator</title>
<para><citerefentry><refentrytitle>systemd-system-update-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- temporarily redirects <filename>default.target</filename> to
- <filename>system-update.target</filename>, if a system update is
- scheduled. Since this needs to override the default user configuration for
- <filename>default.target</filename>, it uses argv[2]. For details about this
- logic, see
+ temporarily redirects <filename>default.target</filename> to <filename>system-update.target</filename>,
+ if a system update is scheduled. Since this needs to override the default user configuration for
+ <filename>default.target</filename>, it uses argv[2]. For details about this logic, see
<citerefentry><refentrytitle>systemd.offline-updates</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
</para>
</example>
<term><varname>RoutingAlgorithm=</varname></term>
<listitem>
<para>This can be either <literal>batman-v</literal> or <literal>batman-iv</literal> and describes which routing_algo
- of <citerefentry><refentrytitle>batctl</refentrytitle><manvolnum>8</manvolnum></citerefentry> to use. The algorithm
+ of <citerefentry project='mankier'><refentrytitle>batctl</refentrytitle><manvolnum>8</manvolnum></citerefentry> to use. The algorithm
cannot be changed after interface creation. Defaults to <literal>batman-v</literal>.
</para>
</listitem>
<row>
<!-- We do not use the common definition from standard-specifiers.xml here since we want a
slightly more verbose explanation here, referring to the reload cycle. -->
- <entry><literal>%R</literal></entry>
+ <entry><literal>%q</literal></entry>
<entry>Pretty host name</entry>
<entry>The pretty hostname of the running system at the point in time the unit configuration is loaded, as read from the <varname>PRETTY_HOSTNAME=</varname> field of <filename>/etc/machine-info</filename>. If not set, resolves to the short hostname. See <citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information.</entry>
</row>
along the chain, up to the root of sysfs that can be used in udev rules.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-t</option></term>
+ <term><option>--tree</option></term>
+ <listitem>
+ <para>Display a sysfs tree. This recursively iterates through the sysfs hierarchy and displays it
+ in a tree structure. If a path is specified only the subtree below that directory is
+ shown. This will show both device and subsystem items.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><option>-x</option></term>
<term><option>--export</option></term>
does not probe a block device while changes are made to it, for example partitions created or file
systems formatted. Note that many tools that interface with block devices natively support taking
relevant locks, see for example
- <citerefentry><refentrytitle>sfdisk</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
+ <citerefentry project='man-pages'><refentrytitle>sfdisk</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
<option>--lock</option> switch.</para>
<para>The command expects at least one block device specified via <option>--device=</option> or
# More items are added later after they have been detected.
summary({'build mode' : get_option('mode')})
-# GCOV doesn't define any macro when compiled with, so let's define it ourselves
-conf.set10('BUILT_WITH_COVERAGE', get_option('b_coverage'))
-
#####################################################################
# Try to install the git pre-commit hook
############################################################
+if get_option('b_coverage')
+ add_project_arguments('-include', 'src/basic/coverage.h', language : 'c')
+endif
+
+############################################################
+
config_h = configure_file(
output : 'config.h',
configuration : conf)
['link-boot-shared', get_option('link-boot-shared')],
['fexecve'],
['standalone-binaries', get_option('standalone-binaries')],
+ ['coverage', get_option('b_coverage')],
]
if tuple.length() >= 2
return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */
}
-static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) {
+static int log_unsafe_transition(int a, int b, const char *path, ChaseSymlinksFlags flags) {
_cleanup_free_ char *n1 = NULL, *n2 = NULL, *user_a = NULL, *user_b = NULL;
struct stat st;
strna(n1), strna(user_a), special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), strna(n2), strna(user_b), path);
}
-static int log_autofs_mount_point(int fd, const char *path, unsigned flags) {
+static int log_autofs_mount_point(int fd, const char *path, ChaseSymlinksFlags flags) {
_cleanup_free_ char *n1 = NULL;
if (!FLAGS_SET(flags, CHASE_WARN))
strna(n1), path);
}
-int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret_path, int *ret_fd) {
+int chase_symlinks(
+ const char *path,
+ const char *original_root,
+ ChaseSymlinksFlags flags,
+ char **ret_path,
+ int *ret_fd) {
+
_cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
_cleanup_close_ int fd = -1;
unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */
if (isempty(path))
return -EINVAL;
+ /* We don't support relative paths in combination with a root directory */
+ if (FLAGS_SET(flags, CHASE_PREFIX_ROOT) && !path_is_absolute(path))
+ return -EINVAL;
+
/* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
* symlinks relative to a root directory, instead of the root of the host.
*
path_simplify(root);
if (flags & CHASE_PREFIX_ROOT) {
- /* We don't support relative paths in combination with a root directory */
- if (!path_is_absolute(path))
- return -EINVAL;
-
- path = prefix_roota(root, path);
+ buffer = path_join(root, path);
+ if (!buffer)
+ return -ENOMEM;
}
}
- r = path_make_absolute_cwd(path, &buffer);
- if (r < 0)
- return r;
+ if (!buffer) {
+ r = path_make_absolute_cwd(path, &buffer);
+ if (r < 0)
+ return r;
+ }
- fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH);
+ fd = open(empty_to_root(root), O_CLOEXEC|O_DIRECTORY|O_PATH);
if (fd < 0)
return -errno;
todo = buffer;
done = strdup("/");
}
+ if (!done)
+ return -ENOMEM;
for (;;) {
_cleanup_free_ char *first = NULL;
struct stat st;
const char *e;
- r = path_find_first_component(&todo, true, &e);
+ r = path_find_first_component(&todo, /* accept_dot_dot= */ true, &e);
if (r < 0)
return r;
if (r == 0) { /* We reached the end. */
if (empty_or_root(done))
continue;
- parent = dirname_malloc(done);
- if (!parent)
- return -ENOMEM;
+ r = path_extract_directory(done, &parent);
+ if (r < 0)
+ return r;
/* Don't allow this to leave the root dir. */
if (root &&
* directory as base. */
safe_close(fd);
- fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH);
+ fd = open(empty_to_root(root), O_CLOEXEC|O_DIRECTORY|O_PATH);
if (fd < 0)
return -errno;
const char *e;
/* todo may contain slashes at the beginning. */
- r = path_find_first_component(&todo, true, &e);
+ r = path_find_first_component(&todo, /* accept_dot_dot= */ true, &e);
if (r < 0)
return r;
if (r == 0)
int chase_symlinks_and_open(
const char *path,
const char *root,
- unsigned chase_flags,
+ ChaseSymlinksFlags chase_flags,
int open_flags,
char **ret_path) {
int chase_symlinks_and_opendir(
const char *path,
const char *root,
- unsigned chase_flags,
+ ChaseSymlinksFlags chase_flags,
char **ret_path,
DIR **ret_dir) {
int chase_symlinks_and_stat(
const char *path,
const char *root,
- unsigned chase_flags,
+ ChaseSymlinksFlags chase_flags,
char **ret_path,
struct stat *ret_stat,
int *ret_fd) {
int chase_symlinks_and_fopen_unlocked(
const char *path,
const char *root,
- unsigned chase_flags,
+ ChaseSymlinksFlags chase_flags,
const char *open_flags,
char **ret_path,
FILE **ret_file) {
#include "stat-util.h"
-enum {
+typedef enum ChaseSymlinksFlags {
CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */
CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */
CHASE_NO_AUTOFS = 1 << 2, /* Return -EREMOTE if autofs mount point found */
CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's
* right-most component refers to symlink, return O_PATH fd of the symlink. */
CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered */
-};
+} ChaseSymlinksFlags;
bool unsafe_transition(const struct stat *a, const struct stat *b);
/* How many iterations to execute before returning -ELOOP */
#define CHASE_SYMLINKS_MAX 32
-int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret_path, int *ret_fd);
+int chase_symlinks(const char *path_with_prefix, const char *root, ChaseSymlinksFlags chase_flags, char **ret_path, int *ret_fd);
-int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path);
-int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir);
-int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd);
+int chase_symlinks_and_open(const char *path, const char *root, ChaseSymlinksFlags chase_flags, int open_flags, char **ret_path);
+int chase_symlinks_and_opendir(const char *path, const char *root, ChaseSymlinksFlags chase_flags, char **ret_path, DIR **ret_dir);
+int chase_symlinks_and_stat(const char *path, const char *root, ChaseSymlinksFlags chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd);
-int chase_symlinks_and_fopen_unlocked(const char *path, const char *root, unsigned chase_flags, const char *open_flags, char **ret_path, FILE **ret_file);
+int chase_symlinks_and_fopen_unlocked(const char *path, const char *root, ChaseSymlinksFlags chase_flags, const char *open_flags, char **ret_path, FILE **ret_file);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+/* When built with --coverage (gcov) we need to explicitly call __gcov_dump()
+ * in places where we use _exit(), since _exit() skips at-exit hooks resulting
+ * in lost coverage.
+ *
+ * To make sure we don't miss any _exit() calls, this header file is included
+ * explicitly on the compiler command line via the -include directive (only
+ * when built with -Db_coverage=true)
+ * */
+extern void _exit(int);
+extern void __gcov_dump(void);
+
+static inline void _coverage__exit(int status) {
+ __gcov_dump();
+ _exit(status);
+}
+#define _exit(x) _coverage__exit(x)
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <string.h>
+#include <sys/stat.h>
+
+#include "chase-symlinks.h"
+#include "devnum-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "string-util.h"
+
+int parse_devnum(const char *s, dev_t *ret) {
+ const char *major;
+ unsigned x, y;
+ size_t n;
+ int r;
+
+ n = strspn(s, DIGITS);
+ if (n == 0)
+ return -EINVAL;
+ if (n > DECIMAL_STR_MAX(dev_t))
+ return -EINVAL;
+ if (s[n] != ':')
+ return -EINVAL;
+
+ major = strndupa_safe(s, n);
+ r = safe_atou(major, &x);
+ if (r < 0)
+ return r;
+
+ r = safe_atou(s + n + 1, &y);
+ if (r < 0)
+ return r;
+
+ if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y))
+ return -ERANGE;
+
+ *ret = makedev(x, y);
+ return 0;
+}
+
+int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret) {
+ const char *t;
+
+ /* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */
+
+ if (S_ISCHR(mode))
+ t = "char";
+ else if (S_ISBLK(mode))
+ t = "block";
+ else
+ return -ENODEV;
+
+ if (asprintf(ret, "/dev/%s/" DEVNUM_FORMAT_STR, t, DEVNUM_FORMAT_VAL(devnum)) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ /* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */
+
+ assert(ret);
+
+ if (major(devnum) == 0 && minor(devnum) == 0) {
+ char *s;
+
+ /* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in
+ * /dev/block/ and /dev/char/, hence we handle them specially here. */
+
+ if (S_ISCHR(mode))
+ s = strdup("/run/systemd/inaccessible/chr");
+ else if (S_ISBLK(mode))
+ s = strdup("/run/systemd/inaccessible/blk");
+ else
+ return -ENODEV;
+
+ if (!s)
+ return -ENOMEM;
+
+ *ret = s;
+ return 0;
+ }
+
+ r = device_path_make_major_minor(mode, devnum, &p);
+ if (r < 0)
+ return r;
+
+ return chase_symlinks(p, NULL, 0, ret, NULL);
+}
+
+int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devnum) {
+ mode_t mode;
+ dev_t devnum;
+ int r;
+
+ /* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/
+ * paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device
+ * path cannot be parsed like this. */
+
+ if (path_equal(path, "/run/systemd/inaccessible/chr")) {
+ mode = S_IFCHR;
+ devnum = makedev(0, 0);
+ } else if (path_equal(path, "/run/systemd/inaccessible/blk")) {
+ mode = S_IFBLK;
+ devnum = makedev(0, 0);
+ } else {
+ const char *w;
+
+ w = path_startswith(path, "/dev/block/");
+ if (w)
+ mode = S_IFBLK;
+ else {
+ w = path_startswith(path, "/dev/char/");
+ if (!w)
+ return -ENODEV;
+
+ mode = S_IFCHR;
+ }
+
+ r = parse_devnum(w, &devnum);
+ if (r < 0)
+ return r;
+ }
+
+ if (ret_mode)
+ *ret_mode = mode;
+ if (ret_devnum)
+ *ret_devnum = devnum;
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "stdio-util.h"
+
+int parse_devnum(const char *s, dev_t *ret);
+
+/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
+ * specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
+ * major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
+ * comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as
+ * such a test would be pointless in such a case.) */
+
+#define DEVICE_MAJOR_VALID(x) \
+ ({ \
+ typeof(x) _x = (x), _y = 0; \
+ _x >= _y && _x < (UINT32_C(1) << 12); \
+ \
+ })
+
+#define DEVICE_MINOR_VALID(x) \
+ ({ \
+ typeof(x) _x = (x), _y = 0; \
+ _x >= _y && _x < (UINT32_C(1) << 20); \
+ })
+
+int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret);
+int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret);
+int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devnum);
+
+static inline bool devnum_set_and_equal(dev_t a, dev_t b) {
+ /* Returns true if a and b definitely refer to the same device. If either is zero, this means "don't
+ * know" and we'll return false */
+ return a == b && a != 0;
+}
+
+/* Maximum string length for a major:minor string. (Note that DECIMAL_STR_MAX includes space for a trailing NUL) */
+#define DEVNUM_STR_MAX (DECIMAL_STR_MAX(dev_t)-1+1+DECIMAL_STR_MAX(dev_t))
+
+#define DEVNUM_FORMAT_STR "%u:%u"
+#define DEVNUM_FORMAT_VAL(d) major(d), minor(d)
+
+static inline char *format_devnum(dev_t d, char buf[static DEVNUM_STR_MAX]) {
+ return ASSERT_PTR(snprintf_ok(buf, DEVNUM_STR_MAX, DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(d)));
+}
+
+#define FORMAT_DEVNUM(d) format_devnum((d), (char[DEVNUM_STR_MAX]) {})
[SPECIAL_GLYPH_TREE_RIGHT] = "`-",
[SPECIAL_GLYPH_TREE_SPACE] = " ",
[SPECIAL_GLYPH_TREE_TOP] = ",-",
+ [SPECIAL_GLYPH_VERTICAL_DOTTED] = ":",
[SPECIAL_GLYPH_TRIANGULAR_BULLET] = ">",
[SPECIAL_GLYPH_BLACK_CIRCLE] = "*",
[SPECIAL_GLYPH_WHITE_CIRCLE] = "*",
[SPECIAL_GLYPH_TREE_TOP] = u8"┌─",
/* Single glyphs in both cases */
+ [SPECIAL_GLYPH_VERTICAL_DOTTED] = u8"┆",
[SPECIAL_GLYPH_TRIANGULAR_BULLET] = u8"‣",
[SPECIAL_GLYPH_BLACK_CIRCLE] = u8"●",
[SPECIAL_GLYPH_WHITE_CIRCLE] = u8"○",
SPECIAL_GLYPH_TREE_RIGHT,
SPECIAL_GLYPH_TREE_SPACE,
SPECIAL_GLYPH_TREE_TOP,
+ SPECIAL_GLYPH_VERTICAL_DOTTED,
SPECIAL_GLYPH_TRIANGULAR_BULLET,
SPECIAL_GLYPH_BLACK_CIRCLE,
SPECIAL_GLYPH_WHITE_CIRCLE,
#define _alignptr_ __attribute__((__aligned__(sizeof(void*))))
#define _warn_unused_result_ __attribute__((__warn_unused_result__))
-#if defined(BUILT_WITH_COVERAGE) && BUILT_WITH_COVERAGE
-/* We need to explicitly call __gcov_dump() in places where we use _exit(), since
- * _exit() skips at-exit hooks resulting in lost coverage */
-# include <unistd.h>
-extern void __gcov_dump(void);
-
-_noreturn_ static inline void _coverage__exit(int status) {
- __gcov_dump();
- _exit(status);
-}
-# define _exit(x) _coverage__exit(x)
-#endif
-
#if !defined(HAS_FEATURE_MEMORY_SANITIZER)
# if defined(__has_feature)
# if __has_feature(memory_sanitizer)
'conf-files.c',
'conf-files.h',
'def.h',
+ 'devnum-util.c',
+ 'devnum-util.h',
'dirent-util.c',
'dirent-util.h',
'dns-def.h',
* Use 'ninja -C build update-syscall-tables' to download new syscall tables,
* and 'ninja -C build update-syscall-header' to regenerate this file.
*/
+#pragma once
/* Note: if this code looks strange, this is because it is derived from the same
* template as the per-syscall blocks below. */
* Use 'ninja -C build update-syscall-tables' to download new syscall tables,
* and 'ninja -C build update-syscall-header' to regenerate this file.
*/
+#pragma once
''',
file=out)
print(ARCH_CHECK, file=out)
r = namespace_open(pid, NULL, NULL, NULL, &userns_fd, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to open netns fd: %m");
+ return log_error_errno(r, "Failed to open userns fd: %m");
return TAKE_FD(userns_fd);
return 0;
}
-int parse_dev(const char *s, dev_t *ret) {
- const char *major;
- unsigned x, y;
- size_t n;
- int r;
-
- n = strspn(s, DIGITS);
- if (n == 0)
- return -EINVAL;
- if (s[n] != ':')
- return -EINVAL;
-
- major = strndupa_safe(s, n);
- r = safe_atou(major, &x);
- if (r < 0)
- return r;
-
- r = safe_atou(s + n + 1, &y);
- if (r < 0)
- return r;
-
- if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y))
- return -ERANGE;
-
- *ret = makedev(x, y);
- return 0;
-}
-
int parse_oom_score_adjust(const char *s, int *ret) {
int r, v;
typedef unsigned long loadavg_t;
int parse_boolean(const char *v) _pure_;
-int parse_dev(const char *s, dev_t *ret);
int parse_pid(const char *s, pid_t* ret_pid);
int parse_mode(const char *s, mode_t *ret);
int parse_ifindex(const char *s);
#include "extract-word.h"
#include "fd-util.h"
#include "fs-util.h"
-#include "glob-util.h"
#include "log.h"
#include "macro.h"
-#include "nulstr-util.h"
-#include "parse-util.h"
#include "path-util.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
-#include "utf8.h"
int path_split_and_make_absolute(const char *p, char ***ret) {
char **l;
return path;
}
-int path_simplify_and_warn(
- char *path,
- unsigned flag,
- const char *unit,
- const char *filename,
- unsigned line,
- const char *lvalue) {
-
- bool fatal = flag & PATH_CHECK_FATAL;
-
- assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE));
-
- if (!utf8_is_valid(path))
- return log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path);
-
- if (flag & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) {
- bool absolute;
-
- absolute = path_is_absolute(path);
-
- if (!absolute && (flag & PATH_CHECK_ABSOLUTE))
- return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
- "%s= path is not absolute%s: %s",
- lvalue, fatal ? "" : ", ignoring", path);
-
- if (absolute && (flag & PATH_CHECK_RELATIVE))
- return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
- "%s= path is absolute%s: %s",
- lvalue, fatal ? "" : ", ignoring", path);
- }
-
- path_simplify(path);
-
- if (!path_is_valid(path))
- return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
- "%s= path has invalid length (%zu bytes)%s.",
- lvalue, strlen(path), fatal ? "" : ", ignoring");
-
- if (!path_is_normalized(path))
- return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
- "%s= path is not normalized%s: %s",
- lvalue, fatal ? "" : ", ignoring", path);
-
- return 0;
-}
-
char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) {
assert(path);
assert(prefix);
return valid_device_node_path(path);
}
-int systemd_installation_has_version(const char *root, unsigned minimal_version) {
- const char *pattern;
- int r;
-
- /* Try to guess if systemd installation is later than the specified version. This
- * is hacky and likely to yield false negatives, particularly if the installation
- * is non-standard. False positives should be relatively rare.
- */
-
- NULSTR_FOREACH(pattern,
- /* /lib works for systems without usr-merge, and for systems with a sane
- * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary
- * for Gentoo which does a merge without making /lib a symlink.
- */
- "lib/systemd/libsystemd-shared-*.so\0"
- "lib64/systemd/libsystemd-shared-*.so\0"
- "usr/lib/systemd/libsystemd-shared-*.so\0"
- "usr/lib64/systemd/libsystemd-shared-*.so\0") {
-
- _cleanup_strv_free_ char **names = NULL;
- _cleanup_free_ char *path = NULL;
- char *c;
-
- path = path_join(root, pattern);
- if (!path)
- return -ENOMEM;
-
- r = glob_extend(&names, path, 0);
- if (r == -ENOENT)
- continue;
- if (r < 0)
- return r;
-
- assert_se(c = endswith(path, "*.so"));
- *c = '\0'; /* truncate the glob part */
-
- STRV_FOREACH(name, names) {
- /* This is most likely to run only once, hence let's not optimize anything. */
- char *t, *t2;
- unsigned version;
-
- t = startswith(*name, path);
- if (!t)
- continue;
-
- t2 = endswith(t, ".so");
- if (!t2)
- continue;
-
- t2[0] = '\0'; /* truncate the suffix */
-
- r = safe_atou(t, &version);
- if (r < 0) {
- log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name);
- continue;
- }
-
- log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).",
- *name, version,
- version >= minimal_version ? "OK" : "too old");
- if (version >= minimal_version)
- return true;
- }
- }
-
- return false;
-}
-
bool dot_or_dot_dot(const char *path) {
if (!path)
return false;
char* path_simplify(char *path);
-enum {
- PATH_CHECK_FATAL = 1 << 0, /* If not set, then error message is appended with 'ignoring'. */
- PATH_CHECK_ABSOLUTE = 1 << 1,
- PATH_CHECK_RELATIVE = 1 << 2,
-};
-
-int path_simplify_and_warn(char *path, unsigned flag, const char *unit, const char *filename, unsigned line, const char *lvalue);
-
static inline bool path_equal_ptr(const char *a, const char *b) {
return !!a == !!b && (!a || path_equal(a, b));
}
bool valid_device_node_path(const char *path);
bool valid_device_allow_pattern(const char *path);
-int systemd_installation_has_version(const char *root, unsigned minimal_version);
-
bool dot_or_dot_dot(const char *path);
static inline const char *skip_dev_prefix(const char *p) {
return stat_verify_directory(&st);
}
-int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret) {
- const char *t;
-
- /* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */
-
- if (S_ISCHR(mode))
- t = "char";
- else if (S_ISBLK(mode))
- t = "block";
- else
- return -ENODEV;
-
- if (asprintf(ret, "/dev/%s/%u:%u", t, major(devno), minor(devno)) < 0)
- return -ENOMEM;
-
- return 0;
-}
-
-int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) {
- _cleanup_free_ char *p = NULL;
- int r;
-
- /* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */
-
- assert(ret);
-
- if (major(devno) == 0 && minor(devno) == 0) {
- char *s;
-
- /* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in
- * /dev/block/ and /dev/char/, hence we handle them specially here. */
-
- if (S_ISCHR(mode))
- s = strdup("/run/systemd/inaccessible/chr");
- else if (S_ISBLK(mode))
- s = strdup("/run/systemd/inaccessible/blk");
- else
- return -ENODEV;
-
- if (!s)
- return -ENOMEM;
-
- *ret = s;
- return 0;
- }
-
- r = device_path_make_major_minor(mode, devno, &p);
- if (r < 0)
- return r;
-
- return chase_symlinks(p, NULL, 0, ret, NULL);
-}
-
-int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno) {
- mode_t mode;
- dev_t devno;
- int r;
-
- /* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/
- * paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device
- * path cannot be parsed like this. */
-
- if (path_equal(path, "/run/systemd/inaccessible/chr")) {
- mode = S_IFCHR;
- devno = makedev(0, 0);
- } else if (path_equal(path, "/run/systemd/inaccessible/blk")) {
- mode = S_IFBLK;
- devno = makedev(0, 0);
- } else {
- const char *w;
-
- w = path_startswith(path, "/dev/block/");
- if (w)
- mode = S_IFBLK;
- else {
- w = path_startswith(path, "/dev/char/");
- if (!w)
- return -ENODEV;
-
- mode = S_IFCHR;
- }
-
- r = parse_dev(w, &devno);
- if (r < 0)
- return r;
- }
-
- if (ret_mode)
- *ret_mode = mode;
- if (ret_devno)
- *ret_devno = devno;
-
- return 0;
-}
-
int proc_mounted(void) {
int r;
int stat_verify_directory(const struct stat *st);
int fd_verify_directory(int fd);
-/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
- * specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
- * major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
- * comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as
- * such a test would be pointless in such a case.) */
-
-#define DEVICE_MAJOR_VALID(x) \
- ({ \
- typeof(x) _x = (x), _y = 0; \
- _x >= _y && _x < (UINT32_C(1) << 12); \
- \
- })
-
-#define DEVICE_MINOR_VALID(x) \
- ({ \
- typeof(x) _x = (x), _y = 0; \
- _x >= _y && _x < (UINT32_C(1) << 20); \
- })
-
-int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret);
-int device_path_make_canonical(mode_t mode, dev_t devno, char **ret);
-int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno);
-
int proc_mounted(void);
bool stat_inode_same(const struct stat *a, const struct stat *b);
struct new_statx nsx; \
} var
#endif
-
-static inline bool devid_set_and_equal(dev_t a, dev_t b) {
- /* Returns true if a and b definitely refer to the same device. If either is zero, this means "don't
- * know" and we'll return false */
- return a == b && a != 0;
-}
sendmsg
sendto
set_mempolicy
+set_mempolicy_home_node
set_robust_list
set_thread_area
set_tid_address
sendmsg 114
sendto 133
set_mempolicy 431
+set_mempolicy_home_node 560
set_robust_list 466
set_thread_area
set_tid_address 411
sendmsg 211
sendto 206
set_mempolicy 237
+set_mempolicy_home_node 450
set_robust_list 99
set_thread_area
set_tid_address 96
sendmsg 296
sendto 290
set_mempolicy 321
+set_mempolicy_home_node 450
set_robust_list 338
set_thread_area
set_tid_address 256
sendmsg 211
sendto 206
set_mempolicy 237
+set_mempolicy_home_node 450
set_robust_list 99
set_thread_area
set_tid_address 96
sendmsg 370
sendto 369
set_mempolicy 276
+set_mempolicy_home_node 450
set_robust_list 311
set_thread_area 243
set_tid_address 258
sendmsg 1205
sendto 1199
set_mempolicy 1261
+set_mempolicy_home_node 1474
set_robust_list 1298
set_thread_area
set_tid_address 1233
sendmsg 211
sendto 206
set_mempolicy 237
+set_mempolicy_home_node 450
set_robust_list 99
set_thread_area
set_tid_address 96
sendmsg 367
sendto 366
set_mempolicy 270
+set_mempolicy_home_node 450
set_robust_list 304
set_thread_area 334
set_tid_address 253
sendmsg 5045
sendto 5043
set_mempolicy 5229
+set_mempolicy_home_node 5450
set_robust_list 5268
set_thread_area 5242
set_tid_address 5212
sendmsg 6045
sendto 6043
set_mempolicy 6233
+set_mempolicy_home_node 6450
set_robust_list 6272
set_thread_area 6246
set_tid_address 6213
sendmsg 4179
sendto 4180
set_mempolicy 4270
+set_mempolicy_home_node 4450
set_robust_list 4309
set_thread_area 4283
set_tid_address 4252
sendmsg 341
sendto 335
set_mempolicy 261
+set_mempolicy_home_node 450
set_robust_list 300
set_thread_area
set_tid_address 232
sendmsg 341
sendto 335
set_mempolicy 261
+set_mempolicy_home_node 450
set_robust_list 300
set_thread_area
set_tid_address 232
sendmsg 211
sendto 206
set_mempolicy 237
+set_mempolicy_home_node 450
set_robust_list 99
set_thread_area
set_tid_address 96
sendmsg 211
sendto 206
set_mempolicy 237
+set_mempolicy_home_node 450
set_robust_list 99
set_thread_area
set_tid_address 96
sendmsg 370
sendto 369
set_mempolicy 270
+set_mempolicy_home_node 450
set_robust_list 304
set_thread_area
set_tid_address 252
sendmsg 370
sendto 369
set_mempolicy 270
+set_mempolicy_home_node 450
set_robust_list 304
set_thread_area
set_tid_address 252
sendmsg 114
sendto 133
set_mempolicy 305
+set_mempolicy_home_node 450
set_robust_list 300
set_thread_area
set_tid_address 166
sendmsg 46
sendto 44
set_mempolicy 238
+set_mempolicy_home_node 450
set_robust_list 273
set_thread_area 205
set_tid_address 218
#include "alloc-util.h"
#include "def.h"
+#include "devnum-util.h"
#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "alloc-util.h"
#include "bootspec.h"
+#include "devnum-util.h"
#include "efi-api.h"
#include "efi-loader.h"
#include "efivars.h"
#include "parse-util.h"
#include "path-util.h"
#include "pretty-print.h"
-#include "stat-util.h"
#include "sync-util.h"
#include "terminal-util.h"
#include "util.h"
"Couldn't find $BOOT partition. It is recommended to mount it to /boot.\n"
"Alternatively, use --path= to specify path to mount point.");
- if (esp_path && xbootldr_path && !devid_set_and_equal(esp_devid, xbootldr_devid)) /* in case the two paths refer to the same inode, suppress one */
+ if (esp_path && xbootldr_path && !devnum_set_and_equal(esp_devid, xbootldr_devid)) /* in case the two paths refer to the same inode, suppress one */
a = strv_new(esp_path, xbootldr_path);
else if (esp_path)
a = strv_new(esp_path);
#include "blkid-util.h"
#include "bootspec.h"
#include "copy.h"
+#include "devnum-util.h"
#include "dirent-util.h"
#include "efi-api.h"
#include "efi-loader.h"
/* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would
* find the same entries twice. */
- bool same = esp_path && xbootldr_path && devid_set_and_equal(esp_devid, xbootldr_devid);
+ bool same = esp_path && xbootldr_path && devnum_set_and_equal(esp_devid, xbootldr_devid);
r = boot_config_load(config, esp_path, same ? NULL : xbootldr_path);
if (r < 0)
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
#include <efi.h>
#include "bpf-devices.h"
#include "bpf-program.h"
+#include "devnum-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "nulstr-util.h"
#include "parse-util.h"
#include "path-util.h"
-#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "cgroup-setup.h"
#include "cgroup-util.h"
#include "cgroup.h"
+#include "devnum-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "in-addr-prefix-util.h"
#include "procfs-util.h"
#include "restrict-ifaces.h"
#include "special.h"
-#include "stat-util.h"
#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
bfq_weight = BFQ_WEIGHT(io_weight);
if (major(dev) > 0)
- xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), bfq_weight);
+ xsprintf(buf, DEVNUM_FORMAT_STR " %" PRIu64 "\n", DEVNUM_FORMAT_VAL(dev), bfq_weight);
else
xsprintf(buf, "%" PRIu64 "\n", bfq_weight);
r1 = set_bfq_weight(u, "io", dev, io_weight);
- xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), io_weight);
+ xsprintf(buf, DEVNUM_FORMAT_STR " %" PRIu64 "\n", DEVNUM_FORMAT_VAL(dev), io_weight);
r2 = cg_set_attribute("io", u->cgroup_path, "io.weight", buf);
/* Look at the configured device, when both fail, prefer io.weight errno. */
if (r < 0)
return;
- xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), blkio_weight);
+ xsprintf(buf, DEVNUM_FORMAT_STR " %" PRIu64 "\n", DEVNUM_FORMAT_VAL(dev), blkio_weight);
(void) set_attribute_and_warn(u, "blkio", "blkio.weight_device", buf);
}
return;
if (target != USEC_INFINITY)
- xsprintf(buf, "%u:%u target=%" PRIu64 "\n", major(dev), minor(dev), target);
+ xsprintf(buf, DEVNUM_FORMAT_STR " target=%" PRIu64 "\n", DEVNUM_FORMAT_VAL(dev), target);
else
- xsprintf(buf, "%u:%u target=max\n", major(dev), minor(dev));
+ xsprintf(buf, DEVNUM_FORMAT_STR " target=max\n", DEVNUM_FORMAT_VAL(dev));
(void) set_attribute_and_warn(u, "io", "io.latency", buf);
}
else
xsprintf(limit_bufs[type], "%s", limits[type] == CGROUP_LIMIT_MAX ? "max" : "0");
- xsprintf(buf, "%u:%u rbps=%s wbps=%s riops=%s wiops=%s\n", major(dev), minor(dev),
+ xsprintf(buf, DEVNUM_FORMAT_STR " rbps=%s wbps=%s riops=%s wiops=%s\n", DEVNUM_FORMAT_VAL(dev),
limit_bufs[CGROUP_IO_RBPS_MAX], limit_bufs[CGROUP_IO_WBPS_MAX],
limit_bufs[CGROUP_IO_RIOPS_MAX], limit_bufs[CGROUP_IO_WIOPS_MAX]);
(void) set_attribute_and_warn(u, "io", "io.max", buf);
if (lookup_block_device(dev_path, &dev) < 0)
return;
- sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), rbps);
+ sprintf(buf, DEVNUM_FORMAT_STR " %" PRIu64 "\n", DEVNUM_FORMAT_VAL(dev), rbps);
(void) set_attribute_and_warn(u, "blkio", "blkio.throttle.read_bps_device", buf);
- sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), wbps);
+ sprintf(buf, DEVNUM_FORMAT_STR " %" PRIu64 "\n", DEVNUM_FORMAT_VAL(dev), wbps);
(void) set_attribute_and_warn(u, "blkio", "blkio.throttle.write_bps_device", buf);
}
SD_BUS_PROPERTY("DefaultOOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CtrlAltDelBurstAction", "s", bus_property_get_emergency_action, offsetof(Manager, cad_burst_action), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_METHOD_WITH_NAMES("GetUnit",
- "s",
- SD_BUS_PARAM(name),
- "o",
- SD_BUS_PARAM(unit),
- method_get_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetUnitByPID",
- "u",
- SD_BUS_PARAM(pid),
- "o",
- SD_BUS_PARAM(unit),
- method_get_unit_by_pid,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetUnitByInvocationID",
- "ay",
- SD_BUS_PARAM(invocation_id),
- "o",
- SD_BUS_PARAM(unit),
- method_get_unit_by_invocation_id,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetUnitByControlGroup",
- "s",
- SD_BUS_PARAM(cgroup),
- "o",
- SD_BUS_PARAM(unit),
- method_get_unit_by_control_group,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("LoadUnit",
- "s",
- SD_BUS_PARAM(name),
- "o",
- SD_BUS_PARAM(unit),
- method_load_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("StartUnit",
- "ss",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(mode),
- "o",
- SD_BUS_PARAM(job),
- method_start_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("StartUnitWithFlags",
- "sst",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(mode)
- SD_BUS_PARAM(flags),
- "o",
- SD_BUS_PARAM(job),
- method_start_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("StartUnitReplace",
- "sss",
- SD_BUS_PARAM(old_unit)
- SD_BUS_PARAM(new_unit)
- SD_BUS_PARAM(mode),
- "o",
- SD_BUS_PARAM(job),
- method_start_unit_replace,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("StopUnit",
- "ss",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(mode),
- "o",
- SD_BUS_PARAM(job),
- method_stop_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ReloadUnit",
- "ss",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(mode),
- "o",
- SD_BUS_PARAM(job),
- method_reload_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("RestartUnit",
- "ss",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(mode),
- "o",
- SD_BUS_PARAM(job),
- method_restart_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("TryRestartUnit",
- "ss",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(mode),
- "o",
- SD_BUS_PARAM(job),
- method_try_restart_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ReloadOrRestartUnit",
- "ss",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(mode),
- "o",
- SD_BUS_PARAM(job),
- method_reload_or_restart_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ReloadOrTryRestartUnit",
- "ss",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(mode),
- "o",
- SD_BUS_PARAM(job),
- method_reload_or_try_restart_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("EnqueueUnitJob",
- "sss",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(job_type)
- SD_BUS_PARAM(job_mode),
- "uososa(uosos)",
- SD_BUS_PARAM(job_id)
- SD_BUS_PARAM(job_path)
- SD_BUS_PARAM(unit_id)
- SD_BUS_PARAM(unit_path)
- SD_BUS_PARAM(job_type)
- SD_BUS_PARAM(affected_jobs),
- method_enqueue_unit_job,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("KillUnit",
- "ssi",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(whom)
- SD_BUS_PARAM(signal),
- NULL,,
- method_kill_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("CleanUnit",
- "sas",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(mask),
- NULL,,
- method_clean_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("FreezeUnit",
- "s",
- SD_BUS_PARAM(name),
- NULL,,
- method_freeze_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ThawUnit",
- "s",
- SD_BUS_PARAM(name),
- NULL,,
- method_thaw_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ResetFailedUnit",
- "s",
- SD_BUS_PARAM(name),
- NULL,,
- method_reset_failed_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetUnitProperties",
- "sba(sv)",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(runtime)
- SD_BUS_PARAM(properties),
- NULL,,
- method_set_unit_properties,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("BindMountUnit",
- "sssbb",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(source)
- SD_BUS_PARAM(destination)
- SD_BUS_PARAM(read_only)
- SD_BUS_PARAM(mkdir),
- NULL,,
- method_bind_mount_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("MountImageUnit",
- "sssbba(ss)",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(source)
- SD_BUS_PARAM(destination)
- SD_BUS_PARAM(read_only)
- SD_BUS_PARAM(mkdir)
- SD_BUS_PARAM(options),
- NULL,,
- method_mount_image_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("RefUnit",
- "s",
- SD_BUS_PARAM(name),
- NULL,,
- method_ref_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("UnrefUnit",
- "s",
- SD_BUS_PARAM(name),
- NULL,,
- method_unref_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("StartTransientUnit",
- "ssa(sv)a(sa(sv))",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(mode)
- SD_BUS_PARAM(properties)
- SD_BUS_PARAM(aux),
- "o",
- SD_BUS_PARAM(job),
- method_start_transient_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetUnitProcesses",
- "s",
- SD_BUS_PARAM(name),
- "a(sus)",
- SD_BUS_PARAM(processes),
- method_get_unit_processes,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("AttachProcessesToUnit",
- "ssau",
- SD_BUS_PARAM(unit_name)
- SD_BUS_PARAM(subcgroup)
- SD_BUS_PARAM(pids),
- NULL,,
- method_attach_processes_to_unit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("AbandonScope",
- "s",
- SD_BUS_PARAM(name),
- NULL,,
- method_abandon_scope,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetJob",
- "u",
- SD_BUS_PARAM(id),
- "o",
- SD_BUS_PARAM(job),
- method_get_job,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetJobAfter",
- "u",
- SD_BUS_PARAM(id),
- "a(usssoo)",
- SD_BUS_PARAM(jobs),
- method_get_job_waiting,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetJobBefore",
- "u",
- SD_BUS_PARAM(id),
- "a(usssoo)",
- SD_BUS_PARAM(jobs),
- method_get_job_waiting,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("CancelJob",
- "u",
- SD_BUS_PARAM(id),
- NULL,,
- method_cancel_job,
- SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetUnit",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("o", unit),
+ method_get_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetUnitByPID",
+ SD_BUS_ARGS("u", pid),
+ SD_BUS_RESULT("o", unit),
+ method_get_unit_by_pid,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetUnitByInvocationID",
+ SD_BUS_ARGS("ay", invocation_id),
+ SD_BUS_RESULT("o", unit),
+ method_get_unit_by_invocation_id,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetUnitByControlGroup",
+ SD_BUS_ARGS("s", cgroup),
+ SD_BUS_RESULT("o", unit),
+ method_get_unit_by_control_group,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("LoadUnit",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("o", unit),
+ method_load_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("StartUnit",
+ SD_BUS_ARGS("s", name, "s", mode),
+ SD_BUS_RESULT("o", job),
+ method_start_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("StartUnitWithFlags",
+ SD_BUS_ARGS("s", name, "s", mode, "t", flags),
+ SD_BUS_RESULT("o", job),
+ method_start_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("StartUnitReplace",
+ SD_BUS_ARGS("s", old_unit, "s", new_unit, "s", mode),
+ SD_BUS_RESULT("o", job),
+ method_start_unit_replace,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("StopUnit",
+ SD_BUS_ARGS("s", name, "s", mode),
+ SD_BUS_RESULT("o", job),
+ method_stop_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ReloadUnit",
+ SD_BUS_ARGS("s", name, "s", mode),
+ SD_BUS_RESULT("o", job),
+ method_reload_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("RestartUnit",
+ SD_BUS_ARGS("s", name, "s", mode),
+ SD_BUS_RESULT("o", job),
+ method_restart_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("TryRestartUnit",
+ SD_BUS_ARGS("s", name, "s", mode),
+ SD_BUS_RESULT("o", job),
+ method_try_restart_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ReloadOrRestartUnit",
+ SD_BUS_ARGS("s", name, "s", mode),
+ SD_BUS_RESULT("o", job),
+ method_reload_or_restart_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ReloadOrTryRestartUnit",
+ SD_BUS_ARGS("s", name, "s", mode),
+ SD_BUS_RESULT("o", job),
+ method_reload_or_try_restart_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("EnqueueUnitJob",
+ SD_BUS_ARGS("s", name, "s", job_type, "s", job_mode),
+ SD_BUS_RESULT("u", job_id, "o", job_path, "s", unit_id, "o", unit_path, "s", job_type, "a(uosos)", affected_jobs),
+ method_enqueue_unit_job,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("KillUnit",
+ SD_BUS_ARGS("s", name, "s", whom, "i", signal),
+ SD_BUS_NO_RESULT,
+ method_kill_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("CleanUnit",
+ SD_BUS_ARGS("s", name, "as", mask),
+ SD_BUS_NO_RESULT,
+ method_clean_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("FreezeUnit",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_NO_RESULT,
+ method_freeze_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ThawUnit",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_NO_RESULT,
+ method_thaw_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ResetFailedUnit",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_NO_RESULT,
+ method_reset_failed_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetUnitProperties",
+ SD_BUS_ARGS("s", name, "b", runtime, "a(sv)", properties),
+ SD_BUS_NO_RESULT,
+ method_set_unit_properties,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("BindMountUnit",
+ SD_BUS_ARGS("s", name, "s", source, "s", destination, "b", read_only, "b", mkdir),
+ SD_BUS_NO_RESULT,
+ method_bind_mount_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("MountImageUnit",
+ SD_BUS_ARGS("s", name, "s", source, "s", destination, "b", read_only, "b", mkdir, "a(ss)", options),
+ SD_BUS_NO_RESULT,
+ method_mount_image_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("RefUnit",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_NO_RESULT,
+ method_ref_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("UnrefUnit",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_NO_RESULT,
+ method_unref_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("StartTransientUnit",
+ SD_BUS_ARGS("s", name, "s", mode, "a(sv)", properties, "a(sa(sv))", aux),
+ SD_BUS_RESULT("o", job),
+ method_start_transient_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetUnitProcesses",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("a(sus)", processes),
+ method_get_unit_processes,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("AttachProcessesToUnit",
+ SD_BUS_ARGS("s", unit_name, "s", subcgroup, "au", pids),
+ SD_BUS_NO_RESULT,
+ method_attach_processes_to_unit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("AbandonScope",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_NO_RESULT,
+ method_abandon_scope,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetJob",
+ SD_BUS_ARGS("u", id),
+ SD_BUS_RESULT("o", job),
+ method_get_job,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetJobAfter",
+ SD_BUS_ARGS("u", id),
+ SD_BUS_RESULT("a(usssoo)", jobs),
+ method_get_job_waiting,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetJobBefore",
+ SD_BUS_ARGS("u", id),
+ SD_BUS_RESULT("a(usssoo)", jobs),
+ method_get_job_waiting,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("CancelJob",
+ SD_BUS_ARGS("u", id),
+ SD_BUS_NO_RESULT,
+ method_cancel_job,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ClearJobs",
NULL,
NULL,
NULL,
method_reset_failed,
SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetShowStatus",
- "s",
- SD_BUS_PARAM(mode),
- NULL,,
- method_set_show_status,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ListUnits",
- NULL,,
- "a(ssssssouso)",
- SD_BUS_PARAM(units),
- method_list_units,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ListUnitsFiltered",
- "as",
- SD_BUS_PARAM(states),
- "a(ssssssouso)",
- SD_BUS_PARAM(units),
- method_list_units_filtered,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ListUnitsByPatterns",
- "asas",
- SD_BUS_PARAM(states)
- SD_BUS_PARAM(patterns),
- "a(ssssssouso)",
- SD_BUS_PARAM(units),
- method_list_units_by_patterns,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ListUnitsByNames",
- "as",
- SD_BUS_PARAM(names),
- "a(ssssssouso)",
- SD_BUS_PARAM(units),
- method_list_units_by_names,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ListJobs",
- NULL,,
- "a(usssoo)",
- SD_BUS_PARAM(jobs),
- method_list_jobs,
- SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetShowStatus",
+ SD_BUS_ARGS("s", mode),
+ SD_BUS_NO_RESULT,
+ method_set_show_status,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ListUnits",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("a(ssssssouso)", units),
+ method_list_units,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ListUnitsFiltered",
+ SD_BUS_ARGS("as", states),
+ SD_BUS_RESULT("a(ssssssouso)", units),
+ method_list_units_filtered,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ListUnitsByPatterns",
+ SD_BUS_ARGS("as", states, "as", patterns),
+ SD_BUS_RESULT("a(ssssssouso)", units),
+ method_list_units_by_patterns,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ListUnitsByNames",
+ SD_BUS_ARGS("as", names),
+ SD_BUS_RESULT("a(ssssssouso)", units),
+ method_list_units_by_names,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ListJobs",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("a(usssoo)", jobs),
+ method_list_jobs,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Subscribe",
NULL,
NULL,
NULL,
method_unsubscribe,
SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("Dump",
- NULL,,
- "s",
- SD_BUS_PARAM(output),
- method_dump,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("DumpByFileDescriptor",
- NULL,,
- "h",
- SD_BUS_PARAM(fd),
- method_dump_by_fd,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("CreateSnapshot",
- "sb",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(cleanup),
- "o",
- SD_BUS_PARAM(unit),
- method_refuse_snapshot,
- SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_HIDDEN),
- SD_BUS_METHOD_WITH_NAMES("RemoveSnapshot",
- "s",
- SD_BUS_PARAM(name),
- NULL,,
- method_refuse_snapshot,
- SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_METHOD_WITH_ARGS("Dump",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("s", output),
+ method_dump,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("DumpByFileDescriptor",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("h", fd),
+ method_dump_by_fd,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("CreateSnapshot",
+ SD_BUS_ARGS("s", name, "b", cleanup),
+ SD_BUS_RESULT("o", unit),
+ method_refuse_snapshot,
+ SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_HIDDEN),
+ SD_BUS_METHOD_WITH_ARGS("RemoveSnapshot",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_NO_RESULT,
+ method_refuse_snapshot,
+ SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_HIDDEN),
SD_BUS_METHOD("Reload",
NULL,
NULL,
NULL,
method_kexec,
SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
- SD_BUS_METHOD_WITH_NAMES("SwitchRoot",
- "ss",
- SD_BUS_PARAM(new_root)
- SD_BUS_PARAM(init),
- NULL,,
- method_switch_root,
- SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
- SD_BUS_METHOD_WITH_NAMES("SetEnvironment",
- "as",
- SD_BUS_PARAM(assignments),
- NULL,,
- method_set_environment,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("UnsetEnvironment",
- "as",
- SD_BUS_PARAM(names),
- NULL,,
- method_unset_environment,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("UnsetAndSetEnvironment",
- "asas",
- SD_BUS_PARAM(names)
- SD_BUS_PARAM(assignments),
- NULL,,
- method_unset_and_set_environment,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("EnqueueMarkedJobs",
- NULL,,
- "ao",
- SD_BUS_PARAM(jobs),
- method_enqueue_marked_jobs,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ListUnitFiles",
- NULL,,
- "a(ss)",
- SD_BUS_PARAM(unit_files),
- method_list_unit_files,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ListUnitFilesByPatterns",
- "asas",
- SD_BUS_PARAM(states)
- SD_BUS_PARAM(patterns),
- "a(ss)",
- SD_BUS_PARAM(unit_files),
- method_list_unit_files_by_patterns,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetUnitFileState",
- "s",
- SD_BUS_PARAM(file),
- "s",
- SD_BUS_PARAM(state),
- method_get_unit_file_state,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("EnableUnitFiles",
- "asbb",
- SD_BUS_PARAM(files)
- SD_BUS_PARAM(runtime)
- SD_BUS_PARAM(force),
- "ba(sss)",
- SD_BUS_PARAM(carries_install_info)
- SD_BUS_PARAM(changes),
- method_enable_unit_files,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("DisableUnitFiles",
- "asb",
- SD_BUS_PARAM(files)
- SD_BUS_PARAM(runtime),
- "a(sss)",
- SD_BUS_PARAM(changes),
- method_disable_unit_files,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("EnableUnitFilesWithFlags",
- "ast",
- SD_BUS_PARAM(files)
- SD_BUS_PARAM(flags),
- "ba(sss)",
- SD_BUS_PARAM(carries_install_info)
- SD_BUS_PARAM(changes),
- method_enable_unit_files_with_flags,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("DisableUnitFilesWithFlags",
- "ast",
- SD_BUS_PARAM(files)
- SD_BUS_PARAM(flags),
- "a(sss)",
- SD_BUS_PARAM(changes),
- method_disable_unit_files_with_flags,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ReenableUnitFiles",
- "asbb",
- SD_BUS_PARAM(files)
- SD_BUS_PARAM(runtime)
- SD_BUS_PARAM(force),
- "ba(sss)",
- SD_BUS_PARAM(carries_install_info)
- SD_BUS_PARAM(changes),
- method_reenable_unit_files,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("LinkUnitFiles",
- "asbb",
- SD_BUS_PARAM(files)
- SD_BUS_PARAM(runtime)
- SD_BUS_PARAM(force),
- "a(sss)",
- SD_BUS_PARAM(changes),
- method_link_unit_files,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("PresetUnitFiles",
- "asbb",
- SD_BUS_PARAM(files)
- SD_BUS_PARAM(runtime)
- SD_BUS_PARAM(force),
- "ba(sss)",
- SD_BUS_PARAM(carries_install_info)
- SD_BUS_PARAM(changes),
- method_preset_unit_files,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("PresetUnitFilesWithMode",
- "assbb",
- SD_BUS_PARAM(files)
- SD_BUS_PARAM(mode)
- SD_BUS_PARAM(runtime)
- SD_BUS_PARAM(force),
- "ba(sss)",
- SD_BUS_PARAM(carries_install_info)
- SD_BUS_PARAM(changes),
- method_preset_unit_files_with_mode,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("MaskUnitFiles",
- "asbb",
- SD_BUS_PARAM(files)
- SD_BUS_PARAM(runtime)
- SD_BUS_PARAM(force),
- "a(sss)",
- SD_BUS_PARAM(changes),
- method_mask_unit_files,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("UnmaskUnitFiles",
- "asb",
- SD_BUS_PARAM(files)
- SD_BUS_PARAM(runtime),
- "a(sss)",
- SD_BUS_PARAM(changes),
- method_unmask_unit_files,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("RevertUnitFiles",
- "as",
- SD_BUS_PARAM(files),
- "a(sss)",
- SD_BUS_PARAM(changes),
- method_revert_unit_files,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetDefaultTarget",
- "sb",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(force),
- "a(sss)",
- SD_BUS_PARAM(changes),
- method_set_default_target,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetDefaultTarget",
- NULL,,
- "s",
- SD_BUS_PARAM(name),
- method_get_default_target,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("PresetAllUnitFiles",
- "sbb",
- SD_BUS_PARAM(mode)
- SD_BUS_PARAM(runtime)
- SD_BUS_PARAM(force),
- "a(sss)",
- SD_BUS_PARAM(changes),
- method_preset_all_unit_files,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("AddDependencyUnitFiles",
- "asssbb",
- SD_BUS_PARAM(files)
- SD_BUS_PARAM(target)
- SD_BUS_PARAM(type)
- SD_BUS_PARAM(runtime)
- SD_BUS_PARAM(force),
- "a(sss)",
- SD_BUS_PARAM(changes),
- method_add_dependency_unit_files,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetUnitFileLinks",
- "sb",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(runtime),
- "as",
- SD_BUS_PARAM(links),
- method_get_unit_file_links,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetExitCode",
- "y",
- SD_BUS_PARAM(number),
- NULL,,
- method_set_exit_code,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("LookupDynamicUserByName",
- "s",
- SD_BUS_PARAM(name),
- "u",
- SD_BUS_PARAM(uid),
- method_lookup_dynamic_user_by_name,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("LookupDynamicUserByUID",
- "u",
- SD_BUS_PARAM(uid),
- "s",
- SD_BUS_PARAM(name),
- method_lookup_dynamic_user_by_uid,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetDynamicUsers",
- NULL,,
- "a(us)",
- SD_BUS_PARAM(users),
- method_get_dynamic_users,
- SD_BUS_VTABLE_UNPRIVILEGED),
-
- SD_BUS_SIGNAL_WITH_NAMES("UnitNew",
- "so",
- SD_BUS_PARAM(id)
- SD_BUS_PARAM(unit),
- 0),
- SD_BUS_SIGNAL_WITH_NAMES("UnitRemoved",
- "so",
- SD_BUS_PARAM(id)
- SD_BUS_PARAM(unit),
- 0),
- SD_BUS_SIGNAL_WITH_NAMES("JobNew",
- "uos",
- SD_BUS_PARAM(id)
- SD_BUS_PARAM(job)
- SD_BUS_PARAM(unit),
- 0),
- SD_BUS_SIGNAL_WITH_NAMES("JobRemoved",
- "uoss",
- SD_BUS_PARAM(id)
- SD_BUS_PARAM(job)
- SD_BUS_PARAM(unit)
- SD_BUS_PARAM(result),
- 0),
- SD_BUS_SIGNAL_WITH_NAMES("StartupFinished",
- "tttttt",
- SD_BUS_PARAM(firmware)
- SD_BUS_PARAM(loader)
- SD_BUS_PARAM(kernel)
- SD_BUS_PARAM(initrd)
- SD_BUS_PARAM(userspace)
- SD_BUS_PARAM(total),
- 0),
+ SD_BUS_METHOD_WITH_ARGS("SwitchRoot",
+ SD_BUS_ARGS("s", new_root, "s", init),
+ SD_BUS_NO_RESULT,
+ method_switch_root,
+ SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
+ SD_BUS_METHOD_WITH_ARGS("SetEnvironment",
+ SD_BUS_ARGS("as", assignments),
+ SD_BUS_NO_RESULT,
+ method_set_environment,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("UnsetEnvironment",
+ SD_BUS_ARGS("as", names),
+ SD_BUS_NO_RESULT,
+ method_unset_environment,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("UnsetAndSetEnvironment",
+ SD_BUS_ARGS("as", names, "as", assignments),
+ SD_BUS_NO_RESULT,
+ method_unset_and_set_environment,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("EnqueueMarkedJobs",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("ao", jobs),
+ method_enqueue_marked_jobs,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ListUnitFiles",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("a(ss)", unit_files),
+ method_list_unit_files,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ListUnitFilesByPatterns",
+ SD_BUS_ARGS("as", states, "as", patterns),
+ SD_BUS_RESULT("a(ss)", unit_files),
+ method_list_unit_files_by_patterns,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetUnitFileState",
+ SD_BUS_ARGS("s", file),
+ SD_BUS_RESULT("s", state),
+ method_get_unit_file_state,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("EnableUnitFiles",
+ SD_BUS_ARGS("as", files, "b", runtime, "b", force),
+ SD_BUS_RESULT("b", carries_install_info, "a(sss)", changes),
+ method_enable_unit_files,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("DisableUnitFiles",
+ SD_BUS_ARGS("as", files, "b", runtime),
+ SD_BUS_RESULT("a(sss)", changes),
+ method_disable_unit_files,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("EnableUnitFilesWithFlags",
+ SD_BUS_ARGS("as", files, "t", flags),
+ SD_BUS_RESULT("b", carries_install_info, "a(sss)", changes),
+ method_enable_unit_files_with_flags,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("DisableUnitFilesWithFlags",
+ SD_BUS_ARGS("as", files, "t", flags),
+ SD_BUS_RESULT("a(sss)", changes),
+ method_disable_unit_files_with_flags,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ReenableUnitFiles",
+ SD_BUS_ARGS("as", files, "b", runtime, "b", force),
+ SD_BUS_RESULT("b", carries_install_info, "a(sss)", changes),
+ method_reenable_unit_files,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("LinkUnitFiles",
+ SD_BUS_ARGS("as", files, "b", runtime, "b", force),
+ SD_BUS_RESULT("a(sss)", changes),
+ method_link_unit_files,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("PresetUnitFiles",
+ SD_BUS_ARGS("as", files, "b", runtime, "b", force),
+ SD_BUS_RESULT("b", carries_install_info, "a(sss)", changes),
+ method_preset_unit_files,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("PresetUnitFilesWithMode",
+ SD_BUS_ARGS("as", files, "s", mode, "b", runtime, "b", force),
+ SD_BUS_RESULT("b", carries_install_info, "a(sss)", changes),
+ method_preset_unit_files_with_mode,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("MaskUnitFiles",
+ SD_BUS_ARGS("as", files, "b", runtime, "b", force),
+ SD_BUS_RESULT("a(sss)", changes),
+ method_mask_unit_files,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("UnmaskUnitFiles",
+ SD_BUS_ARGS("as", files, "b", runtime),
+ SD_BUS_RESULT("a(sss)", changes),
+ method_unmask_unit_files,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("RevertUnitFiles",
+ SD_BUS_ARGS("as", files),
+ SD_BUS_RESULT("a(sss)", changes),
+ method_revert_unit_files,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetDefaultTarget",
+ SD_BUS_ARGS("s", name, "b", force),
+ SD_BUS_RESULT("a(sss)", changes),
+ method_set_default_target,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetDefaultTarget",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("s", name),
+ method_get_default_target,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("PresetAllUnitFiles",
+ SD_BUS_ARGS("s", mode, "b", runtime, "b", force),
+ SD_BUS_RESULT("a(sss)", changes),
+ method_preset_all_unit_files,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("AddDependencyUnitFiles",
+ SD_BUS_ARGS("as", files, "s", target, "s", type, "b", runtime, "b", force),
+ SD_BUS_RESULT("a(sss)", changes),
+ method_add_dependency_unit_files,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetUnitFileLinks",
+ SD_BUS_ARGS("s", name, "b", runtime),
+ SD_BUS_RESULT("as", links),
+ method_get_unit_file_links,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetExitCode",
+ SD_BUS_ARGS("y", number),
+ SD_BUS_NO_RESULT,
+ method_set_exit_code,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("LookupDynamicUserByName",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("u", uid),
+ method_lookup_dynamic_user_by_name,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("LookupDynamicUserByUID",
+ SD_BUS_ARGS("u", uid),
+ SD_BUS_RESULT("s", name),
+ method_lookup_dynamic_user_by_uid,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetDynamicUsers",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("a(us)", users),
+ method_get_dynamic_users,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+
+ SD_BUS_SIGNAL_WITH_ARGS("UnitNew",
+ SD_BUS_ARGS("s", id, "o", unit),
+ 0),
+ SD_BUS_SIGNAL_WITH_ARGS("UnitRemoved",
+ SD_BUS_ARGS("s", id, "o", unit),
+ 0),
+ SD_BUS_SIGNAL_WITH_ARGS("JobNew",
+ SD_BUS_ARGS("u", id, "o", job, "s", unit),
+ 0),
+ SD_BUS_SIGNAL_WITH_ARGS("JobRemoved",
+ SD_BUS_ARGS("u", id, "o", job, "s", unit, "s", result),
+ 0),
+ SD_BUS_SIGNAL_WITH_ARGS("StartupFinished",
+ SD_BUS_ARGS("t", firmware, "t", loader, "t", kernel, "t", initrd, "t", userspace, "t", total),
+ 0),
SD_BUS_SIGNAL("UnitFilesChanged", NULL, 0),
- SD_BUS_SIGNAL_WITH_NAMES("Reloading",
- "b",
- SD_BUS_PARAM(active),
- 0),
+ SD_BUS_SIGNAL_WITH_ARGS("Reloading",
+ SD_BUS_ARGS("b", active),
+ 0),
SD_BUS_VTABLE_END
};
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecStopPostEx", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
- SD_BUS_METHOD_WITH_NAMES("BindMount",
- "ssbb",
- SD_BUS_PARAM(source)
- SD_BUS_PARAM(destination)
- SD_BUS_PARAM(read_only)
- SD_BUS_PARAM(mkdir),
- NULL,,
- bus_service_method_bind_mount,
- SD_BUS_VTABLE_UNPRIVILEGED),
-
- SD_BUS_METHOD_WITH_NAMES("MountImage",
- "ssbba(ss)",
- SD_BUS_PARAM(source)
- SD_BUS_PARAM(destination)
- SD_BUS_PARAM(read_only)
- SD_BUS_PARAM(mkdir)
- SD_BUS_PARAM(options),
- NULL,,
+ SD_BUS_METHOD_WITH_ARGS("BindMount",
+ SD_BUS_ARGS("s", source, "s", destination, "b", read_only, "b", mkdir),
+ SD_BUS_NO_RESULT,
+ bus_service_method_bind_mount,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+
+ SD_BUS_METHOD_WITH_ARGS("MountImage",
+ SD_BUS_ARGS("s", source, "s", destination, "b", read_only, "b", mkdir, "a(ss)", options),
+ SD_BUS_NO_RESULT,
bus_service_method_mount_image,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_PROPERTY("CollectMode", "s", property_get_collect_mode, offsetof(Unit, collect_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Refs", "as", property_get_refs, 0, 0),
- SD_BUS_METHOD_WITH_NAMES("Start",
- "s",
- SD_BUS_PARAM(mode),
- "o",
- SD_BUS_PARAM(job),
- bus_unit_method_start,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("Stop",
- "s",
- SD_BUS_PARAM(mode),
- "o",
- SD_BUS_PARAM(job),
- bus_unit_method_stop,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("Reload",
- "s",
- SD_BUS_PARAM(mode),
- "o",
- SD_BUS_PARAM(job),
- bus_unit_method_reload,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("Restart",
- "s",
- SD_BUS_PARAM(mode),
- "o",
- SD_BUS_PARAM(job),
- bus_unit_method_restart,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("TryRestart",
- "s",
- SD_BUS_PARAM(mode),
- "o",
- SD_BUS_PARAM(job),
- bus_unit_method_try_restart,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ReloadOrRestart",
- "s",
- SD_BUS_PARAM(mode),
- "o",
- SD_BUS_PARAM(job),
- bus_unit_method_reload_or_restart,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ReloadOrTryRestart",
- "s",
- SD_BUS_PARAM(mode),
- "o",
- SD_BUS_PARAM(job),
- bus_unit_method_reload_or_try_restart,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("EnqueueJob",
- "ss",
- SD_BUS_PARAM(job_type)
- SD_BUS_PARAM(job_mode),
- "uososa(uosos)",
- SD_BUS_PARAM(job_id)
- SD_BUS_PARAM(job_path)
- SD_BUS_PARAM(unit_id)
- SD_BUS_PARAM(unit_path)
- SD_BUS_PARAM(job_type)
- SD_BUS_PARAM(affected_jobs),
- bus_unit_method_enqueue_job,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("Kill",
- "si",
- SD_BUS_PARAM(whom)
- SD_BUS_PARAM(signal),
- NULL,,
- bus_unit_method_kill,
- SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("Start",
+ SD_BUS_ARGS("s", mode),
+ SD_BUS_RESULT("o", job),
+ bus_unit_method_start,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("Stop",
+ SD_BUS_ARGS("s", mode),
+ SD_BUS_RESULT("o", job),
+ bus_unit_method_stop,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("Reload",
+ SD_BUS_ARGS("s", mode),
+ SD_BUS_RESULT("o", job),
+ bus_unit_method_reload,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("Restart",
+ SD_BUS_ARGS("s", mode),
+ SD_BUS_RESULT("o", job),
+ bus_unit_method_restart,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("TryRestart",
+ SD_BUS_ARGS("s", mode),
+ SD_BUS_RESULT("o", job),
+ bus_unit_method_try_restart,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ReloadOrRestart",
+ SD_BUS_ARGS("s", mode),
+ SD_BUS_RESULT("o", job),
+ bus_unit_method_reload_or_restart,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ReloadOrTryRestart",
+ SD_BUS_ARGS("s", mode),
+ SD_BUS_RESULT("o", job),
+ bus_unit_method_reload_or_try_restart,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("EnqueueJob",
+ SD_BUS_ARGS("s", job_type, "s", job_mode),
+ SD_BUS_RESULT("u", job_id, "o", job_path, "s", unit_id, "o", unit_path, "s", job_type, "a(uosos)", affected_jobs),
+ bus_unit_method_enqueue_job,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("Kill",
+ SD_BUS_ARGS("s", whom, "i", signal),
+ SD_BUS_NO_RESULT,
+ bus_unit_method_kill,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetFailed",
NULL,
NULL,
bus_unit_method_reset_failed,
SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetProperties",
- "ba(sv)",
- SD_BUS_PARAM(runtime)
- SD_BUS_PARAM(properties),
- NULL,,
- bus_unit_method_set_properties,
- SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetProperties",
+ SD_BUS_ARGS("b", runtime, "a(sv)", properties),
+ SD_BUS_NO_RESULT,
+ bus_unit_method_set_properties,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Ref",
NULL,
NULL,
NULL,
bus_unit_method_unref,
SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("Clean",
- "as",
- SD_BUS_PARAM(mask),
- NULL,,
- bus_unit_method_clean,
- SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("Clean",
+ SD_BUS_ARGS("as", mask),
+ SD_BUS_NO_RESULT,
+ bus_unit_method_clean,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Freeze",
NULL,
NULL,
SD_BUS_PROPERTY("IOWriteBytes", "t", property_get_io_counter, 0, 0),
SD_BUS_PROPERTY("IOWriteOperations", "t", property_get_io_counter, 0, 0),
- SD_BUS_METHOD_WITH_NAMES("GetProcesses",
- NULL,,
- "a(sus)",
- SD_BUS_PARAM(processes),
+ SD_BUS_METHOD_WITH_ARGS("GetProcesses",
+ SD_BUS_NO_ARGS,
+ SD_BUS_ARGS("a(sus)", processes),
bus_unit_method_get_processes,
SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("AttachProcesses",
- "sau",
- SD_BUS_PARAM(subcgroup)
- SD_BUS_PARAM(pids),
- NULL,,
+ SD_BUS_METHOD_WITH_ARGS("AttachProcesses",
+ SD_BUS_ARGS("s", subcgroup, "au", pids),
+ SD_BUS_NO_RESULT,
bus_unit_method_attach_processes,
SD_BUS_VTABLE_UNPRIVILEGED),
#include "missing_ioprio.h"
#include "mountpoint-util.h"
#include "nulstr-util.h"
-#include "parse-socket-bind-item.h"
+#include "parse-helpers.h"
#include "parse-util.h"
#include "path-util.h"
#include "percent-util.h"
int locale_setup(char ***environment) {
_cleanup_(locale_variables_freep) char *variables[_VARIABLE_LC_MAX] = {};
_cleanup_strv_free_ char **add = NULL;
- LocaleVariable i;
int r;
r = proc_cmdline_get_key_many(PROC_CMDLINE_STRIP_RD_PREFIX,
log_warning_errno(r, "Failed to read /etc/locale.conf: %m");
}
- for (i = 0; i < _VARIABLE_LC_MAX; i++) {
+ for (LocaleVariable i = 0; i < _VARIABLE_LC_MAX; i++) {
char *s;
if (!variables[i])
}
}
- assert(uname(&uts) >= 0);
+ assert_se(uname(&uts) >= 0);
if (strverscmp_improved(uts.release, KERNEL_BASELINE_VERSION) < 0)
log_warning("Warning! Reported kernel version %s is older than systemd's required baseline kernel version %s. "
return r;
}
+static int build_generator_environment(Manager *m, char ***ret) {
+ _cleanup_strv_free_ char **nl = NULL;
+ Virtualization v;
+ int r;
+
+ assert(m);
+ assert(ret);
+
+ /* Generators oftentimes want to know some basic facts about the environment they run in, in order to
+ * adjust generated units to that. Let's pass down some bits of information that are easy for us to
+ * determine (but a bit harder for generator scripts to determine), as environment variables. */
+
+ nl = strv_copy(m->transient_environment);
+ if (!nl)
+ return -ENOMEM;
+
+ r = strv_env_assign(&nl, "SYSTEMD_SCOPE", MANAGER_IS_SYSTEM(m) ? "system" : "user");
+ if (r < 0)
+ return r;
+
+ if (MANAGER_IS_SYSTEM(m)) {
+ /* Note that $SYSTEMD_IN_INITRD may be used to override the initrd detection in much of our
+ * codebase. This is hence more than purely informational. It will shortcut detection of the
+ * initrd state if generators invoke our own tools. But that's OK, as it would come to the
+ * same results (hopefully). */
+ r = strv_env_assign(&nl, "SYSTEMD_IN_INITRD", one_zero(in_initrd()));
+ if (r < 0)
+ return r;
+
+ if (m->first_boot >= 0) {
+ r = strv_env_assign(&nl, "SYSTEMD_FIRST_BOOT", one_zero(m->first_boot));
+ if (r < 0)
+ return r;
+ }
+ }
+
+ v = detect_virtualization();
+ if (v < 0)
+ log_debug_errno(v, "Failed to detect virtualization, ignoring: %m");
+ else if (v > 0) {
+ const char *s;
+
+ s = strjoina(VIRTUALIZATION_IS_VM(v) ? "vm:" :
+ VIRTUALIZATION_IS_CONTAINER(v) ? "container:" : ":",
+ virtualization_to_string(v));
+
+ r = strv_env_assign(&nl, "SYSTEMD_VIRTUALIZATION", s);
+ if (r < 0)
+ return r;
+ }
+
+ r = strv_env_assign(&nl, "SYSTEMD_ARCHITECTURE", architecture_to_string(uname_architecture()));
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(nl);
+ return 0;
+}
+
static int manager_run_generators(Manager *m) {
- _cleanup_strv_free_ char **paths = NULL;
- const char *argv[5];
+ _cleanup_strv_free_ char **paths = NULL, **ge = NULL;
int r;
assert(m);
goto finish;
}
- argv[0] = NULL; /* Leave this empty, execute_directory() will fill something in */
- argv[1] = m->lookup_paths.generator;
- argv[2] = m->lookup_paths.generator_early;
- argv[3] = m->lookup_paths.generator_late;
- argv[4] = NULL;
+ const char *argv[] = {
+ NULL, /* Leave this empty, execute_directory() will fill something in */
+ m->lookup_paths.generator,
+ m->lookup_paths.generator_early,
+ m->lookup_paths.generator_late,
+ NULL,
+ };
+
+ r = build_generator_environment(m, &ge);
+ if (r < 0) {
+ log_error_errno(r, "Failed to build generator environment: %m");
+ goto finish;
+ }
RUN_WITH_UMASK(0022)
- (void) execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, NULL, NULL,
- (char**) argv, m->transient_environment,
- EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS | EXEC_DIR_SET_SYSTEMD_EXEC_PID);
+ (void) execute_directories(
+ (const char* const*) paths,
+ DEFAULT_TIMEOUT_USEC,
+ /* callbacks= */ NULL, /* callback_args= */ NULL,
+ (char**) argv,
+ ge,
+ EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS | EXEC_DIR_SET_SYSTEMD_EXEC_PID);
r = 0;
#include "base-filesystem.h"
#include "chase-symlinks.h"
#include "dev-setup.h"
+#include "devnum-util.h"
#include "env-util.h"
#include "escape.h"
#include "extension-release.h"
return 0;
/* Create symlinks like /dev/char/1:9 → ../urandom */
- if (asprintf(&sl, "%s/dev/%s/%u:%u",
+ if (asprintf(&sl, "%s/dev/%s/" DEVNUM_FORMAT_STR,
temporary_mount,
S_ISCHR(st.st_mode) ? "char" : "block",
- major(st.st_rdev), minor(st.st_rdev)) < 0)
+ DEVNUM_FORMAT_VAL(st.st_rdev)) < 0)
return log_oom();
(void) mkdir_parents(sl, 0755);
if (r < 0)
return log_debug_errno(r, "Failed to create loop device for root image: %m");
+ /* Make sure udevd won't issue BLKRRPART (which might flush out the loaded partition table)
+ * while we are still trying to mount things */
+ r = loop_device_flock(loop_device, LOCK_SH);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to lock loopback device with LOCK_SH: %m");
+
r = dissect_image(
loop_device->fd,
&verity,
goto finish;
}
+ /* Now release the block device lock, so that udevd is free to call BLKRRPART on the device
+ * if it likes. */
+ r = loop_device_flock(loop_device, LOCK_UN);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to release lock on loopback block device: %m");
+ goto finish;
+ }
+
if (decrypted_image) {
r = decrypted_image_relinquish(decrypted_image);
if (r < 0) {
}
int unit_full_printf_full(const Unit *u, const char *format, size_t max_length, char **ret) {
- /* This is similar to unit_name_printf() but also supports unescaping. Also, adds a couple of additional codes
- * (which are likely not suitable for unescaped inclusion in unit names):
+ /* This is similar to unit_name_printf() but also supports unescaping. Also, adds a couple of
+ * additional codes (which are likely not suitable for unescaped inclusion in unit names):
*
* %f: the unescaped instance if set, otherwise the id unescaped as path
*
* %h: the homedir of the running user
* %s: the shell of the running user
*
- * NOTICE: When you add new entries here, please be careful: specifiers which depend on settings of the unit
- * file itself are broken by design, as they would resolve differently depending on whether they are used
- * before or after the relevant configuration setting. Hence: don't add them.
+ * NOTICE: When you add new entries here, please be careful: specifiers which depend on settings of
+ * the unit file itself are broken by design, as they would resolve differently depending on whether
+ * they are used before or after the relevant configuration setting. Hence: don't add them.
*/
assert(u);
{ 'y', specifier_real_path, u->fragment_path },
{ 'Y', specifier_real_directory, u->fragment_path },
- { 'c', specifier_cgroup, NULL },
- { 'r', specifier_cgroup_slice, NULL },
- { 'R', specifier_cgroup_root, NULL },
+ { 'c', specifier_cgroup, NULL }, /* deprecated, see 1b89b0c499cd4bf0ff389caab4ecaae6e75f9d4e */
+ { 'r', specifier_cgroup_slice, NULL }, /* deprecated, see 1b89b0c499cd4bf0ff389caab4ecaae6e75f9d4e */
+ { 'R', specifier_cgroup_root, NULL }, /* deprecated, see 1b89b0c499cd4bf0ff389caab4ecaae6e75f9d4e */
{ 'C', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_CACHE) },
{ 'd', specifier_credentials_dir, NULL },
r = open_credential_directory(&d);
if (r == -ENXIO)
- return log_error_errno(r, "No credentials received. (i.e. $CREDENTIALS_PATH not set or pointing to empty directory.)");
+ return log_error_errno(r, "No credentials received. (i.e. $CREDENTIALS_DIRECTORY not set or pointing to empty directory.)");
if (r < 0)
return log_error_errno(r, "Failed to open credentials directory: %m");
if (r < 0)
return r;
+ r = loop_device_flock(d, LOCK_UN);
+ if (r < 0)
+ return log_error_errno(r, "Failed to unlock loopback block device: %m");
+
if (di) {
r = decrypted_image_relinquish(di);
if (r < 0)
mounted_dir = TAKE_PTR(created_dir);
+ r = loop_device_flock(d, LOCK_UN);
+ if (r < 0)
+ return log_error_errno(r, "Failed to unlock loopback block device: %m");
+
if (di) {
r = decrypted_image_relinquish(di);
if (r < 0)
if (r < 0)
return log_error_errno(r, "Failed to set up loopback device for %s: %m", arg_image);
+ /* Make sure udevd doesn't issue BLKRRPART underneath us thus making devices disappear in the middle,
+ * that we assume already are there. */
+ r = loop_device_flock(d, LOCK_SH);
+ if (r < 0)
+ return log_error_errno(r, "Failed to lock loopback device: %m");
+
r = dissect_image_and_warn(
d->fd,
arg_image,
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <stdlib.h>
+#include <sys/file.h>
#include <unistd.h>
#include "sd-device.h"
#include "blockdev-util.h"
#include "btrfs-util.h"
#include "device-util.h"
+#include "devnum-util.h"
#include "dirent-util.h"
#include "dissect-image.h"
#include "dropin.h"
if (sd_device_get_devname(d, &name) < 0) {
r = sd_device_get_syspath(d, &name);
if (r < 0) {
- log_device_debug_errno(d, r, "Device %u:%u does not have a name, ignoring: %m",
- major(devnum), minor(devnum));
+ log_device_debug_errno(d, r, "Device " DEVNUM_FORMAT_STR " does not have a name, ignoring: %m",
+ DEVNUM_FORMAT_VAL(devnum));
return 0;
}
}
if (r <= 0)
return r;
+ /* Let's take a LOCK_SH lock on the block device, in case udevd is already running. If we don't take
+ * the lock, udevd might end up issuing BLKRRPART in the middle, and we don't want that, since that
+ * might remove all partitions while we are operating on them. */
+ if (flock(fd, LOCK_SH) < 0)
+ return log_error_errno(errno, "Failed to lock root block device: %m");
+
r = dissect_image(
fd,
NULL, NULL,
UINT64_MAX,
USEC_INFINITY,
DISSECT_IMAGE_GPT_ONLY|
- DISSECT_IMAGE_NO_UDEV|
DISSECT_IMAGE_USR_NO_ROOT,
&m);
if (r == -ENOPKG) {
#include <sys/stat.h>
#include "alloc-util.h"
+#include "devnum-util.h"
#include "fileio.h"
#include "log.h"
#include "util.h"
int main(int argc, char *argv[]) {
struct stat st;
const char *device;
- _cleanup_free_ char *major_minor = NULL;
int r;
if (argc != 2) {
return EXIT_FAILURE;
}
- if (asprintf(&major_minor, "%d:%d", major(st.st_rdev), minor(st.st_rdev)) < 0) {
- log_oom();
- return EXIT_FAILURE;
- }
-
- r = write_string_file("/sys/power/resume", major_minor, WRITE_STRING_FILE_DISABLE_BUFFER);
+ r = write_string_file("/sys/power/resume", FORMAT_DEVNUM(st.st_rdev), WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0) {
- log_error_errno(r, "Failed to write '%s' to /sys/power/resume: %m", major_minor);
+ log_error_errno(r, "Failed to write '" DEVNUM_FORMAT_STR "' to /sys/power/resume: %m", DEVNUM_FORMAT_VAL(st.st_rdev));
return EXIT_FAILURE;
}
* no hibernation image).
*/
- log_info("Could not resume from '%s' (%s).", device, major_minor);
+ log_info("Could not resume from '%s' (" DEVNUM_FORMAT_STR ").", device, DEVNUM_FORMAT_VAL(st.st_rdev));
return EXIT_SUCCESS;
}
previous_devno = st.st_dev;
- r = quotactl_devno(QCMD_FIXED(Q_GETQUOTA, USRQUOTA), st.st_dev, uid, &req);
+ r = quotactl_devnum(QCMD_FIXED(Q_GETQUOTA, USRQUOTA), st.st_dev, uid, &req);
if (r < 0) {
if (ERRNO_IS_NOT_SUPPORTED(r))
log_debug_errno(r, "No UID quota support on %s, ignoring.", where);
#include "blockdev-util.h"
#include "btrfs-util.h"
#include "chattr-util.h"
+#include "devnum-util.h"
#include "dm-util.h"
#include "env-util.h"
#include "errno-util.h"
#include "process-util.h"
#include "random-util.h"
#include "resize-fs.h"
-#include "stat-util.h"
#include "strv.h"
#include "sync-util.h"
#include "tmpfile-util.h"
return log_error_errno(r, "Failed to stat block device %s: %m", n);
assert(S_ISBLK(st.st_mode));
- if (asprintf(&sysfs, "/sys/dev/block/%u:%u/partition", major(st.st_rdev), minor(st.st_rdev)) < 0)
+ if (asprintf(&sysfs, "/sys/dev/block/" DEVNUM_FORMAT_STR "/partition", DEVNUM_FORMAT_VAL(st.st_rdev)) < 0)
return log_oom();
if (access(sysfs, F_OK) < 0) {
} else {
_cleanup_free_ char *buffer = NULL;
- if (asprintf(&sysfs, "/sys/dev/block/%u:%u/start", major(st.st_rdev), minor(st.st_rdev)) < 0)
+ if (asprintf(&sysfs, "/sys/dev/block/" DEVNUM_FORMAT_STR "/start", DEVNUM_FORMAT_VAL(st.st_rdev)) < 0)
return log_oom();
r = read_one_line_file(sysfs, &buffer);
if (!S_ISBLK(st.st_mode))
return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Device is not a block device, refusing.");
- if (asprintf(&sysfs, "/sys/dev/block/%u:%u/partition", major(st.st_rdev), minor(st.st_rdev)) < 0)
+ if (asprintf(&sysfs, "/sys/dev/block/" DEVNUM_FORMAT_STR "/partition", DEVNUM_FORMAT_VAL(st.st_rdev)) < 0)
return log_oom();
if (access(sysfs, F_OK) < 0) {
if (errno != ENOENT)
if (devno == 0)
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system %s not backed by a block device.", path);
- r = quotactl_devno(QCMD_FIXED(Q_GETQUOTA, USRQUOTA), devno, h->uid, &req);
+ r = quotactl_devnum(QCMD_FIXED(Q_GETQUOTA, USRQUOTA), devno, h->uid, &req);
if (r < 0) {
if (ERRNO_IS_NOT_SUPPORTED(r))
return log_error_errno(r, "No UID quota support on %s.", path);
req.dqb_valid = QIF_BLIMITS;
req.dqb_bsoftlimit = req.dqb_bhardlimit = h->disk_size / QIF_DQBLKSIZE;
- r = quotactl_devno(QCMD_FIXED(Q_SETQUOTA, USRQUOTA), devno, h->uid, &req);
+ r = quotactl_devnum(QCMD_FIXED(Q_SETQUOTA, USRQUOTA), devno, h->uid, &req);
if (r < 0) {
if (r == -ESRCH)
return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "UID quota not available on %s.", path);
#include "main-func.h"
#include "mkdir.h"
#include "parse-argument.h"
-#include "parse-util.h"
-#include "path-util.h"
+#include "parse-helpers.h"
#include "pretty-print.h"
#include "process-util.h"
#include "rlimit-util.h"
return (enumerator->match_initialized == MATCH_INITIALIZED_NO) == (r == 0);
}
-static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, const char *basedir, const char *subdir1, const char *subdir2) {
+static int test_matches(
+ sd_device_enumerator *enumerator,
+ sd_device *device) {
+
+ int r;
+
+ assert(enumerator);
+ assert(device);
+
+ /* Checks all matches, except for the sysname match (which the caller should check beforehand) */
+
+ r = match_initialized(enumerator, device);
+ if (r <= 0)
+ return r;
+
+ if (!device_match_parent(device, enumerator->match_parent, NULL))
+ return false;
+
+ if (!match_tag(enumerator, device))
+ return false;
+
+ if (!match_property(enumerator, device))
+ return false;
+
+ if (!device_match_sysattr(device, enumerator->match_sysattr, enumerator->nomatch_sysattr))
+ return false;
+
+ return true;
+}
+
+static bool match_subsystem(sd_device_enumerator *enumerator, const char *subsystem) {
+ const char *subsystem_match;
+
+ assert(enumerator);
+
+ if (!subsystem)
+ return false;
+
+ SET_FOREACH(subsystem_match, enumerator->nomatch_subsystem)
+ if (fnmatch(subsystem_match, subsystem, 0) == 0)
+ return false;
+
+ if (set_isempty(enumerator->match_subsystem))
+ return true;
+
+ SET_FOREACH(subsystem_match, enumerator->match_subsystem)
+ if (fnmatch(subsystem_match, subsystem, 0) == 0)
+ return true;
+
+ return false;
+}
+
+static bool relevant_sysfs_subdir(const struct dirent *de) {
+ assert(de);
+
+ if (de->d_name[0] == '.')
+ return false;
+
+ /* Also filter out regular files and such, i.e. stuff that definitely isn't a kobject path. (Note
+ * that we rely on the fact that sysfs fills in d_type here, i.e. doesn't do DT_UNKNOWN) */
+ return IN_SET(de->d_type, DT_DIR, DT_LNK);
+}
+
+static int enumerator_scan_dir_and_add_devices(
+ sd_device_enumerator *enumerator,
+ const char *basedir,
+ const char *subdir1,
+ const char *subdir2) {
+
_cleanup_closedir_ DIR *dir = NULL;
char *path;
int k, r = 0;
FOREACH_DIRENT_ALL(de, dir, return -errno) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
char syspath[strlen(path) + 1 + strlen(de->d_name) + 1];
+ sd_device *upwards;
- if (de->d_name[0] == '.')
+ if (!relevant_sysfs_subdir(de))
continue;
if (!match_sysname(enumerator, de->d_name))
continue;
}
- k = match_initialized(enumerator, device);
+ k = test_matches(enumerator, device);
if (k <= 0) {
if (k < 0)
r = k;
continue;
}
- if (!device_match_parent(device, enumerator->match_parent, NULL))
- continue;
-
- if (!match_tag(enumerator, device))
- continue;
-
- if (!match_property(enumerator, device))
- continue;
-
- if (!device_match_sysattr(device, enumerator->match_sysattr, enumerator->nomatch_sysattr))
- continue;
-
k = device_enumerator_add_device(enumerator, device);
if (k < 0)
r = k;
- }
- return r;
-}
+ /* Also include all potentially matching parent devices in the enumeration. These are things
+ * like root busses — e.g. /sys/devices/pci0000:00/ or /sys/devices/pnp0/, which ar not
+ * linked from /sys/class/ or /sys/bus/, hence pick them up explicitly here. */
+ upwards = device;
+ for (;;) {
+ const char *ss, *usn;
-static bool match_subsystem(sd_device_enumerator *enumerator, const char *subsystem) {
- const char *subsystem_match;
+ k = sd_device_get_parent(upwards, &upwards);
+ if (k == -ENOENT) /* Reached the top? */
+ break;
+ if (k < 0) {
+ r = k;
+ break;
+ }
- assert(enumerator);
+ k = sd_device_get_subsystem(upwards, &ss);
+ if (k == -ENOENT) /* Has no subsystem? */
+ continue;
+ if (k < 0) {
+ r = k;
+ break;
+ }
- if (!subsystem)
- return false;
+ if (!match_subsystem(enumerator, ss))
+ continue;
- SET_FOREACH(subsystem_match, enumerator->nomatch_subsystem)
- if (fnmatch(subsystem_match, subsystem, 0) == 0)
- return false;
+ k = sd_device_get_sysname(upwards, &usn);
+ if (k < 0) {
+ r = k;
+ break;
+ }
- if (set_isempty(enumerator->match_subsystem))
- return true;
+ if (!match_sysname(enumerator, usn))
+ continue;
- SET_FOREACH(subsystem_match, enumerator->match_subsystem)
- if (fnmatch(subsystem_match, subsystem, 0) == 0)
- return true;
+ k = test_matches(enumerator, upwards);
+ if (k < 0)
+ break;
+ if (k == 0)
+ continue;
- return false;
+ k = device_enumerator_add_device(enumerator, upwards);
+ if (k < 0)
+ r = k;
+ else if (k == 0) /* Exists already? Then no need to go further up. */
+ break;
+ }
+ }
+
+ return r;
}
-static int enumerator_scan_dir(sd_device_enumerator *enumerator, const char *basedir, const char *subdir, const char *subsystem) {
+static int enumerator_scan_dir(
+ sd_device_enumerator *enumerator,
+ const char *basedir,
+ const char *subdir,
+ const char *subsystem) {
+
_cleanup_closedir_ DIR *dir = NULL;
char *path;
int r = 0;
FOREACH_DIRENT_ALL(de, dir, return -errno) {
int k;
- if (de->d_name[0] == '.')
+ if (!relevant_sysfs_subdir(de))
continue;
if (!match_subsystem(enumerator, subsystem ? : de->d_name))
}
int device_enumerator_scan_devices_and_subsystems(sd_device_enumerator *enumerator) {
- int r = 0, k;
+ int r;
assert(enumerator);
device_enumerator_unref_devices(enumerator);
- if (!set_isempty(enumerator->match_tag)) {
- k = enumerator_scan_devices_tags(enumerator);
- if (k < 0)
- r = k;
- } else if (enumerator->match_parent) {
- k = enumerator_scan_devices_children(enumerator);
- if (k < 0)
- r = k;
- } else {
- k = enumerator_scan_dir(enumerator, "class", NULL, NULL);
- if (k < 0)
- r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/class: %m");
+ if (!set_isempty(enumerator->match_tag))
+ r = enumerator_scan_devices_tags(enumerator);
+ else if (enumerator->match_parent)
+ r = enumerator_scan_devices_children(enumerator);
+ else {
+ int k;
- k = enumerator_scan_dir(enumerator, "bus", "devices", NULL);
- if (k < 0)
- r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m");
+ r = enumerator_scan_devices_all(enumerator);
if (match_subsystem(enumerator, "module")) {
k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL);
#include "device-internal.h"
#include "device-private.h"
#include "device-util.h"
+#include "devnum-util.h"
#include "dirent-util.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
#include "hashmap.h"
#include "id128-util.h"
#include "macro.h"
+#include "missing_magic.h"
#include "netlink-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "set.h"
#include "socket-util.h"
-#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
_syspath);
if (verify) {
- r = chase_symlinks(_syspath, NULL, 0, &syspath, NULL);
+ _cleanup_close_ int fd = -1;
+
+ r = chase_symlinks(_syspath, NULL, 0, &syspath, &fd);
if (r == -ENOENT)
/* the device does not exist (any more?) */
return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
path_simplify(syspath);
}
- if (path_startswith(syspath, "/sys/devices/")) {
- char *path;
+ if (path_startswith(syspath, "/sys/devices/")) {
+ /* For proper devices, stricter rules apply: they must have a 'uevent' file,
+ * otherwise we won't allow them */
- /* all 'devices' require an 'uevent' file */
- path = strjoina(syspath, "/uevent");
- if (access(path, F_OK) < 0) {
+ if (faccessat(fd, "uevent", F_OK, 0) < 0) {
if (errno == ENOENT)
- /* This is not a valid device.
- * Note, this condition is quite often satisfied when
- * enumerating devices or finding a parent device.
+ /* This is not a valid device. Note, this condition is quite often
+ * satisfied when enumerating devices or finding a parent device.
* Hence, use log_trace_errno() here. */
return log_trace_errno(SYNTHETIC_ERRNO(ENODEV),
- "sd-device: the uevent file \"%s\" does not exist.", path);
+ "sd-device: the uevent file \"%s/uevent\" does not exist.", syspath);
+ if (errno == ENOTDIR)
+ /* Not actually a directory. */
+ return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+ "sd-device: the syspath \"%s\" is not a directory.", syspath);
- return log_debug_errno(errno, "sd-device: cannot access uevent file for %s: %m", syspath);
+ return log_debug_errno(errno, "sd-device: cannot find uevent file for %s: %m", syspath);
}
} else {
- /* everything else just needs to be a directory */
- if (!is_dir(syspath, false))
+ struct stat st;
+
+ /* For everything else lax rules apply: they just need to be a directory */
+
+ if (fstat(fd, &st) < 0)
+ return log_debug_errno(errno, "sd-device: failed to check if syspath \"%s\" is a directory: %m", syspath);
+ if (!S_ISDIR(st.st_mode))
return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
"sd-device: the syspath \"%s\" is not a directory.", syspath);
}
+
+ /* Only operate on sysfs, i.e. refuse going down into /sys/fs/cgroup/ or similar places where
+ * things are not arranged as kobjects in kernel, and hence don't necessarily have
+ * kobject/attribute structure. */
+ r = getenv_bool_secure("SYSTEMD_DEVICE_VERIFY_SYSFS");
+ if (r < 0 && r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_DEVICE_VERIFY_SYSFS value: %m");
+ if (r != 0) {
+ r = fd_is_fs_type(fd, SYSFS_MAGIC);
+ if (r < 0)
+ return log_debug_errno(r, "sd-device: failed to check if syspath \"%s\" is backed by sysfs.", syspath);
+ if (r == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+ "sd-device: the syspath \"%s\" is outside of sysfs, refusing.", syspath);
+ }
} else {
syspath = strdup(_syspath);
if (!syspath)
return log_oom_debug();
- }
- devpath = syspath + STRLEN("/sys");
+ path_simplify(syspath);
+ }
+ assert_se(devpath = startswith(syspath, "/sys"));
if (devpath[0] != '/')
return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "sd-device: \"/sys\" alone is not a valid device path.");
if (r < 0)
return r;
- r = device_set_syspath(device, syspath, true);
+ r = device_set_syspath(device, syspath, /* verify= */ true);
if (r < 0)
return r;
return 0;
}
-_public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum) {
- char id[DECIMAL_STR_MAX(unsigned) * 2 + 1], *syspath;
+static int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ _cleanup_free_ char *syspath = NULL;
+ const char *t, *subsystem;
+ dev_t n;
+ int r;
- assert_return(ret, -EINVAL);
- assert_return(IN_SET(type, 'b', 'c'), -EINVAL);
+ assert(ret);
+
+ if (S_ISCHR(mode))
+ t = "char";
+ else if (S_ISBLK(mode))
+ t = "block";
+ else
+ return -ENOTTY;
- if (devnum == 0)
- return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
- "sd-device: Attempted to allocate device by zero major/minor, refusing.");
+ if (major(devnum) == 0)
+ return -ENODEV;
- /* use /sys/dev/{block,char}/<maj>:<min> link */
- xsprintf(id, "%u:%u", major(devnum), minor(devnum));
+ if (asprintf(&syspath, "/sys/dev/%s/%u:%u", t, major(devnum), minor(devnum)) < 0)
+ return -ENOMEM;
- syspath = strjoina("/sys/dev/", (type == 'b' ? "block" : "char"), "/", id);
+ r = sd_device_new_from_syspath(&dev, syspath);
+ if (r < 0)
+ return r;
- return sd_device_new_from_syspath(ret, syspath);
+ r = sd_device_get_devnum(dev, &n);
+ if (r == -ENOENT)
+ return -ENXIO;
+ if (r < 0)
+ return r;
+ if (n != devnum)
+ return -ENXIO;
+
+ r = sd_device_get_subsystem(dev, &subsystem);
+ if (r < 0 && r != -ENOENT)
+ return r;
+ if (r >= 0 && streq(subsystem, "block") != !!S_ISBLK(mode))
+ return -ENXIO;
+
+ *ret = TAKE_PTR(dev);
+ return 0;
+}
+
+_public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum) {
+ assert_return(ret, -EINVAL);
+ assert_return(IN_SET(type, 'b', 'c'), -EINVAL);
+
+ return device_new_from_mode_and_devnum(ret, type == 'b' ? S_IFBLK : S_IFCHR, devnum);
}
static int device_new_from_main_ifname(sd_device **ret, const char *ifname) {
}
_public_ int sd_device_new_from_ifindex(sd_device **ret, int ifindex) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
char ifname[IF_NAMESIZE];
+ int r, i;
assert_return(ret, -EINVAL);
assert_return(ifindex > 0, -EINVAL);
if (format_ifname(ifindex, ifname) < 0)
return -ENODEV;
- return device_new_from_main_ifname(ret, ifname);
+ r = device_new_from_main_ifname(&dev, ifname);
+ if (r < 0)
+ return r;
+
+ r = sd_device_get_ifindex(dev, &i);
+ if (r == -ENOENT)
+ return -ENXIO;
+ if (r < 0)
+ return r;
+ if (i != ifindex)
+ return -ENXIO;
+
+ *ret = TAKE_PTR(dev);
+ return 0;
}
static int device_strjoin_new(
const char *subsys = memdupa_suffix0(sysname, sep - sysname);
sep++;
- r = device_strjoin_new("/sys/bus/", subsys, "/drivers/", sep, ret);
+ if (streq(sep, "drivers")) /* If the sysname is "drivers", then it's the drivers directory itself that is meant. */
+ r = device_strjoin_new("/sys/bus/", subsys, "/drivers", NULL, ret);
+ else
+ r = device_strjoin_new("/sys/bus/", subsys, "/drivers/", sep, ret);
if (r < 0)
return r;
if (r > 0)
}
_public_ int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st) {
- char type;
-
assert_return(ret, -EINVAL);
assert_return(st, -EINVAL);
- if (S_ISBLK(st->st_mode))
- type = 'b';
- else if (S_ISCHR(st->st_mode))
- type = 'c';
- else
- return -ENOTTY;
-
- return sd_device_new_from_devnum(ret, type, st->st_rdev);
+ return device_new_from_mode_and_devnum(ret, st->st_mode, st->st_rdev);
}
_public_ int sd_device_new_from_devname(sd_device **ret, const char *devname) {
struct stat st;
+ dev_t devnum;
+ mode_t mode;
assert_return(ret, -EINVAL);
assert_return(devname, -EINVAL);
if (isempty(path_startswith(devname, "/dev")))
return -EINVAL;
- if (device_path_parse_major_minor(devname, NULL, NULL) >= 0) {
- _cleanup_free_ char *syspath = NULL;
-
+ if (device_path_parse_major_minor(devname, &mode, &devnum) >= 0)
/* Let's shortcut when "/dev/block/maj:min" or "/dev/char/maj:min" is specified.
- * In that case, we directly convert the path to syspath, hence it is not necessary
+ * In that case, we can directly convert the path to syspath, hence it is not necessary
* that the specified path exists. So, this works fine without udevd being running. */
-
- syspath = path_join("/sys", devname);
- return sd_device_new_from_syspath(ret, syspath);
- }
+ return device_new_from_mode_and_devnum(ret, mode, devnum);
if (stat(devname, &st) < 0)
return ERRNO_IS_DEVICE_ABSENT(errno) ? -ENODEV : -errno;
return r;
if (maj == 0)
return 0;
+ if (!DEVICE_MAJOR_VALID(maj))
+ return -EINVAL;
if (minor) {
r = safe_atou(minor, &min);
if (r < 0)
return r;
+ if (!DEVICE_MINOR_VALID(min))
+ return -EINVAL;
}
r = device_add_property_internal(device, "MAJOR", major);
const char *value,
const char **major,
const char **minor) {
- int r;
assert(device);
assert(key);
assert(major);
assert(minor);
- if (streq(key, "DEVTYPE")) {
- r = device_set_devtype(device, value);
- if (r < 0)
- return r;
- } else if (streq(key, "IFINDEX")) {
- r = device_set_ifindex(device, value);
- if (r < 0)
- return r;
- } else if (streq(key, "DEVNAME")) {
- r = device_set_devname(device, value);
- if (r < 0)
- return r;
- } else if (streq(key, "DEVMODE")) {
- r = device_set_devmode(device, value);
- if (r < 0)
- return r;
- } else if (streq(key, "DISKSEQ")) {
- r = device_set_diskseq(device, value);
- if (r < 0)
- return r;
- } else if (streq(key, "MAJOR"))
+ if (streq(key, "DEVTYPE"))
+ return device_set_devtype(device, value);
+ if (streq(key, "IFINDEX"))
+ return device_set_ifindex(device, value);
+ if (streq(key, "DEVNAME"))
+ return device_set_devname(device, value);
+ if (streq(key, "DEVMODE"))
+ return device_set_devmode(device, value);
+ if (streq(key, "DISKSEQ"))
+ return device_set_diskseq(device, value);
+ if (streq(key, "MAJOR"))
*major = value;
else if (streq(key, "MINOR"))
*minor = value;
- else {
- r = device_add_property_internal(device, key, value);
- if (r < 0)
- return r;
- }
+ else
+ return device_add_property_internal(device, key, value);
return 0;
}
if (major) {
r = device_set_devnum(device, major, minor);
if (r < 0)
- log_device_debug_errno(device, r, "sd-device: Failed to set 'MAJOR=%s' or 'MINOR=%s' from '%s', ignoring: %m", major, minor, path);
+ log_device_debug_errno(device, r, "sd-device: Failed to set 'MAJOR=%s' or 'MINOR=%s' from '%s', ignoring: %m", major, strna(minor), path);
}
return 0;
if (isempty(id))
return -EINVAL;
- r = parse_dev(id + 1, &devt);
+ r = parse_devnum(id + 1, &devt);
if (r < 0)
return r;
return r;
drivers = strstr(devpath, "/drivers/");
+ if (!drivers)
+ drivers = endswith(devpath, "/drivers");
if (!drivers)
return -EINVAL;
- r = path_find_last_component(devpath, false, &drivers, &p);
+ /* Find the path component immediately before the "/drivers/" string */
+ r = path_find_last_component(devpath, /* accept_dot_dot= */ false, &drivers, &p);
if (r < 0)
return r;
if (r == 0)
if (subsystem)
r = device_set_subsystem(device, subsystem);
/* use implicit names */
- else if (path_startswith(device->devpath, "/module/"))
+ else if (!isempty(path_startswith(device->devpath, "/module/")))
r = device_set_subsystem(device, "module");
- else if (strstr(syspath, "/drivers/"))
+ else if (strstr(syspath, "/drivers/") || endswith(syspath, "/drivers"))
r = device_set_drivers_subsystem(device);
- else if (PATH_STARTSWITH_SET(device->devpath, "/class/", "/bus/"))
+ else if (!isempty(PATH_STARTSWITH_SET(device->devpath, "/class/", "/bus/")))
r = device_set_subsystem(device, "subsystem");
else {
device->subsystem_set = true;
}
static int handle_db_line(sd_device *device, char key, const char *value) {
- char *path;
int r;
assert(device);
switch (key) {
case 'G': /* Any tag */
case 'Q': /* Current tag */
- r = device_add_tag(device, value, key == 'Q');
- if (r < 0)
- return r;
+ return device_add_tag(device, value, key == 'Q');
- break;
- case 'S':
- path = strjoina("/dev/", value);
- r = device_add_devlink(device, path);
- if (r < 0)
- return r;
+ case 'S': {
+ const char *path;
- break;
+ path = strjoina("/dev/", value);
+ return device_add_devlink(device, path);
+ }
case 'E':
- r = device_add_property_internal_from_string(device, value);
- if (r < 0)
- return r;
+ return device_add_property_internal_from_string(device, value);
- break;
case 'I': {
usec_t t;
if (r < 0)
return r;
- r = device_set_usec_initialized(device, t);
- if (r < 0)
- return r;
-
- break;
+ return device_set_usec_initialized(device, t);
}
case 'L':
- r = safe_atoi(value, &device->devlink_priority);
- if (r < 0)
- return r;
+ return safe_atoi(value, &device->devlink_priority);
- break;
case 'W':
/* Deprecated. Previously, watch handle is both saved in database and /run/udev/watch.
* However, the handle saved in database may not be updated when the handle is updated
* or removed. Moreover, it is not necessary to store the handle within the database,
* as its value becomes meaningless when udevd is restarted. */
- break;
+ return 0;
+
case 'V':
- r = safe_atou(value, &device->database_version);
- if (r < 0)
- return r;
+ return safe_atou(value, &device->database_version);
- break;
default:
log_device_debug(device, "sd-device: Unknown key '%c' in device db, ignoring", key);
+ return 0;
}
-
- return 0;
}
int device_get_device_id(sd_device *device, const char **ret) {
return;
}
+ if (event_pid_changed(e))
+ return;
+
assert(d->fd >= 0);
if (signalfd(d->fd, &d->sigset, SFD_NONBLOCK|SFD_CLOEXEC) < 0)
break;
case SOURCE_CHILD:
+ if (event_pid_changed(s->event))
+ s->child.process_owned = false;
+
if (s->child.pid > 0) {
if (event_source_is_online(s)) {
assert(s->event->n_online_child_sources > 0);
return -ENOMEM;
s->wakeup = WAKEUP_EVENT_SOURCE;
- s->child.pid = pid;
s->child.options = options;
s->child.callback = callback;
s->userdata = userdata;
* pin the PID, and make regular waitid() handling race-free. */
if (shall_use_pidfd()) {
- s->child.pidfd = pidfd_open(s->child.pid, 0);
+ s->child.pidfd = pidfd_open(pid, 0);
if (s->child.pidfd < 0) {
/* Propagate errors unless the syscall is not supported or blocked */
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
} else
s->child.pidfd = -1;
- r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s);
- if (r < 0)
- return r;
-
if (EVENT_SOURCE_WATCH_PIDFD(s)) {
/* We have a pidfd and we only want to watch for exit */
r = source_child_pidfd_register(s, s->enabled);
e->need_process_child = true;
}
+ r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s);
+ if (r < 0)
+ return r;
+
+ /* These must be done after everything succeeds. */
+ s->child.pid = pid;
e->n_online_child_sources++;
if (ret)
assert_se(hashmap_remove(e->inotify_data, &d->priority) == d);
if (d->fd >= 0) {
- if (epoll_ctl(e->epoll_fd, EPOLL_CTL_DEL, d->fd, NULL) < 0)
+ if (!event_pid_changed(e) &&
+ epoll_ctl(e->epoll_fd, EPOLL_CTL_DEL, d->fd, NULL) < 0)
log_debug_errno(errno, "Failed to remove inotify fd from epoll, ignoring: %m");
safe_close(d->fd);
if (d->inotify_data) {
if (d->wd >= 0) {
- if (d->inotify_data->fd >= 0) {
+ if (d->inotify_data->fd >= 0 && !event_pid_changed(e)) {
/* So here's a problem. At the time this runs the watch descriptor might already be
* invalidated, because an IN_IGNORED event might be queued right the moment we enter
* the syscall. Hence, whenever we get EINVAL, ignore it entirely, since it's a very
e->need_process_child = false;
- /*
- So, this is ugly. We iteratively invoke waitid() with P_PID
- + WNOHANG for each PID we wait for, instead of using
- P_ALL. This is because we only want to get child
- information of very specific child processes, and not all
- of them. We might not have processed the SIGCHLD even of a
- previous invocation and we don't want to maintain a
- unbounded *per-child* event queue, hence we really don't
- want anything flushed out of the kernel's queue that we
- don't care about. Since this is O(n) this means that if you
- have a lot of processes you probably want to handle SIGCHLD
- yourself.
-
- We do not reap the children here (by using WNOWAIT), this
- is only done after the event source is dispatched so that
- the callback still sees the process as a zombie.
- */
+ /* So, this is ugly. We iteratively invoke waitid() with P_PID + WNOHANG for each PID we wait
+ * for, instead of using P_ALL. This is because we only want to get child information of very
+ * specific child processes, and not all of them. We might not have processed the SIGCHLD event
+ * of a previous invocation and we don't want to maintain a unbounded *per-child* event queue,
+ * hence we really don't want anything flushed out of the kernel's queue that we don't care
+ * about. Since this is O(n) this means that if you have a lot of processes you probably want
+ * to handle SIGCHLD yourself.
+ *
+ * We do not reap the children here (by using WNOWAIT), this is only done after the event
+ * source is dispatched so that the callback still sees the process as a zombie. */
HASHMAP_FOREACH(s, e->child_sources) {
assert(s->type == SOURCE_CHILD);
if (s->child.exited)
continue;
- if (EVENT_SOURCE_WATCH_PIDFD(s)) /* There's a usable pidfd known for this event source? then don't waitid() for it here */
+ if (EVENT_SOURCE_WATCH_PIDFD(s))
+ /* There's a usable pidfd known for this event source? Then don't waitid() for
+ * it here */
continue;
zero(s->child.siginfo);
s->child.exited = true;
if (!zombie && (s->child.options & WEXITED)) {
- /* If the child isn't dead then let's
- * immediately remove the state change
- * from the queue, since there's no
- * benefit in leaving it queued */
+ /* If the child isn't dead then let's immediately remove the state
+ * change from the queue, since there's no benefit in leaving it
+ * queued. */
assert(s->child.options & (WSTOPPED|WCONTINUED));
(void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
assert_return(events == EPOLLIN, -EIO);
assert(min_priority);
- /* If there's a signal queued on this priority and SIGCHLD is
- on this priority too, then make sure to recheck the
- children we watch. This is because we only ever dequeue
- the first signal per priority, and if we dequeue one, and
- SIGCHLD might be enqueued later we wouldn't know, but we
- might have higher priority children we care about hence we
- need to check that explicitly. */
+ /* If there's a signal queued on this priority and SIGCHLD is on this priority too, then make
+ * sure to recheck the children we watch. This is because we only ever dequeue the first signal
+ * per priority, and if we dequeue one, and SIGCHLD might be enqueued later we wouldn't know,
+ * but we might have higher priority children we care about hence we need to check that
+ * explicitly. */
if (sigismember(&d->sigset, SIGCHLD))
e->need_process_child = true;
- /* If there's already an event source pending for this
- * priority we don't read another */
+ /* If there's already an event source pending for this priority we don't read another */
if (d->current)
return 0;
r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU");
if (r == -ENXIO) {
- /* EFI case: returns the current value of LoaderConfigTimeoutOneShot. Three cases are distuingished:
+ /* EFI case: returns the current value of LoaderConfigTimeoutOneShot. Three cases are distinguished:
*
* 1. Variable not set, boot into boot loader menu is not enabled (we return UINT64_MAX to the user)
* 2. Variable set to "0", boot into boot loader menu is enabled with no timeout (we return 0 to the user)
#include "bus-label.h"
#include "bus-polkit.h"
#include "bus-util.h"
+#include "devnum-util.h"
#include "fd-util.h"
#include "logind-brightness.h"
#include "logind-dbus.h"
#include "missing_capability.h"
#include "path-util.h"
#include "signal-util.h"
-#include "stat-util.h"
#include "strv.h"
#include "user-util.h"
#include "util.h"
#include "audit-util.h"
#include "bus-error.h"
#include "bus-util.h"
+#include "devnum-util.h"
#include "env-file.h"
#include "escape.h"
#include "fd-util.h"
break;
}
- k = parse_dev(word, &dev);
+ k = parse_devnum(word, &dev);
if (k < 0) {
r = k;
continue;
NULL,
bus_machine_method_terminate,
SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("Kill",
- "si",
- SD_BUS_PARAM(who)
- SD_BUS_PARAM(signal),
- NULL,,
- bus_machine_method_kill,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetAddresses",
- NULL,,
- "a(iay)",
- SD_BUS_PARAM(addresses),
- bus_machine_method_get_addresses,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetOSRelease",
- NULL,,
- "a{ss}",
- SD_BUS_PARAM(fields),
- bus_machine_method_get_os_release,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetUIDShift",
- NULL,,
- "u",
- SD_BUS_PARAM(shift),
- bus_machine_method_get_uid_shift,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("OpenPTY",
- NULL,,
- "hs",
- SD_BUS_PARAM(pty)
- SD_BUS_PARAM(pty_path),
- bus_machine_method_open_pty,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("OpenLogin",
- NULL,,
- "hs",
- SD_BUS_PARAM(pty)
- SD_BUS_PARAM(pty_path),
- bus_machine_method_open_login,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("OpenShell",
- "ssasas",
- SD_BUS_PARAM(user)
- SD_BUS_PARAM(path)
- SD_BUS_PARAM(args)
- SD_BUS_PARAM(environment),
- "hs",
- SD_BUS_PARAM(pty)
- SD_BUS_PARAM(pty_path),
- bus_machine_method_open_shell,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("BindMount",
- "ssbb",
- SD_BUS_PARAM(source)
- SD_BUS_PARAM(destination)
- SD_BUS_PARAM(read_only)
- SD_BUS_PARAM(mkdir),
- NULL,,
- bus_machine_method_bind_mount,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("CopyFrom",
- "ss",
- SD_BUS_PARAM(source)
- SD_BUS_PARAM(destination),
- NULL,,
- bus_machine_method_copy,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("CopyTo",
- "ss",
- SD_BUS_PARAM(source)
- SD_BUS_PARAM(destination),
- NULL,,
- bus_machine_method_copy,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("OpenRootDirectory",
- NULL,,
- "h",
- SD_BUS_PARAM(fd),
- bus_machine_method_open_root_directory,
- SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("Kill",
+ SD_BUS_ARGS("s", who, "i", signal),
+ SD_BUS_NO_RESULT,
+ bus_machine_method_kill,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetAddresses",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("a(iay)", addresses),
+ bus_machine_method_get_addresses,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetOSRelease",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("a{ss}", fields),
+ bus_machine_method_get_os_release,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetUIDShift",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("u", shift),
+ bus_machine_method_get_uid_shift,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("OpenPTY",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("h", pty, "s", pty_path),
+ bus_machine_method_open_pty,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("OpenLogin",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("h", pty, "s", pty_path),
+ bus_machine_method_open_login,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("OpenShell",
+ SD_BUS_ARGS("s", user, "s", path, "as", args, "as", environment),
+ SD_BUS_RESULT("h", pty, "s", pty_path),
+ bus_machine_method_open_shell,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("BindMount",
+ SD_BUS_ARGS("s", source, "s", destination, "b", read_only, "b", mkdir),
+ SD_BUS_NO_RESULT,
+ bus_machine_method_bind_mount,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("CopyFrom",
+ SD_BUS_ARGS("s", source, "s", destination),
+ SD_BUS_NO_RESULT,
+ bus_machine_method_copy,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("CopyTo",
+ SD_BUS_ARGS("s", source, "s", destination),
+ SD_BUS_NO_RESULT,
+ bus_machine_method_copy,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("OpenRootDirectory",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("h", fd),
+ bus_machine_method_open_root_directory,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage, 0, 0),
SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit, 0, 0),
- SD_BUS_METHOD_WITH_NAMES("GetMachine",
- "s",
- SD_BUS_PARAM(name),
- "o",
- SD_BUS_PARAM(machine),
- method_get_machine,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetImage",
- "s",
- SD_BUS_PARAM(name),
- "o",
- SD_BUS_PARAM(image),
- method_get_image,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetMachineByPID",
- "u",
- SD_BUS_PARAM(pid),
- "o",
- SD_BUS_PARAM(machine),
- method_get_machine_by_pid,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ListMachines",
- NULL,,
- "a(ssso)",
- SD_BUS_PARAM(machines),
- method_list_machines,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ListImages",
- NULL,,
- "a(ssbttto)",
- SD_BUS_PARAM(images),
- method_list_images,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("CreateMachine",
- "sayssusa(sv)",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(id)
- SD_BUS_PARAM(service)
- SD_BUS_PARAM(class)
- SD_BUS_PARAM(leader)
- SD_BUS_PARAM(root_directory)
- SD_BUS_PARAM(scope_properties),
- "o",
- SD_BUS_PARAM(path),
- method_create_machine, 0),
- SD_BUS_METHOD_WITH_NAMES("CreateMachineWithNetwork",
- "sayssusaia(sv)",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(id)
- SD_BUS_PARAM(service)
- SD_BUS_PARAM(class)
- SD_BUS_PARAM(leader)
- SD_BUS_PARAM(root_directory)
- SD_BUS_PARAM(ifindices)
- SD_BUS_PARAM(scope_properties),
- "o",
- SD_BUS_PARAM(path),
- method_create_machine_with_network, 0),
- SD_BUS_METHOD_WITH_NAMES("RegisterMachine",
- "sayssus",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(id)
- SD_BUS_PARAM(service)
- SD_BUS_PARAM(class)
- SD_BUS_PARAM(leader)
- SD_BUS_PARAM(root_directory),
- "o",
- SD_BUS_PARAM(path),
- method_register_machine, 0),
- SD_BUS_METHOD_WITH_NAMES("RegisterMachineWithNetwork",
- "sayssusai",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(id)
- SD_BUS_PARAM(service)
- SD_BUS_PARAM(class)
- SD_BUS_PARAM(leader)
- SD_BUS_PARAM(root_directory)
- SD_BUS_PARAM(ifindices),
- "o",
- SD_BUS_PARAM(path),
- method_register_machine_with_network, 0),
- SD_BUS_METHOD_WITH_NAMES("UnregisterMachine",
- "s",
- SD_BUS_PARAM(name),
- NULL,,
- method_unregister_machine,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("TerminateMachine",
- "s",
- SD_BUS_PARAM(id),
- NULL,,
- method_terminate_machine,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("KillMachine",
- "ssi",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(who)
- SD_BUS_PARAM(signal),
- NULL,,
- method_kill_machine,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetMachineAddresses",
- "s",
- SD_BUS_PARAM(name),
- "a(iay)",
- SD_BUS_PARAM(addresses),
- method_get_machine_addresses,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetMachineOSRelease",
- "s",
- SD_BUS_PARAM(name),
- "a{ss}",
- SD_BUS_PARAM(fields),
- method_get_machine_os_release,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("OpenMachinePTY",
- "s",
- SD_BUS_PARAM(name),
- "hs",
- SD_BUS_PARAM(pty)
- SD_BUS_PARAM(pty_path),
- method_open_machine_pty,
- 0),
- SD_BUS_METHOD_WITH_NAMES("OpenMachineLogin",
- "s",
- SD_BUS_PARAM(name),
- "hs",
- SD_BUS_PARAM(pty)
- SD_BUS_PARAM(pty_path),
- method_open_machine_login,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("OpenMachineShell",
- "sssasas",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(user)
- SD_BUS_PARAM(path)
- SD_BUS_PARAM(args)
- SD_BUS_PARAM(environment),
- "hs",
- SD_BUS_PARAM(pty)
- SD_BUS_PARAM(pty_path),
- method_open_machine_shell,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("BindMountMachine",
- "sssbb",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(source)
- SD_BUS_PARAM(destination)
- SD_BUS_PARAM(read_only)
- SD_BUS_PARAM(mkdir),
- NULL,,
- method_bind_mount_machine,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("CopyFromMachine",
- "sss",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(source)
- SD_BUS_PARAM(destination),
- NULL,,
- method_copy_machine,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("CopyToMachine",
- "sss",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(source)
- SD_BUS_PARAM(destination),
- NULL,,
- method_copy_machine,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("OpenMachineRootDirectory",
- "s",
- SD_BUS_PARAM(name),
- "h",
- SD_BUS_PARAM(fd),
- method_open_machine_root_directory,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetMachineUIDShift",
- "s",
- SD_BUS_PARAM(name),
- "u",
- SD_BUS_PARAM(shift),
- method_get_machine_uid_shift,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("RemoveImage",
- "s",
- SD_BUS_PARAM(name),
- NULL,,
- method_remove_image,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("RenameImage",
- "ss",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(new_name),
- NULL,,
- method_rename_image,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("CloneImage",
- "ssb",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(new_name)
- SD_BUS_PARAM(read_only),
- NULL,,
- method_clone_image,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("MarkImageReadOnly",
- "sb",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(read_only),
- NULL,,
- method_mark_image_read_only,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetImageHostname",
- "s",
- SD_BUS_PARAM(name),
- "s",
- SD_BUS_PARAM(hostname),
- method_get_image_hostname,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetImageMachineID",
- "s",
- SD_BUS_PARAM(name),
- "ay",
- SD_BUS_PARAM(id),
- method_get_image_machine_id,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetImageMachineInfo",
- "s",
- SD_BUS_PARAM(name),
- "a{ss}",
- SD_BUS_PARAM(machine_info),
- method_get_image_machine_info,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("GetImageOSRelease",
- "s",
- SD_BUS_PARAM(name),
- "a{ss}",
- SD_BUS_PARAM(os_release),
- method_get_image_os_release,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetPoolLimit",
- "t",
- SD_BUS_PARAM(size),
- NULL,,
- method_set_pool_limit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetImageLimit",
- "st",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(size),
- NULL,,
- method_set_image_limit,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("CleanPool",
- "s",
- SD_BUS_PARAM(mode),
- "a(st)",
- SD_BUS_PARAM(images),
- method_clean_pool,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("MapFromMachineUser",
- "su",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(uid_inner),
- "u",
- SD_BUS_PARAM(uid_outer),
- method_map_from_machine_user,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("MapToMachineUser",
- "u",
- SD_BUS_PARAM(uid_outer),
- "sou",
- SD_BUS_PARAM(machine_name)
- SD_BUS_PARAM(machine_path)
- SD_BUS_PARAM(uid_inner),
- method_map_to_machine_user,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("MapFromMachineGroup",
- "su",
- SD_BUS_PARAM(name)
- SD_BUS_PARAM(gid_inner),
- "u",
- SD_BUS_PARAM(gid_outer),
- method_map_from_machine_group,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("MapToMachineGroup",
- "u",
- SD_BUS_PARAM(gid_outer),
- "sou",
- SD_BUS_PARAM(machine_name)
- SD_BUS_PARAM(machine_path)
- SD_BUS_PARAM(gid_inner),
- method_map_to_machine_group,
- SD_BUS_VTABLE_UNPRIVILEGED),
-
- SD_BUS_SIGNAL_WITH_NAMES("MachineNew",
- "so",
- SD_BUS_PARAM(machine)
- SD_BUS_PARAM(path),
- 0),
- SD_BUS_SIGNAL_WITH_NAMES("MachineRemoved",
- "so",
- SD_BUS_PARAM(machine)
- SD_BUS_PARAM(path),
- 0),
+ SD_BUS_METHOD_WITH_ARGS("GetMachine",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("o", machine),
+ method_get_machine,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetImage",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("o", image),
+ method_get_image,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetMachineByPID",
+ SD_BUS_ARGS("u", pid),
+ SD_BUS_RESULT("o", machine),
+ method_get_machine_by_pid,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ListMachines",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("a(ssso)", machines),
+ method_list_machines,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ListImages",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("a(ssbttto)", images),
+ method_list_images,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("CreateMachine",
+ SD_BUS_ARGS("s", name, "ay", id, "s", service, "s", class, "u", leader, "s", root_directory, "a(sv)", scope_properties),
+ SD_BUS_RESULT("o", path),
+ method_create_machine, 0),
+ SD_BUS_METHOD_WITH_ARGS("CreateMachineWithNetwork",
+ SD_BUS_ARGS("s", name, "ay", id, "s", service, "s", class, "u", leader, "s", root_directory, "ai", ifindices, "a(sv)", scope_properties),
+ SD_BUS_RESULT("o", path),
+ method_create_machine_with_network, 0),
+ SD_BUS_METHOD_WITH_ARGS("RegisterMachine",
+ SD_BUS_ARGS("s", name, "ay", id, "s", service, "s", class, "u", leader, "s", root_directory),
+ SD_BUS_RESULT("o", path),
+ method_register_machine, 0),
+ SD_BUS_METHOD_WITH_ARGS("RegisterMachineWithNetwork",
+ SD_BUS_ARGS("s", name, "ay", id, "s", service, "s", class, "u", leader, "s", root_directory, "ai", ifindices),
+ SD_BUS_RESULT("o", path),
+ method_register_machine_with_network, 0),
+ SD_BUS_METHOD_WITH_ARGS("UnregisterMachine",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_NO_RESULT,
+ method_unregister_machine,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("TerminateMachine",
+ SD_BUS_ARGS("s", id),
+ SD_BUS_NO_RESULT,
+ method_terminate_machine,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("KillMachine",
+ SD_BUS_ARGS("s", name, "s", who, "i", signal),
+ SD_BUS_NO_RESULT,
+ method_kill_machine,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetMachineAddresses",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("a(iay)", addresses),
+ method_get_machine_addresses,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetMachineOSRelease",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("a{ss}", fields),
+ method_get_machine_os_release,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("OpenMachinePTY",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("h", pty, "s", pty_path),
+ method_open_machine_pty,
+ 0),
+ SD_BUS_METHOD_WITH_ARGS("OpenMachineLogin",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("h", pty, "s", pty_path),
+ method_open_machine_login,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("OpenMachineShell",
+ SD_BUS_ARGS("s", name, "s", user, "s", path, "as", args, "as", environment),
+ SD_BUS_RESULT("h", pty, "s", pty_path),
+ method_open_machine_shell,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("BindMountMachine",
+ SD_BUS_ARGS("s", name, "s", source, "s", destination, "b", read_only, "b", mkdir),
+ SD_BUS_NO_RESULT,
+ method_bind_mount_machine,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("CopyFromMachine",
+ SD_BUS_ARGS("s", name, "s", source, "s", destination),
+ SD_BUS_NO_RESULT,
+ method_copy_machine,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("CopyToMachine",
+ SD_BUS_ARGS("s", name, "s", source, "s", destination),
+ SD_BUS_NO_RESULT,
+ method_copy_machine,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("OpenMachineRootDirectory",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("h", fd),
+ method_open_machine_root_directory,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetMachineUIDShift",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("u", shift),
+ method_get_machine_uid_shift,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("RemoveImage",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_NO_RESULT,
+ method_remove_image,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("RenameImage",
+ SD_BUS_ARGS("s", name, "s", new_name),
+ SD_BUS_NO_RESULT,
+ method_rename_image,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("CloneImage",
+ SD_BUS_ARGS("s", name, "s", new_name, "b", read_only),
+ SD_BUS_NO_RESULT,
+ method_clone_image,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("MarkImageReadOnly",
+ SD_BUS_ARGS("s", name, "b", read_only),
+ SD_BUS_NO_RESULT,
+ method_mark_image_read_only,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetImageHostname",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("s", hostname),
+ method_get_image_hostname,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetImageMachineID",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("ay", id),
+ method_get_image_machine_id,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetImageMachineInfo",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("a{ss}", machine_info),
+ method_get_image_machine_info,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("GetImageOSRelease",
+ SD_BUS_ARGS("s", name),
+ SD_BUS_RESULT("a{ss}", os_release),
+ method_get_image_os_release,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetPoolLimit",
+ SD_BUS_ARGS("t", size),
+ SD_BUS_NO_RESULT,
+ method_set_pool_limit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetImageLimit",
+ SD_BUS_ARGS("s", name, "t", size),
+ SD_BUS_NO_RESULT,
+ method_set_image_limit,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("CleanPool",
+ SD_BUS_ARGS("s", mode),
+ SD_BUS_RESULT("a(st)",images),
+ method_clean_pool,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("MapFromMachineUser",
+ SD_BUS_ARGS("s", name, "u", uid_inner),
+ SD_BUS_RESULT("u", uid_outer),
+ method_map_from_machine_user,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("MapToMachineUser",
+ SD_BUS_ARGS("u", uid_outer),
+ SD_BUS_RESULT("s", machine_name, "o", machine_path, "u", uid_inner),
+ method_map_to_machine_user,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("MapFromMachineGroup",
+ SD_BUS_ARGS("s", name, "u", gid_inner),
+ SD_BUS_RESULT("u", gid_outer),
+ method_map_from_machine_group,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("MapToMachineGroup",
+ SD_BUS_ARGS("u", gid_outer),
+ SD_BUS_RESULT("s", machine_name, "o", machine_path, "u", gid_inner),
+ method_map_to_machine_group,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+
+ SD_BUS_SIGNAL_WITH_ARGS("MachineNew",
+ SD_BUS_ARGS("s", machine, "o", path),
+ 0),
+ SD_BUS_SIGNAL_WITH_ARGS("MachineRemoved",
+ SD_BUS_ARGS("s", machine, "o", path),
+ 0),
SD_BUS_VTABLE_END
};
#include "memory-util.h"
#include "netlink-util.h"
#include "networkd-manager.h"
-#include "path-util.h"
+#include "parse-helpers.h"
#include "socket-util.h"
#include "string-table.h"
#include "string-util.h"
#include "networkd-route-util.h"
#include "networkd-route.h"
#include "networkd-util.h"
+#include "parse-helpers.h"
#include "parse-util.h"
-#include "path-util.h"
#include "random-util.h"
#include "resolve-private.h"
#include "string-util.h"
return log_error_errno(r, "Could not enumerate links: %m");
r = manager_enumerate_qdisc(m);
- if (r < 0)
+ if (r == -EOPNOTSUPP)
+ log_debug_errno(r, "Could not enumerate QDiscs, ignoring: %m");
+ else if (r < 0)
return log_error_errno(r, "Could not enumerate QDisc: %m");
r = manager_enumerate_tclass(m);
- if (r < 0)
+ if (r == -EOPNOTSUPP)
+ log_debug_errno(r, "Could not enumerate TClasses, ignoring: %m");
+ else if (r < 0)
return log_error_errno(r, "Could not enumerate TClass: %m");
r = manager_enumerate_addresses(m);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
#include <inttypes.h>
'nspawn-setuid.h',
'nspawn-stub-pid1.c',
'nspawn-stub-pid1.h',
+ 'nspawn-util.c',
+ 'nspawn-util.h',
'nspawn.h',
)
libshared],
[libseccomp]],
+ [files('test-nspawn-util.c'),
+ [libnspawn_core,
+ libshared],
+ [libseccomp]],
+
[files('test-patch-uid.c'),
[libnspawn_core,
libshared],
#include "bus-util.h"
#include "cap-list.h"
#include "cpu-set-util.h"
+#include "devnum-util.h"
#include "env-util.h"
#include "format-util.h"
#include "fs-util.h"
#if HAVE_SECCOMP
#include "seccomp-util.h"
#endif
-#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "glob-util.h"
+#include "log.h"
+#include "nspawn-util.h"
+#include "parse-util.h"
+#include "path-util.h"
+#include "string-util.h"
+
+int systemd_installation_has_version(const char *root, const char *minimal_version) {
+ int r;
+
+ /* Try to guess if systemd installation is later than the specified version. This
+ * is hacky and likely to yield false negatives, particularly if the installation
+ * is non-standard. False positives should be relatively rare.
+ */
+
+ FOREACH_STRING(pattern,
+ /* /lib works for systems without usr-merge, and for systems with a sane
+ * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary
+ * for Gentoo which does a merge without making /lib a symlink.
+ */
+ "/lib/systemd/libsystemd-shared-*.so",
+ "/lib64/systemd/libsystemd-shared-*.so",
+ "/usr/lib/systemd/libsystemd-shared-*.so",
+ "/usr/lib64/systemd/libsystemd-shared-*.so") {
+
+ _cleanup_strv_free_ char **names = NULL;
+ _cleanup_free_ char *path = NULL;
+ char *c;
+
+ path = path_join(root, pattern);
+ if (!path)
+ return -ENOMEM;
+
+ r = glob_extend(&names, path, 0);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return r;
+
+ assert_se(c = endswith(path, "*.so"));
+ *c = '\0'; /* truncate the glob part */
+
+ STRV_FOREACH(name, names) {
+ /* This is most likely to run only once, hence let's not optimize anything. */
+ char *t, *t2;
+
+ t = startswith(*name, path);
+ if (!t)
+ continue;
+
+ t2 = endswith(t, ".so");
+ if (!t2)
+ continue;
+ *t2 = '\0';
+
+ r = strverscmp_improved(t, minimal_version);
+ log_debug("Found libsystemd shared at \"%s.so\", version %s (%s).",
+ *name, t,
+ r >= 0 ? "OK" : "too old");
+ if (r >= 0)
+ return true;
+ }
+ }
+
+ return false;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int systemd_installation_has_version(const char *root, const char *minimal_version);
#include "nspawn-settings.h"
#include "nspawn-setuid.h"
#include "nspawn-stub-pid1.h"
+#include "nspawn-util.h"
#include "nspawn.h"
#include "nulstr-util.h"
#include "os-util.h"
#include "pager.h"
#include "parse-argument.h"
#include "parse-util.h"
-#include "path-util.h"
#include "pretty-print.h"
#include "process-util.h"
#include "ptyfwd.h"
if (r > 0) {
/* Unified cgroup hierarchy support was added in 230. Unfortunately the detection
* routine only detects 231, so we'll have a false negative here for 230. */
- r = systemd_installation_has_version(directory, 230);
+ r = systemd_installation_has_version(directory, "230");
if (r < 0)
return log_error_errno(r, "Failed to determine systemd version in container: %m");
if (r > 0)
arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE;
} else if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0) {
/* Mixed cgroup hierarchy support was added in 233 */
- r = systemd_installation_has_version(directory, 233);
+ r = systemd_installation_has_version(directory, "233");
if (r < 0)
return log_error_errno(r, "Failed to determine systemd version in container: %m");
if (r > 0)
goto finish;
}
+ /* Take a LOCK_SH lock on the device, so that udevd doesn't issue BLKRRPART in our back */
+ r = loop_device_flock(loop, LOCK_SH);
+ if (r < 0) {
+ log_error_errno(r, "Failed to take lock on loopback block device: %m");
+ goto finish;
+ }
+
r = dissect_image_and_warn(
loop->fd,
arg_image,
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "nspawn-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "tests.h"
+
+TEST(systemd_installation_has_version) {
+ int r;
+
+ FOREACH_STRING(version, "0", "231", STRINGIFY(PROJECT_VERSION), "999") {
+ r = systemd_installation_has_version(saved_argv[1], version);
+ assert_se(r >= 0);
+ log_info("%s has systemd >= %s: %s",
+ saved_argv[1] ?: "Current installation", version, yes_no(r));
+ }
+}
+
+/* This program can be called with a path to an installation root.
+ * For example: build/test-nspawn-util /var/lib/machines/rawhide
+ */
+DEFINE_TEST_MAIN(LOG_DEBUG);
#include "btrfs-util.h"
#include "cryptsetup-util.h"
#include "device-nodes.h"
+#include "devnum-util.h"
#include "dissect-image.h"
#include "escape.h"
#include "fd-util.h"
#include <sys/ioctl.h>
#include <sys/stat.h>
+#include "sd-device.h"
#include "sd-id128.h"
#include "alloc-util.h"
#include "conf-parser.h"
#include "cryptsetup-util.h"
#include "def.h"
+#include "devnum-util.h"
#include "dirent-util.h"
#include "efivars.h"
#include "errno-util.h"
#include "mount-util.h"
#include "mountpoint-util.h"
#include "parse-argument.h"
-#include "parse-util.h"
-#include "path-util.h"
+#include "parse-helpers.h"
#include "pretty-print.h"
#include "proc-cmdline.h"
#include "process-util.h"
#include "resize-fs.h"
#include "sort-util.h"
#include "specifier.h"
-#include "stat-util.h"
#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
sd_id128_t *ret_uuid) {
_cleanup_(blkid_free_probep) blkid_probe b = NULL;
- _cleanup_free_ char *p = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
_cleanup_close_ int fd = -1;
- const char *pttype, *t;
+ const char *pttype, *t, *p;
sd_id128_t pt_parsed, u;
blkid_partition pp;
dev_t whole_devno;
blkid_partlist pl;
- struct stat st;
int r;
/* Checks if the specified partition has the specified GPT type UUID, and is located on the specified
major(partition_devno), minor(partition_devno),
major(restrict_devno), minor(restrict_devno));
- r = device_path_make_major_minor(S_IFBLK, whole_devno, &p);
+ r = sd_device_new_from_devnum(&dev, 'b', whole_devno);
if (r < 0)
- return log_error_errno(r, "Failed to convert block device to device node path: %m");
-
- fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
- if (fd < 0)
- return log_error_errno(r, "Failed to open '%s': %m", p);
+ return log_error_errno(r, "Failed to create sd-device for block device %u:%u: %m",
+ major(whole_devno), minor(whole_devno));
- if (fstat(fd, &st) < 0)
- return log_error_errno(r, "Failed to stat '%s': %m", p);
+ r = sd_device_get_devname(dev, &p);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get name of block device %u:%u: %m",
+ major(whole_devno), minor(whole_devno));
- if (!S_ISBLK(st.st_mode) || st.st_rdev != whole_devno)
- return log_error_errno(
- SYNTHETIC_ERRNO(EPERM),
- "Opened and determined block device don't match, refusing.");
+ fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to open block device %s: %m", p);
b = blkid_new_probe();
if (!b)
sd_id128_t type_uuid,
const char *root,
dev_t restrict_devno,
- char **ret_path,
+ dev_t *ret_devno,
sd_id128_t *ret_uuid) {
const char *try1 = NULL, *try2 = NULL;
dev_t devno, found = 0;
int r;
- assert(ret_path);
-
/* Enforce some security restrictions: CopyBlocks=auto should not be an avenue to get outside of the
* --root=/--image= confinement. Specifically, refuse CopyBlocks= in combination with --root= at all,
* and restrict block device references in the --image= case to loopback block device we set up.
if (r < 0)
return log_error_errno(r, "Failed to read %s: %m", q);
- r = parse_dev(t, &sl);
+ r = parse_devnum(t, &sl);
if (r < 0) {
log_debug_errno(r, "Failed to parse %s, ignoring: %m", q);
continue;
return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
"Unable to automatically discover suitable partition to copy blocks from.");
- r = device_path_make_major_minor(S_IFBLK, found, ret_path);
- if (r < 0)
- return log_error_errno(r, "Failed to convert dev_t to device node path: %m");
+ if (ret_devno)
+ *ret_devno = found;
if (ret_uuid)
*ret_uuid = found_uuid;
"Copying from block device node is not permitted in --image=/--root= mode, refusing.");
} else if (p->copy_blocks_auto) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ const char *devname;
+ dev_t devno;
- r = resolve_copy_blocks_auto(p->type_uuid, root, restrict_devno, &opened, &uuid);
+ r = resolve_copy_blocks_auto(p->type_uuid, root, restrict_devno, &devno, &uuid);
if (r < 0)
return r;
- source_fd = open(opened, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ r = sd_device_new_from_devnum(&dev, 'b', devno);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create sd-device object for device %u:%u: %m", major(devno), minor(devno));
+
+ r = sd_device_get_devname(dev, &devname);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get device name of %u:%u: %m", major(devno), minor(devno));
+
+ opened = strdup(devname);
+ if (!opened)
+ return log_oom();
+
+ source_fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (source_fd < 0)
- return log_error_errno(errno, "Failed to open automatically determined source block copy device '%s': %m", opened);
+ return log_error_errno(source_fd, "Failed to open automatically determined source block copy device '%s': %m", opened);
if (fstat(source_fd, &st) < 0)
return log_error_errno(errno, "Failed to stat block copy file '%s': %m", opened);
-
- /* If we found it automatically, it must be a block device, let's enforce that */
- if (!S_ISBLK(st.st_mode))
- return log_error_errno(SYNTHETIC_ERRNO(EBADF),
- "Automatically detected source block copy device '%s' is not a block device, refusing: %m", opened);
- } else
+ } else
continue;
if (S_ISDIR(st.st_mode)) {
- _cleanup_free_ char *bdev = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ const char *bdev;
/* If the file is a directory, automatically find the backing block device */
if (major(st.st_dev) != 0)
- r = device_path_make_major_minor(S_IFBLK, st.st_dev, &bdev);
+ r = sd_device_new_from_devnum(&dev, 'b', st.st_dev);
else {
dev_t devt;
if (r < 0)
return log_error_errno(r, "Unable to determine backing block device of '%s': %m", opened);
- r = device_path_make_major_minor(S_IFBLK, devt, &bdev);
+ r = sd_device_new_from_devnum(&dev, 'b', devt);
}
if (r < 0)
- return log_error_errno(r, "Failed to determine block device path for block device backing '%s': %m", opened);
+ return log_error_errno(r, "Failed to create sd-device object for block device backing '%s': %m", opened);
+
+ r = sd_device_get_devpath(dev, &bdev);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get device name for block device backing '%s': %m", opened);
safe_close(source_fd);
- source_fd = open(bdev, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ source_fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (source_fd < 0)
- return log_error_errno(errno, "Failed to open block device '%s': %m", bdev);
+ return log_error_errno(source_fd, "Failed to open block device '%s': %m", bdev);
if (fstat(source_fd, &st) < 0)
return log_error_errno(errno, "Failed to stat block device '%s': %m", bdev);
-
- if (!S_ISBLK(st.st_mode))
- return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Block device '%s' is not actually a block device, refusing.", bdev);
}
if (S_ISREG(st.st_mode))
echo "### Testing systemd-repart --empty=create ###"
-"$repart" "$D/zzz" --empty=create --size=1G --seed="$SEED"
+"$repart" "$D/zzz" --empty=create --size=1G --seed="$SEED" --no-pager
sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/empty"
PaddingMinBytes=92M
EOF
-"$repart" "$D/zzz" --dry-run=no --seed="$SEED" --definitions="$D/definitions"
+"$repart" "$D/zzz" --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager
sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/populated"
echo "Label=ignored_label" >>"$D/definitions/home.conf"
echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >>"$D/definitions/home.conf"
-"$repart" "$D/zzz" --dry-run=no --seed="$SEED" --definitions="$D/definitions"
+"$repart" "$D/zzz" --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager
sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/populated2"
echo "### Resizing to 2G ###"
-"$repart" "$D/zzz" --size=2G --dry-run=no --seed="$SEED" --definitions="$D/definitions"
+"$repart" "$D/zzz" --size=2G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager
sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/populated3"
CopyBlocks=$D/block-copy
EOF
-"$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions"
+"$repart" "$D/zzz" --size=3G --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager
sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/populated4"
SizeMinBytes=48M
EOF
- "$repart" "$D/zzz" --size=auto --dry-run=no --seed="$SEED" --definitions="$D/definitions"
+ "$repart" "$D/zzz" --size=auto --dry-run=no --seed="$SEED" --definitions="$D/definitions" --no-pager
sfdisk -d "$D/zzz" | grep -v -e 'sector-size' -e '^$' >"$D/populated5"
/* We now have a loopback block device, let's fork off a child in its own mount namespace, mount it
* there, and extract the metadata we need. The metadata is sent from the child back to us. */
+ r = loop_device_flock(d, LOCK_SH);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to acquire lock on loopback block device: %m");
+
BLOCK_SIGNALS(SIGCHLD);
r = mkdtemp_malloc("/tmp/inspect-XXXXXX", &tmpdir);
{ 'a', specifier_architecture, NULL },
{ 'b', specifier_boot_id, NULL },
{ 'B', specifier_os_build_id, NULL },
- { 'H', specifier_host_name, NULL }, /* We will use specifier_dnssd_host_name(). */
+ { 'H', specifier_hostname, NULL }, /* We will use specifier_dnssd_hostname(). */
{ 'm', specifier_machine_id, NULL },
{ 'o', specifier_os_id, NULL },
{ 'v', specifier_kernel_release, NULL },
} else
changed = false;
- return changed || dns_search_domain_unlink_marked(next);
+ return dns_search_domain_unlink_marked(next) || changed;
}
void dns_search_domain_mark_all(DnsSearchDomain *first) {
return 0;
}
-static int specifier_dnssd_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+static int specifier_dnssd_hostname(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
DnssdService *s = (DnssdService *) userdata;
char *n;
int dnssd_render_instance_name(DnssdService *s, char **ret_name) {
static const Specifier specifier_table[] = {
- { 'a', specifier_architecture, NULL },
- { 'b', specifier_boot_id, NULL },
- { 'B', specifier_os_build_id, NULL },
- { 'H', specifier_dnssd_host_name, NULL },
- { 'm', specifier_machine_id, NULL },
- { 'o', specifier_os_id, NULL },
- { 'v', specifier_kernel_release, NULL },
- { 'w', specifier_os_version_id, NULL },
- { 'W', specifier_os_variant_id, NULL },
+ { 'a', specifier_architecture, NULL },
+ { 'b', specifier_boot_id, NULL },
+ { 'B', specifier_os_build_id, NULL },
+ { 'H', specifier_dnssd_hostname, NULL },
+ { 'm', specifier_machine_id, NULL },
+ { 'o', specifier_os_id, NULL },
+ { 'v', specifier_kernel_release, NULL },
+ { 'w', specifier_os_version_id, NULL },
+ { 'W', specifier_os_variant_id, NULL },
{}
};
_cleanup_free_ char *name = NULL;
#include "alloc-util.h"
#include "blockdev-util.h"
#include "btrfs-util.h"
+#include "devnum-util.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "missing_magic.h"
#include "parse-util.h"
-#include "stat-util.h"
int block_get_whole_disk(dev_t d, dev_t *ret) {
char p[SYS_BLOCK_PATH_MAX("/partition")];
if (r < 0)
return r;
- r = parse_dev(s, &devt);
+ r = parse_devnum(s, &devt);
if (r < 0)
return r;
if (r < 0)
return r;
- r = parse_dev(t, &devt);
+ r = parse_devnum(t, &devt);
if (r < 0)
return -EINVAL;
#include "bootspec.h"
#include "bootspec-fundamental.h"
#include "conf-files.h"
+#include "devnum-util.h"
#include "dirent-util.h"
#include "efi-loader.h"
#include "env-file.h"
#include "pe-header.h"
#include "recurse-dir.h"
#include "sort-util.h"
-#include "stat-util.h"
#include "strv.h"
#include "unaligned.h"
return r; /* It's fine if the XBOOTLDR partition doesn't exist, hence we ignore ENOKEY here */
/* If both paths actually refer to the same inode, suppress the xbootldr path */
- if (esp_where && xbootldr_where && devid_set_and_equal(esp_devid, xbootldr_devid))
+ if (esp_where && xbootldr_where && devnum_set_and_equal(esp_devid, xbootldr_devid))
xbootldr_where = mfree(xbootldr_where);
return boot_config_load(config, esp_where, xbootldr_where);
* prepared with btrfs_subvol_auto_qgroup_fd() with
* insert_intermediary_qgroup=true (or equivalent). For others
* it will return the leaf qgroup instead. The two cases may
- * be distuingished via the return value, which is 1 in case
+ * be distinguished via the return value, which is 1 in case
* an appropriate "subtree" qgroup was found, and 0
* otherwise. */
#include "mountpoint-util.h"
#include "nsflags.h"
#include "numa-util.h"
-#include "parse-socket-bind-item.h"
+#include "parse-helpers.h"
#include "parse-util.h"
#include "path-util.h"
#include "percent-util.h"
#include "macro.h"
#include "missing_network.h"
#include "nulstr-util.h"
+#include "parse-helpers.h"
#include "parse-util.h"
#include "path-util.h"
#include "percent-util.h"
if (r < 0)
return r;
+ /* Make sure udevd doesn't issue BLKRRPART in the background which might make our partitions
+ * disappear temporarily. */
+ r = loop_device_flock(d, LOCK_SH);
+ if (r < 0)
+ return r;
+
r = dissect_image(
d->fd,
NULL, NULL,
#include <valgrind/memcheck.h>
#endif
+#include <linux/blkpg.h>
#include <linux/dm-ioctl.h>
#include <linux/loop.h>
#include <sys/mount.h>
}
#if HAVE_BLKID
-static int enumerator_for_parent(sd_device *d, sd_device_enumerator **ret) {
- _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
- int r;
-
- assert(d);
- assert(ret);
-
- r = sd_device_enumerator_new(&e);
- if (r < 0)
- return r;
-
- r = sd_device_enumerator_add_match_subsystem(e, "block", true);
- if (r < 0)
- return r;
-
- r = sd_device_enumerator_add_match_parent(e, d);
- if (r < 0)
- return r;
-
- r = sd_device_enumerator_add_match_sysattr(e, "partition", NULL, true);
- if (r < 0)
- return r;
-
- *ret = TAKE_PTR(e);
- return 0;
-}
-
-static int device_is_partition(
- sd_device *d,
- sd_device *expected_parent,
- blkid_partition pp) {
-
- const char *v, *parent_syspath, *expected_parent_syspath;
- blkid_loff_t bsize, bstart;
- uint64_t size, start;
- int partno, bpartno, r;
- sd_device *parent;
-
- assert(d);
- assert(expected_parent);
- assert(pp);
-
- r = sd_device_get_subsystem(d, &v);
- if (r < 0)
- return r;
- if (!streq(v, "block"))
- return false;
-
- if (sd_device_get_devtype(d, &v) < 0 || !streq(v, "partition"))
- return false;
-
- r = sd_device_get_parent(d, &parent);
- if (r < 0)
- return false; /* Doesn't have a parent? No relevant to us */
-
- r = sd_device_get_syspath(parent, &parent_syspath); /* Check parent of device of this action */
- if (r < 0)
- return r;
-
- r = sd_device_get_syspath(expected_parent, &expected_parent_syspath); /* Check parent of device we are looking for */
- if (r < 0)
- return r;
-
- if (!path_equal(parent_syspath, expected_parent_syspath))
- return false; /* Has a different parent than what we need, not interesting to us */
-
- /* On kernel uevents we may find the partition number in the PARTN= field. Let's use that preferably,
- * since it's cheaper and more importantly: the sysfs attribute "partition" appears to become
- * available late, hence let's use the property instead, which is available at the moment we see the
- * uevent. */
- r = sd_device_get_property_value(d, "PARTN", &v);
- if (r == -ENOENT)
- r = sd_device_get_sysattr_value(d, "partition", &v);
- if (r < 0)
- return r;
-
- r = safe_atoi(v, &partno);
- if (r < 0)
- return r;
-
- errno = 0;
- bpartno = blkid_partition_get_partno(pp);
- if (bpartno < 0)
- return errno_or_else(EIO);
-
- if (partno != bpartno)
- return false;
-
- r = sd_device_get_sysattr_value(d, "start", &v);
- if (r < 0)
- return r;
- r = safe_atou64(v, &start);
- if (r < 0)
- return r;
-
- errno = 0;
- bstart = blkid_partition_get_start(pp);
- if (bstart < 0)
- return errno_or_else(EIO);
-
- if (start != (uint64_t) bstart)
- return false;
-
- r = sd_device_get_sysattr_value(d, "size", &v);
- if (r < 0)
- return r;
- r = safe_atou64(v, &size);
- if (r < 0)
- return r;
-
- errno = 0;
- bsize = blkid_partition_get_size(pp);
- if (bsize < 0)
- return errno_or_else(EIO);
-
- if (size != (uint64_t) bsize)
- return false;
-
- return true;
-}
-
-static int find_partition(
- sd_device *parent,
- blkid_partition pp,
- usec_t timestamp_not_before,
- DissectImageFlags flags,
- sd_device **ret) {
-
- _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
- sd_device *q;
- int r;
-
- assert(parent);
- assert(pp);
- assert(ret);
-
- r = enumerator_for_parent(parent, &e);
- if (r < 0)
- return r;
-
- FOREACH_DEVICE(e, q) {
- uint64_t usec;
-
- if (!FLAGS_SET(flags, DISSECT_IMAGE_NO_UDEV)) {
- r = sd_device_get_usec_initialized(q, &usec);
- if (r == -EBUSY) /* Not initialized yet */
- continue;
- if (r < 0)
- return r;
-
- if (timestamp_not_before != USEC_INFINITY &&
- usec < timestamp_not_before) /* udev database entry older than our attachment? Then it's not ours */
- continue;
- }
-
- r = device_is_partition(q, parent, pp);
- if (r < 0)
- return r;
- if (r > 0) {
- *ret = sd_device_ref(q);
- return 0;
- }
- }
-
- return -ENXIO;
-}
-
-struct wait_data {
- sd_device *parent_device;
- blkid_partition blkidp;
- sd_device *found;
- uint64_t diskseq;
- uint64_t uevent_seqnum_not_before;
- usec_t timestamp_not_before;
- DissectImageFlags flags;
-};
-
-static inline void wait_data_done(struct wait_data *d) {
- sd_device_unref(d->found);
-}
-
-static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) {
- struct wait_data *w = userdata;
- int r;
-
- assert(w);
-
- if (device_for_action(device, SD_DEVICE_REMOVE))
- return 0;
-
- if (w->diskseq != 0) {
- uint64_t diskseq;
-
- /* If w->diskseq is non-zero, then we must have a disk seqnum */
- r = sd_device_get_diskseq(device, &diskseq);
- if (r < 0) {
- log_debug_errno(r, "Dropping event because it has no diskseq, but waiting for %" PRIu64, w->diskseq);
- return 0;
- }
- if (diskseq < w->diskseq) {
- log_debug("Dropping event because diskseq too old (%" PRIu64 " < %" PRIu64 ")",
- diskseq, w->diskseq);
- return 0;
- }
- if (diskseq > w->diskseq) {
- r = -EBUSY;
- goto finish; /* Newer than what we were expecting, so we missed it, stop waiting */
- }
- } else if (w->uevent_seqnum_not_before != UINT64_MAX) {
- uint64_t seqnum;
-
- r = sd_device_get_seqnum(device, &seqnum);
- if (r < 0)
- goto finish;
-
- if (seqnum <= w->uevent_seqnum_not_before) { /* From an older use of this loop device */
- log_debug("Dropping event because seqnum too old (%" PRIu64 " <= %" PRIu64 ")",
- seqnum, w->uevent_seqnum_not_before);
- return 0;
- }
- }
-
- r = device_is_partition(device, w->parent_device, w->blkidp);
- if (r < 0)
- goto finish;
- if (r == 0) /* Not the one we need */
- return 0;
-
- /* It's the one we need! Yay! */
- assert(!w->found);
- w->found = sd_device_ref(device);
- r = 0;
-
-finish:
- return sd_event_exit(sd_device_monitor_get_event(monitor), r);
-}
-
-static int timeout_handler(sd_event_source *s, uint64_t usec, void *userdata) {
- struct wait_data *w = userdata;
- int r;
-
- assert(w);
-
- /* Why partition not appeared within the timeout? We may lost some uevent, as some properties
- * were not ready when we received uevent... Not sure, but anyway, let's try to find the
- * partition again before give up. */
-
- r = find_partition(w->parent_device, w->blkidp, w->timestamp_not_before, w->flags, &w->found);
- if (r == -ENXIO)
- return log_debug_errno(SYNTHETIC_ERRNO(ETIMEDOUT),
- "Partition still not appeared after timeout reached.");
- if (r < 0)
- return log_debug_errno(r, "Failed to find partition: %m");
-
- log_debug("Partition appeared after timeout reached.");
- return sd_event_exit(sd_event_source_get_event(s), 0);
-}
-
-static int retry_handler(sd_event_source *s, uint64_t usec, void *userdata) {
- struct wait_data *w = userdata;
- int r;
-
- assert(w);
-
- r = find_partition(w->parent_device, w->blkidp, w->timestamp_not_before, w->flags, &w->found);
- if (r != -ENXIO) {
- if (r < 0)
- return log_debug_errno(r, "Failed to find partition: %m");
-
- log_debug("Partition found by a periodic search.");
- return sd_event_exit(sd_event_source_get_event(s), 0);
- }
-
- r = sd_event_source_set_time_relative(s, 500 * USEC_PER_MSEC);
- if (r < 0)
- return r;
-
- return sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
-}
-
-static int wait_for_partition_device(
- sd_device *parent,
- blkid_partition pp,
- usec_t deadline,
- uint64_t diskseq,
- uint64_t uevent_seqnum_not_before,
- usec_t timestamp_not_before,
- DissectImageFlags flags,
- sd_device **ret) {
-
- _cleanup_(sd_event_source_unrefp) sd_event_source *timeout_source = NULL, *retry_source = NULL;
- _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
- _cleanup_(sd_event_unrefp) sd_event *event = NULL;
- int r;
-
- assert(parent);
- assert(pp);
- assert(ret);
-
- r = find_partition(parent, pp, timestamp_not_before, flags, ret);
- if (r != -ENXIO)
- return r;
-
- r = sd_event_new(&event);
- if (r < 0)
- return r;
-
- r = sd_device_monitor_new(&monitor);
- if (r < 0)
- return r;
-
- r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, "block", "partition");
- if (r < 0)
- return r;
-
- r = sd_device_monitor_filter_add_match_parent(monitor, parent, true);
- if (r < 0)
- return r;
-
- r = sd_device_monitor_filter_add_match_sysattr(monitor, "partition", NULL, true);
- if (r < 0)
- return r;
-
- r = sd_device_monitor_attach_event(monitor, event);
- if (r < 0)
- return r;
-
- _cleanup_(wait_data_done) struct wait_data w = {
- .parent_device = parent,
- .blkidp = pp,
- .diskseq = diskseq,
- .uevent_seqnum_not_before = uevent_seqnum_not_before,
- .timestamp_not_before = timestamp_not_before,
- .flags = flags,
- };
-
- r = sd_device_monitor_start(monitor, device_monitor_handler, &w);
- if (r < 0)
- return r;
-
- /* Check again, the partition might have appeared in the meantime */
- r = find_partition(parent, pp, timestamp_not_before, flags, ret);
- if (r != -ENXIO)
- return r;
-
- if (deadline != USEC_INFINITY) {
- r = sd_event_add_time(
- event, &timeout_source,
- CLOCK_MONOTONIC, deadline, 0,
- timeout_handler, &w);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_exit_on_failure(timeout_source, true);
- if (r < 0)
- return r;
- }
-
- /* If we don't have a disk sequence number then we cannot do exact matching,
- * and we cannot know if we missed it or if it has not been sent yet, so set
- * up additional retries to increase the chances of receiving the event. */
- if (diskseq == 0) {
- r = sd_event_add_time_relative(
- event, &retry_source,
- CLOCK_MONOTONIC, 500 * USEC_PER_MSEC, 0,
- retry_handler, &w);
- if (r < 0)
- return r;
-
- r = sd_event_source_set_exit_on_failure(retry_source, true);
- if (r < 0)
- return r;
- }
-
- r = sd_event_loop(event);
- if (r < 0)
- return r;
-
- assert(w.found);
- *ret = TAKE_PTR(w.found);
- return 0;
-}
-
static void check_partition_flags(
const char *node,
unsigned long long pflags,
log_debug("Unexpected partition flag %llu set on %s!", bit, node);
}
}
+#endif
-static int device_wait_for_initialization_harder(
- sd_device *device,
- const char *subsystem,
- usec_t deadline,
- sd_device **ret) {
-
- usec_t start, left, retrigger_timeout;
- int r;
-
- start = now(CLOCK_MONOTONIC);
- left = usec_sub_unsigned(deadline, start);
+static void dissected_partition_done(DissectedPartition *p) {
+ assert(p);
- if (DEBUG_LOGGING) {
- const char *sn = NULL;
+ free(p->fstype);
+ free(p->node);
+ free(p->label);
+ free(p->decrypted_fstype);
+ free(p->decrypted_node);
+ free(p->mount_options);
- (void) sd_device_get_sysname(device, &sn);
- log_device_debug(device,
- "Will wait up to %s for '%s' to initialize…", FORMAT_TIMESPAN(left, 0), strna(sn));
- }
+ *p = (DissectedPartition) {
+ .partno = -1,
+ .architecture = _ARCHITECTURE_INVALID,
+ };
+}
- if (left != USEC_INFINITY)
- retrigger_timeout = CLAMP(left / 4, 1 * USEC_PER_SEC, 5 * USEC_PER_SEC); /* A fourth of the total timeout, but let's clamp to 1s…5s range */
- else
- retrigger_timeout = 2 * USEC_PER_SEC;
+#if HAVE_BLKID
+static int ioctl_partition_add(
+ int fd,
+ const char *name,
+ int nr,
+ uint64_t start,
+ uint64_t size) {
- for (;;) {
- usec_t local_deadline, n;
- bool last_try;
+ assert(fd >= 0);
+ assert(name);
+ assert(nr > 0);
- n = now(CLOCK_MONOTONIC);
- assert(n >= start);
+ struct blkpg_partition bp = {
+ .pno = nr,
+ .start = start,
+ .length = size,
+ };
- /* Find next deadline, when we'll retrigger */
- local_deadline = start +
- DIV_ROUND_UP(n - start, retrigger_timeout) * retrigger_timeout;
+ struct blkpg_ioctl_arg ba = {
+ .op = BLKPG_ADD_PARTITION,
+ .data = &bp,
+ .datalen = sizeof(bp),
+ };
- if (deadline != USEC_INFINITY && deadline <= local_deadline) {
- local_deadline = deadline;
- last_try = true;
- } else
- last_try = false;
+ if (strlen(name) >= sizeof(bp.devname))
+ return -EINVAL;
- r = device_wait_for_initialization(device, subsystem, local_deadline, ret);
- if (r >= 0 && DEBUG_LOGGING) {
- const char *sn = NULL;
+ strcpy(bp.devname, name);
- (void) sd_device_get_sysname(device, &sn);
- log_device_debug(device,
- "Successfully waited for device '%s' to initialize for %s.",
- strna(sn),
- FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC), start), 0));
+ return RET_NERRNO(ioctl(fd, BLKPG, &ba));
+}
- }
- if (r != -ETIMEDOUT || last_try)
- return r;
+static int make_partition_devname(
+ const char *whole_devname,
+ int nr,
+ char **ret) {
- if (DEBUG_LOGGING)
- log_device_debug(device,
- "Device didn't initialize within %s, assuming lost event. Retriggering device.",
- FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC), start), 0));
+ bool need_p;
- r = sd_device_trigger(device, SD_DEVICE_CHANGE);
- if (r < 0)
- return r;
- }
-}
-#endif
+ assert(whole_devname);
+ assert(nr > 0);
-#define DEVICE_TIMEOUT_USEC (45 * USEC_PER_SEC)
+ /* Given a whole block device node name (e.g. /dev/sda or /dev/loop7) generate a partition device
+ * name (e.g. /dev/sda7 or /dev/loop7p5). The rule the kernel uses is simple: if whole block device
+ * node name ends in a digit, then suffix a 'p', followed by the partition number. Otherwise, just
+ * suffix the partition number without any 'p'. */
-static void dissected_partition_done(DissectedPartition *p) {
- assert(p);
+ if (isempty(whole_devname)) /* Make sure there *is* a last char */
+ return -EINVAL;
- free(p->fstype);
- free(p->node);
- free(p->label);
- free(p->decrypted_fstype);
- free(p->decrypted_node);
- free(p->mount_options);
+ need_p = strchr(DIGITS, whole_devname[strlen(whole_devname)-1]); /* Last char a digit? */
- *p = (DissectedPartition) {
- .partno = -1,
- .architecture = _ARCHITECTURE_INVALID,
- };
+ return asprintf(ret, "%s%s%i", whole_devname, need_p ? "p" : "", nr);
}
+#endif
int dissect_image(
int fd,
_cleanup_(blkid_free_probep) blkid_probe b = NULL;
_cleanup_free_ char *generic_node = NULL;
sd_id128_t generic_uuid = SD_ID128_NULL;
- const char *pttype = NULL, *sysname = NULL;
+ const char *pttype = NULL, *sysname = NULL, *devname = NULL;
blkid_partlist pl;
int r, generic_nr = -1, n_partitions;
struct stat st;
- usec_t deadline;
assert(fd >= 0);
assert(ret);
if (r < 0)
return r;
- if (!FLAGS_SET(flags, DISSECT_IMAGE_NO_UDEV)) {
- _cleanup_(sd_device_unrefp) sd_device *initialized = NULL;
-
- /* If udev support is enabled, then let's wait for the device to be initialized before we doing anything. */
-
- r = device_wait_for_initialization_harder(
- d,
- "block",
- usec_add(now(CLOCK_MONOTONIC), DEVICE_TIMEOUT_USEC),
- &initialized);
- if (r < 0)
- return r;
-
- sd_device_unref(d);
- d = TAKE_PTR(initialized);
- }
-
b = blkid_new_probe();
if (!b)
return -ENOMEM;
if (r < 0)
return r;
}
+ r = sd_device_get_devname(d, &devname);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to get device devname: %m");
if (!image_name_is_valid(m->image_name)) {
log_debug("Image name %s is not valid, ignoring", strempty(m->image_name));
(void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
- const char *fstype = NULL, *options = NULL, *devname = NULL;
_cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
+ const char *fstype = NULL, *options = NULL;
/* OK, we have found a file system, that's our root partition then. */
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
return -ENOMEM;
}
- r = sd_device_get_devname(d, &devname);
- if (r < 0)
- return r;
-
n = strdup(devname);
if (!n)
return -ENOMEM;
if (n_partitions < 0)
return errno_or_else(EIO);
- deadline = usec_add(now(CLOCK_MONOTONIC), DEVICE_TIMEOUT_USEC);
for (int i = 0; i < n_partitions; i++) {
- _cleanup_(sd_device_unrefp) sd_device *q = NULL;
+ _cleanup_free_ char *node = NULL;
unsigned long long pflags;
blkid_loff_t start, size;
blkid_partition pp;
- const char *node;
int nr;
errno = 0;
if (!pp)
return errno_or_else(EIO);
- r = wait_for_partition_device(d, pp, deadline, diskseq, uevent_seqnum_not_before, timestamp_not_before, flags, &q);
- if (r < 0)
- return r;
-
- r = sd_device_get_devname(q, &node);
- if (r < 0)
- return r;
-
pflags = blkid_partition_get_flags(pp);
errno = 0;
assert((uint64_t) size < UINT64_MAX/512);
+ r = make_partition_devname(devname, nr, &node);
+ if (r < 0)
+ return r;
+
+ /* So here's the thing: after the main ("whole") block device popped up it might take a while
+ * before the kernel fully probed the partition table. Waiting for that to finish is icky in
+ * userspace. So here's what we do instead. We issue the BLKPG_ADD_PARTITION ioctl to add the
+ * partition ourselves, racing against the kernel. Good thing is: if this call fails with
+ * EBUSY then the kernel was quicker than us, and that's totally OK, the outcome is good for
+ * us: the device node will exist. If OTOH our call was successful we won the race. Which is
+ * also good as the outcome is the same: the partition block device exists, and we can use
+ * it.
+ *
+ * Kernel returns EBUSY if there's already a partition by that number or an overlapping
+ * partition already existent. */
+
+ r = ioctl_partition_add(fd, node, nr, (uint64_t) start * 512, (uint64_t) size * 512);
+ if (r < 0) {
+ if (r != -EBUSY)
+ return log_debug_errno(r, "BLKPG_ADD_PARTITION failed: %m");
+
+ log_debug_errno(r, "Kernel was quicker than us in adding partition %i.", nr);
+ } else
+ log_debug("We were quicker than kernel in adding partition %i.", nr);
+
if (is_gpt) {
PartitionDesignator designator = _PARTITION_DESIGNATOR_INVALID;
Architecture architecture = _ARCHITECTURE_INVALID;
(flags & DISSECT_IMAGE_GENERIC_ROOT) &&
(!verity || !verity->root_hash || verity->designator != PARTITION_USR)) {
- /* OK, we found nothing usable, then check if there's a single generic one distro, and use
+ /* OK, we found nothing usable, then check if there's a single generic partition, and use
* that. If the root hash was set however, then we won't fall back to a generic node, because
* the root hash decides. */
if (r < 0)
return log_error_errno(r, "Failed to set up loopback device for %s: %m", image);
+ /* Make sure udevd doesn't issue BLKRRPART behind our backs */
+ r = loop_device_flock(d, LOCK_SH);
+ if (r < 0)
+ return r;
+
r = dissect_image_and_warn(d->fd, image, &verity, NULL, d->diskseq, d->uevent_seqnum_not_before, d->timestamp_not_before, flags, &dissected_image);
if (r < 0)
return r;
if (r < 0)
return r;
+ r = loop_device_flock(d, LOCK_UN);
+ if (r < 0)
+ return r;
+
if (decrypted_image) {
r = decrypted_image_relinquish(decrypted_image);
if (r < 0)
if (r < 0)
return log_debug_errno(r, "Failed to create loop device for image: %m");
+ r = loop_device_flock(loop_device, LOCK_SH);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to lock loop device: %m");
+
r = dissect_image(
loop_device->fd,
&verity,
if (r < 0)
return log_debug_errno(r, "Failed to mount image: %m");
+ r = loop_device_flock(loop_device, LOCK_UN);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to unlock loopback device: %m");
+
/* If we got os-release values from the caller, then we need to match them with the image's
* extension-release.d/ content. Return -EINVAL if there's any mismatch.
* First, check the distro ID. If that matches, then check the new SYSEXT_LEVEL value if
DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY = 1 << 7, /* Mount only the non-root and non-/usr partitions */
DISSECT_IMAGE_VALIDATE_OS = 1 << 8, /* Refuse mounting images that aren't identifiable as OS images */
DISSECT_IMAGE_VALIDATE_OS_EXT = 1 << 9, /* Refuse mounting images that aren't identifiable as OS extension images */
- DISSECT_IMAGE_NO_UDEV = 1 << 10, /* Don't wait for udev initializing things */
- DISSECT_IMAGE_RELAX_VAR_CHECK = 1 << 11, /* Don't insist that the UUID of /var is hashed from /etc/machine-id */
- DISSECT_IMAGE_FSCK = 1 << 12, /* File system check the partition before mounting (no effect when combined with DISSECT_IMAGE_READ_ONLY) */
- DISSECT_IMAGE_NO_PARTITION_TABLE = 1 << 13, /* Only recognize single file system images */
- DISSECT_IMAGE_VERITY_SHARE = 1 << 14, /* When activating a verity device, reuse existing one if already open */
- DISSECT_IMAGE_MKDIR = 1 << 15, /* Make top-level directory to mount right before mounting, if missing */
- DISSECT_IMAGE_USR_NO_ROOT = 1 << 16, /* If no root fs is in the image, but /usr is, then allow this (so that we can mount the rootfs as tmpfs or so */
- DISSECT_IMAGE_REQUIRE_ROOT = 1 << 17, /* Don't accept disks without root partition (or at least /usr partition if DISSECT_IMAGE_USR_NO_ROOT is set) */
- DISSECT_IMAGE_MOUNT_READ_ONLY = 1 << 18, /* Make mounts read-only */
+ DISSECT_IMAGE_RELAX_VAR_CHECK = 1 << 10, /* Don't insist that the UUID of /var is hashed from /etc/machine-id */
+ DISSECT_IMAGE_FSCK = 1 << 11, /* File system check the partition before mounting (no effect when combined with DISSECT_IMAGE_READ_ONLY) */
+ DISSECT_IMAGE_NO_PARTITION_TABLE = 1 << 12, /* Only recognize single file system images */
+ DISSECT_IMAGE_VERITY_SHARE = 1 << 13, /* When activating a verity device, reuse existing one if already open */
+ DISSECT_IMAGE_MKDIR = 1 << 14, /* Make top-level directory to mount right before mounting, if missing */
+ DISSECT_IMAGE_USR_NO_ROOT = 1 << 15, /* If no root fs is in the image, but /usr is, then allow this (so that we can mount the rootfs as tmpfs or so */
+ DISSECT_IMAGE_REQUIRE_ROOT = 1 << 16, /* Don't accept disks without root partition (or at least /usr partition if DISSECT_IMAGE_USR_NO_ROOT is set) */
+ DISSECT_IMAGE_MOUNT_READ_ONLY = 1 << 17, /* Make mounts read-only */
DISSECT_IMAGE_READ_ONLY = DISSECT_IMAGE_DEVICE_READ_ONLY |
DISSECT_IMAGE_MOUNT_READ_ONLY,
- DISSECT_IMAGE_GROWFS = 1 << 19, /* Grow file systems in partitions marked for that to the size of the partitions after mount */
- DISSECT_IMAGE_MOUNT_IDMAPPED = 1 << 20, /* Mount mounts with kernel 5.12-style userns ID mapping, if file system type doesn't support uid=/gid= */
+ DISSECT_IMAGE_GROWFS = 1 << 18, /* Grow file systems in partitions marked for that to the size of the partitions after mount */
+ DISSECT_IMAGE_MOUNT_IDMAPPED = 1 << 19, /* Mount mounts with kernel 5.12-style userns ID mapping, if file system type doesn't support uid=/gid= */
} DissectImageFlags;
struct DissectedImage {
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <linux/magic.h>
+#include <sys/vfs.h>
#include "sd-device.h"
#include "alloc-util.h"
#include "blkid-util.h"
+#include "devnum-util.h"
#include "env-util.h"
#include "errno-util.h"
#include "find-esp.h"
#include "alloc-util.h"
#include "blockdev-util.h"
#include "device-util.h"
+#include "devnum-util.h"
#include "env-util.h"
#include "errno-util.h"
#include "fd-util.h"
r = read_one_line_file(sysfs, &buffer);
if (r < 0)
return r;
- r = parse_dev(buffer, &devno);
+ r = parse_devnum(buffer, &devno);
if (r < 0)
return r;
'pager.h',
'parse-argument.c',
'parse-argument.h',
- 'parse-socket-bind-item.c',
- 'parse-socket-bind-item.h',
+ 'parse-helpers.c',
+ 'parse-helpers.h',
'pcre2-dlopen.c',
'pcre2-dlopen.h',
'pe-header.h',
#include "af-list.h"
#include "extract-word.h"
#include "ip-protocol-list.h"
-#include "parse-socket-bind-item.h"
+#include "log.h"
+#include "parse-helpers.h"
#include "parse-util.h"
+#include "path-util.h"
+#include "utf8.h"
+
+int path_simplify_and_warn(
+ char *path,
+ unsigned flag,
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *lvalue) {
+
+ bool fatal = flag & PATH_CHECK_FATAL;
+
+ assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE));
+
+ if (!utf8_is_valid(path))
+ return log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path);
+
+ if (flag & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) {
+ bool absolute;
+
+ absolute = path_is_absolute(path);
+
+ if (!absolute && (flag & PATH_CHECK_ABSOLUTE))
+ return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
+ "%s= path is not absolute%s: %s",
+ lvalue, fatal ? "" : ", ignoring", path);
+
+ if (absolute && (flag & PATH_CHECK_RELATIVE))
+ return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
+ "%s= path is absolute%s: %s",
+ lvalue, fatal ? "" : ", ignoring", path);
+ }
+
+ path_simplify(path);
+
+ if (!path_is_valid(path))
+ return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
+ "%s= path has invalid length (%zu bytes)%s.",
+ lvalue, strlen(path), fatal ? "" : ", ignoring");
+
+ if (!path_is_normalized(path))
+ return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
+ "%s= path is not normalized%s: %s",
+ lvalue, fatal ? "" : ", ignoring", path);
+
+ return 0;
+}
static int parse_af_token(
const char *token,
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdint.h>
+
+enum {
+ PATH_CHECK_FATAL = 1 << 0, /* If not set, then error message is appended with 'ignoring'. */
+ PATH_CHECK_ABSOLUTE = 1 << 1,
+ PATH_CHECK_RELATIVE = 1 << 2,
+};
+
+int path_simplify_and_warn(
+ char *path,
+ unsigned flag,
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *lvalue);
+
+int parse_socket_bind_item(
+ const char *str,
+ int *address_family,
+ int *ip_protocol,
+ uint16_t *nr_ports,
+ uint16_t *port_min);
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#pragma once
-
-#include <stdint.h>
-
-int parse_socket_bind_item(
- const char *str,
- int *address_family,
- int *ip_protocol,
- uint16_t *nr_ports,
- uint16_t *port_min);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <sys/quota.h>
+#include <sys/stat.h>
#include "alloc-util.h"
#include "blockdev-util.h"
+#include "devnum-util.h"
#include "quota-util.h"
-#include "stat-util.h"
-int quotactl_devno(int cmd, dev_t devno, int id, void *addr) {
+int quotactl_devnum(int cmd, dev_t devnum, int id, void *addr) {
_cleanup_free_ char *devnode = NULL;
int r;
/* Like quotactl() but takes a dev_t instead of a path to a device node, and fixes caddr_t → void*,
* like we should, today */
- r = device_path_make_major_minor(S_IFBLK, devno, &devnode);
+ r = device_path_make_major_minor(S_IFBLK, devnum, &devnode);
if (r < 0)
return r;
if (devno == 0) /* Doesn't have a block device */
return -ENODEV;
- return quotactl_devno(cmd, devno, id, addr);
+ return quotactl_devnum(cmd, devno, id, addr);
}
return (int) QCMD(cmd, type);
}
-int quotactl_devno(int cmd, dev_t devno, int id, void *addr);
+int quotactl_devnum(int cmd, dev_t devnum, int id, void *addr);
int quotactl_path(int cmd, const char *path, int id, void *addr);
#include "btrfs-util.h"
#include "conf-parser.h"
#include "def.h"
+#include "devnum-util.h"
#include "env-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "log.h"
#include "macro.h"
-#include "parse-util.h"
#include "path-util.h"
#include "sleep-config.h"
#include "stat-util.h"
if (r < 0)
return log_debug_errno(r, "Error reading /sys/power/resume: %m");
- r = parse_dev(resume_str, &resume);
+ r = parse_devnum(resume_str, &resume);
if (r < 0)
return log_debug_errno(r, "Error parsing /sys/power/resume device: %s: %m", resume_str);
return 0;
}
-int specifier_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+int specifier_hostname(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
char *n;
assert(ret);
return 0;
}
-int specifier_short_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+int specifier_short_hostname(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
char *n;
assert(ret);
return 0;
}
-int specifier_pretty_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+int specifier_pretty_hostname(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
char *n = NULL;
assert(ret);
* installation. */
static int parse_os_release_specifier(const char *root, const char *id, char **ret) {
+ char *v = NULL;
int r;
assert(ret);
+ r = parse_os_release(root, id, &v);
+ if (r >= 0)
+ /* parse_os_release() calls parse_env_file() which only sets the return value for
+ * entries found. Let's make sure we set the return value in all cases. */
+ *ret = v;
+
/* Translate error for missing os-release file to EUNATCH. */
- r = parse_os_release(root, id, ret);
return r == -ENOENT ? -EUNATCH : r;
}
int specifier_machine_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
int specifier_boot_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
-int specifier_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret);
-int specifier_short_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret);
-int specifier_pretty_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_hostname(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_short_hostname(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_pretty_hostname(char specifier, const void *data, const char *root, const void *userdata, char **ret);
int specifier_kernel_release(char specifier, const void *data, const char *root, const void *userdata, char **ret);
int specifier_architecture(char specifier, const void *data, const char *root, const void *userdata, char **ret);
int specifier_os_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
* %B: the OS build ID, according to /etc/os-release
* %H: the hostname of the running system
* %l: the short hostname of the running system
+ * %q: the 'pretty' hostname as per /etc/machine-info
* %m: the machine ID of the running system
* %M: the OS image ID, according to /etc/os-release
* %o: the OS ID according to /etc/os-release
* %V: the temporary directory for large, persistent stuff (e.g. /var/tmp, or $TMPDIR, $TEMP, $TMP)
*/
-#define COMMON_SYSTEM_SPECIFIERS \
- { 'a', specifier_architecture, NULL }, \
- { 'A', specifier_os_image_version,NULL }, \
- { 'b', specifier_boot_id, NULL }, \
- { 'B', specifier_os_build_id, NULL }, \
- { 'H', specifier_host_name, NULL }, \
- { 'l', specifier_short_host_name, NULL }, \
- { 'R', specifier_pretty_host_name,NULL }, \
- { 'm', specifier_machine_id, NULL }, \
- { 'M', specifier_os_image_id, NULL }, \
- { 'o', specifier_os_id, NULL }, \
- { 'v', specifier_kernel_release, NULL }, \
- { 'w', specifier_os_version_id, NULL }, \
- { 'W', specifier_os_variant_id, NULL }
+#define COMMON_SYSTEM_SPECIFIERS \
+ { 'a', specifier_architecture, NULL }, \
+ { 'A', specifier_os_image_version, NULL }, \
+ { 'b', specifier_boot_id, NULL }, \
+ { 'B', specifier_os_build_id, NULL }, \
+ { 'H', specifier_hostname, NULL }, \
+ { 'l', specifier_short_hostname, NULL }, \
+ { 'q', specifier_pretty_hostname, NULL }, \
+ { 'm', specifier_machine_id, NULL }, \
+ { 'M', specifier_os_image_id, NULL }, \
+ { 'o', specifier_os_id, NULL }, \
+ { 'v', specifier_kernel_release, NULL }, \
+ { 'w', specifier_os_version_id, NULL }, \
+ { 'W', specifier_os_variant_id, NULL }
-#define COMMON_CREDS_SPECIFIERS(scope) \
- { 'g', specifier_group_name, INT_TO_PTR(scope) }, \
- { 'G', specifier_group_id, INT_TO_PTR(scope) }, \
- { 'u', specifier_user_name, INT_TO_PTR(scope) }, \
- { 'U', specifier_user_id, INT_TO_PTR(scope) }
+#define COMMON_CREDS_SPECIFIERS(scope) \
+ { 'g', specifier_group_name, INT_TO_PTR(scope) }, \
+ { 'G', specifier_group_id, INT_TO_PTR(scope) }, \
+ { 'u', specifier_user_name, INT_TO_PTR(scope) }, \
+ { 'U', specifier_user_id, INT_TO_PTR(scope) }
-#define COMMON_TMP_SPECIFIERS \
- { 'T', specifier_tmp_dir, NULL }, \
- { 'V', specifier_var_tmp_dir, NULL }
+#define COMMON_TMP_SPECIFIERS \
+ { 'T', specifier_tmp_dir, NULL }, \
+ { 'V', specifier_var_tmp_dir, NULL }
static inline char* specifier_escape(const char *string) {
return strreplace(string, "%", "%%");
#include "capability-util.h"
#include "chase-symlinks.h"
+#include "devnum-util.h"
#include "discover-image.h"
#include "dissect-image.h"
#include "env-util.h"
#include "pretty-print.h"
#include "process-util.h"
#include "sort-util.h"
-#include "stat-util.h"
#include "terminal-util.h"
#include "user-util.h"
#include "verbs.h"
if (r < 0)
return log_error_errno(r, "Failed to determine whether hierarchy '%s' contains '.systemd-sysext/dev': %m", p);
- r = parse_dev(buf, &dev);
+ r = parse_devnum(buf, &dev);
if (r < 0)
return log_error_errno(r, "Failed to parse device major/minor stored in '.systemd-sysext/dev' file on '%s': %m", p);
if (r < 0)
return log_error_errno(r, "Failed to set up loopback device for %s: %m", img->path);
+ r = loop_device_flock(d, LOCK_SH);
+ if (r < 0)
+ return log_error_errno(r, "Failed to lock loopback device: %m");
+
r = dissect_image_and_warn(
d->fd,
img->path,
#include "alloc-util.h"
#include "blockdev-util.h"
#include "chase-symlinks.h"
+#include "devnum-util.h"
#include "dirent-util.h"
#include "env-util.h"
#include "fd-util.h"
#include "macro.h"
#include "process-util.h"
#include "sort-util.h"
-#include "stat-util.h"
#include "string-table.h"
#include "sysupdate-cache.h"
#include "sysupdate-instance.h"
#include "gpt.h"
#include "hexdecoct.h"
#include "install-file.h"
+#include "parse-helpers.h"
#include "parse-util.h"
-#include "path-util.h"
#include "process-util.h"
#include "rm-rf.h"
#include "specifier.h"
[files('test-stat-util.c')],
+ [files('test-devnum-util.c')],
+
[files('test-os-util.c')],
[files('test-libcrypt-util.c'),
[files('test-parse-argument.c')],
- [files('test-parse-socket-bind-item.c')],
+ [files('test-parse-helpers.c')],
[files('test-parse-util.c')],
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <sys/stat.h>
+
+#include "devnum-util.h"
+#include "path-util.h"
+#include "stat-util.h"
+#include "tests.h"
+
+TEST(parse_devnum) {
+ dev_t dev;
+
+ assert_se(parse_devnum("", &dev) == -EINVAL);
+ assert_se(parse_devnum("junk", &dev) == -EINVAL);
+ assert_se(parse_devnum("0", &dev) == -EINVAL);
+ assert_se(parse_devnum("5", &dev) == -EINVAL);
+ assert_se(parse_devnum("5:", &dev) == -EINVAL);
+ assert_se(parse_devnum(":5", &dev) == -EINVAL);
+ assert_se(parse_devnum("-1:-1", &dev) == -EINVAL);
+#if SIZEOF_DEV_T < 8
+ assert_se(parse_devnum("4294967295:4294967295", &dev) == -EINVAL);
+#endif
+ assert_se(parse_devnum("8:11", &dev) >= 0 && major(dev) == 8 && minor(dev) == 11);
+ assert_se(parse_devnum("0:0", &dev) >= 0 && major(dev) == 0 && minor(dev) == 0);
+}
+
+TEST(device_major_minor_valid) {
+ /* on glibc dev_t is 64bit, even though in the kernel it is only 32bit */
+ assert_cc(sizeof(dev_t) == sizeof(uint64_t));
+
+ assert_se(DEVICE_MAJOR_VALID(0U));
+ assert_se(DEVICE_MINOR_VALID(0U));
+
+ assert_se(DEVICE_MAJOR_VALID(1U));
+ assert_se(DEVICE_MINOR_VALID(1U));
+
+ assert_se(!DEVICE_MAJOR_VALID(-1U));
+ assert_se(!DEVICE_MINOR_VALID(-1U));
+
+ assert_se(DEVICE_MAJOR_VALID(1U << 10));
+ assert_se(DEVICE_MINOR_VALID(1U << 10));
+
+ assert_se(DEVICE_MAJOR_VALID((1U << 12) - 1));
+ assert_se(DEVICE_MINOR_VALID((1U << 20) - 1));
+
+ assert_se(!DEVICE_MAJOR_VALID((1U << 12)));
+ assert_se(!DEVICE_MINOR_VALID((1U << 20)));
+
+ assert_se(!DEVICE_MAJOR_VALID(1U << 25));
+ assert_se(!DEVICE_MINOR_VALID(1U << 25));
+
+ assert_se(!DEVICE_MAJOR_VALID(UINT32_MAX));
+ assert_se(!DEVICE_MINOR_VALID(UINT32_MAX));
+
+ assert_se(!DEVICE_MAJOR_VALID(UINT64_MAX));
+ assert_se(!DEVICE_MINOR_VALID(UINT64_MAX));
+
+ assert_se(DEVICE_MAJOR_VALID(major(0)));
+ assert_se(DEVICE_MINOR_VALID(minor(0)));
+}
+
+static void test_device_path_make_canonical_one(const char *path) {
+ _cleanup_free_ char *resolved = NULL, *raw = NULL;
+ struct stat st;
+ dev_t devno;
+ mode_t mode;
+ int r;
+
+ log_debug("> %s", path);
+
+ if (stat(path, &st) < 0) {
+ assert_se(errno == ENOENT);
+ log_notice("Path %s not found, skipping test", path);
+ return;
+ }
+
+ r = device_path_make_canonical(st.st_mode, st.st_rdev, &resolved);
+ if (r == -ENOENT) {
+ /* maybe /dev/char/x:y and /dev/block/x:y are missing in this test environment, because we
+ * run in a container or so? */
+ log_notice("Device %s cannot be resolved, skipping test", path);
+ return;
+ }
+
+ assert_se(r >= 0);
+ assert_se(path_equal(path, resolved));
+
+ assert_se(device_path_make_major_minor(st.st_mode, st.st_rdev, &raw) >= 0);
+ assert_se(device_path_parse_major_minor(raw, &mode, &devno) >= 0);
+
+ assert_se(st.st_rdev == devno);
+ assert_se((st.st_mode & S_IFMT) == (mode & S_IFMT));
+}
+
+TEST(device_path_make_canonical) {
+ test_device_path_make_canonical_one("/dev/null");
+ test_device_path_make_canonical_one("/dev/zero");
+ test_device_path_make_canonical_one("/dev/full");
+ test_device_path_make_canonical_one("/dev/random");
+ test_device_path_make_canonical_one("/dev/urandom");
+ test_device_path_make_canonical_one("/dev/tty");
+
+ if (is_device_node("/run/systemd/inaccessible/blk") > 0) {
+ test_device_path_make_canonical_one("/run/systemd/inaccessible/chr");
+ test_device_path_make_canonical_one("/run/systemd/inaccessible/blk");
+ }
+}
+
+static void test_devnum_format_str_one(dev_t devnum, const char *s) {
+ dev_t x;
+
+ assert_se(streq(FORMAT_DEVNUM(devnum), s));
+ assert_se(parse_devnum(s, &x) >= 0);
+ assert_se(x == devnum);
+}
+
+TEST(devnum_format_str) {
+ test_devnum_format_str_one(makedev(0, 0), "0:0");
+ test_devnum_format_str_one(makedev(1, 2), "1:2");
+ test_devnum_format_str_one(makedev(99, 100), "99:100");
+ test_devnum_format_str_one(makedev(4095, 1048575), "4095:1048575");
+}
+
+DEFINE_TEST_MAIN(LOG_INFO);
dump_glyph(SPECIAL_GLYPH_TREE_RIGHT);
dump_glyph(SPECIAL_GLYPH_TREE_SPACE);
dump_glyph(SPECIAL_GLYPH_TREE_TOP);
+ dump_glyph(SPECIAL_GLYPH_VERTICAL_DOTTED);
dump_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET);
dump_glyph(SPECIAL_GLYPH_BLACK_CIRCLE);
dump_glyph(SPECIAL_GLYPH_WHITE_CIRCLE);
log_notice("Acquired loop device %s, will mount on %s", loop->node, mounted);
+ /* Let's make sure udev doesn't call BLKRRPART in the background, while we try to mount the device. */
+ assert_se(loop_device_flock(loop, LOCK_SH) >= 0);
+
r = dissect_image(loop->fd, NULL, NULL, loop->diskseq, loop->uevent_seqnum_not_before, loop->timestamp_not_before, DISSECT_IMAGE_READ_ONLY, &dissected);
if (r < 0)
log_error_errno(r, "Failed dissect loopback device %s: %m", loop->node);
log_notice_errno(r, "Mounted %s → %s: %m", loop->node, mounted);
assert_se(r >= 0);
+ /* Now the block device is mounted, we don't need no manual lock anymore, the devices are now
+ * pinned by the mounts. */
+ assert_se(loop_device_flock(loop, LOCK_UN) >= 0);
+
log_notice("Unmounting %s", mounted);
mounted = umount_and_rmdir_and_free(mounted);
return EXIT_TEST_SKIP;
}
- if (strstr_ptr(ci_environment(), "autopkgtest") || strstr_ptr(ci_environment(), "github-actions")) {
- // FIXME: we should reenable this one day
- log_tests_skipped("Skipping test on Ubuntu autopkgtest CI/GH Actions, test too slow and installed udev too flakey.");
- return EXIT_TEST_SKIP;
- }
-
/* This is a test for the loopback block device setup code and it's use by the image dissection
* logic: since the kernel APIs are hard use and prone to races, let's test this in a heavy duty
* test: we open a bunch of threads and repeatedly allocate and deallocate loopback block devices in
pthread_t threads[arg_n_threads];
sd_id128_t id;
+ /* Take an explicit lock while we format the file systems, in accordance with
+ * https://systemd.io/BLOCK_DEVICE_LOCKING/. We don't want udev to interfere and probe while we write
+ * or even issue BLKRRPART or similar while we are working on this. */
+ assert_se(loop_device_flock(loop, LOCK_EX) >= 0);
+
assert_se(dissect_image(loop->fd, NULL, NULL, loop->diskseq, loop->uevent_seqnum_not_before, loop->timestamp_not_before, 0, &dissected) >= 0);
assert_se(dissected->partitions[PARTITION_ESP].found);
assert_se(mkdtemp_malloc(NULL, &mounted) >= 0);
+ /* We are particularly correct here, and now downgrade LOCK → LOCK_SH. That's because we are done
+ * with formatting the file systems, so we don't need the exclusive lock anymore. From now on a
+ * shared one is fine. This way udev can now probe the device if it wants, but still won't call
+ * BLKRRPART on it, and that's good, because that would destroy our partition table while we are at
+ * it. */
+ assert_se(loop_device_flock(loop, LOCK_SH) >= 0);
+
/* This first (writable) mount will initialize the mount point dirs, so that the subsequent read-only ones can work */
assert_se(dissected_image_mount(dissected, mounted, UID_INVALID, UID_INVALID, 0) >= 0);
+ /* Now we mounted everything, the partitions are pinned. Now it's fine to release the lock
+ * fully. This means udev could now issue BLKRRPART again, but that's OK given this will fail because
+ * we now mounted the device. */
+ assert_se(loop_device_flock(loop, LOCK_UN) >= 0);
+
assert_se(umount_recursive(mounted, 0) >= 0);
loop = loop_device_unref(loop);
#include <stdio.h>
#include "macro.h"
-#include "parse-socket-bind-item.h"
+#include "parse-helpers.h"
#include "tests.h"
static void test_valid_item(
assert_se(parse_nice("+20", &n) == -ERANGE);
}
-TEST(parse_dev) {
- dev_t dev;
-
- assert_se(parse_dev("", &dev) == -EINVAL);
- assert_se(parse_dev("junk", &dev) == -EINVAL);
- assert_se(parse_dev("0", &dev) == -EINVAL);
- assert_se(parse_dev("5", &dev) == -EINVAL);
- assert_se(parse_dev("5:", &dev) == -EINVAL);
- assert_se(parse_dev(":5", &dev) == -EINVAL);
- assert_se(parse_dev("-1:-1", &dev) == -EINVAL);
-#if SIZEOF_DEV_T < 8
- assert_se(parse_dev("4294967295:4294967295", &dev) == -EINVAL);
-#endif
- assert_se(parse_dev("8:11", &dev) >= 0 && major(dev) == 8 && minor(dev) == 11);
- assert_se(parse_dev("0:0", &dev) >= 0 && major(dev) == 0 && minor(dev) == 0);
-}
-
TEST(parse_errno) {
assert_se(parse_errno("EILSEQ") == EILSEQ);
assert_se(parse_errno("EINVAL") == EINVAL);
assert_se(!hidden_or_backup_file("test.dpkg-old.foo"));
}
-TEST(systemd_installation_has_version) {
- int r;
- const unsigned versions[] = {0, 231, PROJECT_VERSION, 999};
- unsigned i;
-
- log_info("/* %s */", __func__);
-
- for (i = 0; i < ELEMENTSOF(versions); i++) {
- r = systemd_installation_has_version(saved_argv[1], versions[i]);
- assert_se(r >= 0);
- log_info("%s has systemd >= %u: %s",
- saved_argv[1] ?: "Current installation", versions[i], yes_no(r));
- }
-}
-
TEST(skip_dev_prefix) {
assert_se(streq(skip_dev_prefix("/"), "/"));
assert_se(streq(skip_dev_prefix("/dev"), ""));
assert_se(streq(w, "xxx a=AAAA b=BBBB e= yyy"));
free(w);
- r = specifier_printf("machine=%m, boot=%b, host=%H, pretty=%R, version=%v, arch=%a, empty=%e", SIZE_MAX, table, NULL, NULL, &w);
+ r = specifier_printf("machine=%m, boot=%b, host=%H, pretty=%q, version=%v, arch=%a, empty=%e", SIZE_MAX, table, NULL, NULL, &w);
assert_se(r >= 0);
assert_se(w);
puts(w);
assert_se(IN_SET(fd_is_ns(fd, CLONE_NEWNET), 1, -EUCLEAN));
}
-TEST(device_major_minor_valid) {
- /* on glibc dev_t is 64bit, even though in the kernel it is only 32bit */
- assert_cc(sizeof(dev_t) == sizeof(uint64_t));
-
- assert_se(DEVICE_MAJOR_VALID(0U));
- assert_se(DEVICE_MINOR_VALID(0U));
-
- assert_se(DEVICE_MAJOR_VALID(1U));
- assert_se(DEVICE_MINOR_VALID(1U));
-
- assert_se(!DEVICE_MAJOR_VALID(-1U));
- assert_se(!DEVICE_MINOR_VALID(-1U));
-
- assert_se(DEVICE_MAJOR_VALID(1U << 10));
- assert_se(DEVICE_MINOR_VALID(1U << 10));
-
- assert_se(DEVICE_MAJOR_VALID((1U << 12) - 1));
- assert_se(DEVICE_MINOR_VALID((1U << 20) - 1));
-
- assert_se(!DEVICE_MAJOR_VALID((1U << 12)));
- assert_se(!DEVICE_MINOR_VALID((1U << 20)));
-
- assert_se(!DEVICE_MAJOR_VALID(1U << 25));
- assert_se(!DEVICE_MINOR_VALID(1U << 25));
-
- assert_se(!DEVICE_MAJOR_VALID(UINT32_MAX));
- assert_se(!DEVICE_MINOR_VALID(UINT32_MAX));
-
- assert_se(!DEVICE_MAJOR_VALID(UINT64_MAX));
- assert_se(!DEVICE_MINOR_VALID(UINT64_MAX));
-
- assert_se(DEVICE_MAJOR_VALID(major(0)));
- assert_se(DEVICE_MINOR_VALID(minor(0)));
-}
-
-static void test_device_path_make_canonical_one(const char *path) {
- _cleanup_free_ char *resolved = NULL, *raw = NULL;
- struct stat st;
- dev_t devno;
- mode_t mode;
- int r;
-
- log_debug("> %s", path);
-
- if (stat(path, &st) < 0) {
- assert_se(errno == ENOENT);
- log_notice("Path %s not found, skipping test", path);
- return;
- }
-
- r = device_path_make_canonical(st.st_mode, st.st_rdev, &resolved);
- if (r == -ENOENT) {
- /* maybe /dev/char/x:y and /dev/block/x:y are missing in this test environment, because we
- * run in a container or so? */
- log_notice("Device %s cannot be resolved, skipping test", path);
- return;
- }
-
- assert_se(r >= 0);
- assert_se(path_equal(path, resolved));
-
- assert_se(device_path_make_major_minor(st.st_mode, st.st_rdev, &raw) >= 0);
- assert_se(device_path_parse_major_minor(raw, &mode, &devno) >= 0);
-
- assert_se(st.st_rdev == devno);
- assert_se((st.st_mode & S_IFMT) == (mode & S_IFMT));
-}
-
-TEST(device_path_make_canonical) {
- test_device_path_make_canonical_one("/dev/null");
- test_device_path_make_canonical_one("/dev/zero");
- test_device_path_make_canonical_one("/dev/full");
- test_device_path_make_canonical_one("/dev/random");
- test_device_path_make_canonical_one("/dev/urandom");
- test_device_path_make_canonical_one("/dev/tty");
-
- if (is_device_node("/run/systemd/inaccessible/blk") > 0) {
- test_device_path_make_canonical_one("/run/systemd/inaccessible/chr");
- test_device_path_make_canonical_one("/run/systemd/inaccessible/blk");
- }
-}
-
TEST(dir_is_empty) {
_cleanup_(rm_rf_physical_and_freep) char *empty_dir = NULL;
_cleanup_free_ char *j = NULL, *jj = NULL;
#include "specifier.h"
#include "string-util.h"
#include "tests.h"
+#include "tmpfile-util.h"
#include "unit-def.h"
#include "unit-name.h"
#include "unit-printf.h"
}
TEST_RET(unit_printf, .sd_booted = true) {
- _cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *gid = NULL, *group = NULL, *uid = NULL, *user = NULL, *shell = NULL, *home = NULL;
+ _cleanup_free_ char
+ *architecture, *os_image_version, *boot_id, *os_build_id,
+ *hostname, *short_hostname, *pretty_hostname,
+ *machine_id, *os_image_id, *os_id, *os_version_id, *os_variant_id,
+ *user, *group, *uid, *gid, *home, *shell,
+ *tmp_dir, *var_tmp_dir;
_cleanup_(manager_freep) Manager *m = NULL;
Unit *u;
int r;
- assert_se(specifier_machine_id('m', NULL, NULL, NULL, &mid) >= 0 && mid);
- assert_se(specifier_boot_id('b', NULL, NULL, NULL, &bid) >= 0 && bid);
- assert_se(host = gethostname_malloc());
+ _cleanup_(unlink_tempfilep) char filename[] = "/tmp/test-unit_printf.XXXXXX";
+ assert_se(mkostemp_safe(filename) >= 0);
+
+ /* Using the specifier functions is admittedly a bit circular, but we don't want to reimplement the
+ * logic a second time. We're at least testing that the hookup works. */
+ assert_se(specifier_architecture('a', NULL, NULL, NULL, &architecture) >= 0);
+ assert_se(architecture);
+ assert_se(specifier_os_image_version('A', NULL, NULL, NULL, &os_image_version) >= 0);
+ assert_se(specifier_boot_id('b', NULL, NULL, NULL, &boot_id) >= 0);
+ assert_se(boot_id);
+ assert_se(specifier_os_build_id('B', NULL, NULL, NULL, &os_build_id) >= 0);
+ assert_se(hostname = gethostname_malloc());
+ assert_se(specifier_short_hostname('l', NULL, NULL, NULL, &short_hostname) == 0);
+ assert_se(short_hostname);
+ assert_se(specifier_pretty_hostname('q', NULL, NULL, NULL, &pretty_hostname) == 0);
+ assert_se(pretty_hostname);
+ assert_se(specifier_machine_id('m', NULL, NULL, NULL, &machine_id) >= 0);
+ assert_se(machine_id);
+ assert_se(specifier_os_image_id('M', NULL, NULL, NULL, &os_image_id) >= 0);
+ assert_se(specifier_os_id('o', NULL, NULL, NULL, &os_id) >= 0);
+ assert_se(specifier_os_version_id('w', NULL, NULL, NULL, &os_version_id) >= 0);
+ assert_se(specifier_os_variant_id('W', NULL, NULL, NULL, &os_variant_id) >= 0);
assert_se(user = uid_to_name(getuid()));
assert_se(group = gid_to_name(getgid()));
assert_se(asprintf(&uid, UID_FMT, getuid()));
assert_se(asprintf(&gid, UID_FMT, getgid()));
assert_se(get_home_dir(&home) >= 0);
assert_se(get_shell(&shell) >= 0);
+ assert_se(specifier_tmp_dir('T', NULL, NULL, NULL, &tmp_dir) >= 0);
+ assert_se(tmp_dir);
+ assert_se(specifier_var_tmp_dir('V', NULL, NULL, NULL, &var_tmp_dir) >= 0);
+ assert_se(var_tmp_dir);
r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_MINIMAL, &m);
if (manager_errno_skip_test(r))
return log_tests_skipped_errno(r, "manager_new");
assert_se(r == 0);
-#define expect(unit, pattern, expected) \
+ assert_se(free_and_strdup(&m->cgroup_root, "/cgroup-root") == 1);
+
+#define expect(unit, pattern, _expected) \
{ \
- char *e; \
_cleanup_free_ char *t = NULL; \
assert_se(unit_full_printf(unit, pattern, &t) >= 0); \
- printf("result: %s\nexpect: %s\n", t, expected); \
- if ((e = endswith(expected, "*"))) \
- assert_se(strncmp(t, e, e-expected)); \
- else \
- assert_se(streq(t, expected)); \
+ const char *expected = strempty(_expected); \
+ printf("%s: result: %s\n expect: %s\n", pattern, t, expected); \
+ assert_se(fnmatch(expected, t, FNM_NOESCAPE) == 0); \
}
assert_se(u = unit_new(m, sizeof(Service)));
assert_se(unit_add_name(u, "blah.service") == 0);
assert_se(unit_add_name(u, "blah.service") == 0);
+ /* We need *a* file that exists, but it doesn't even need to have the right suffix. */
+ assert_se(free_and_strdup(&u->fragment_path, filename) == 1);
+
+ /* This sets the slice to /app.slice. */
+ assert_se(unit_set_default_slice(u) == 1);
+
/* general tests */
expect(u, "%%", "%");
expect(u, "%%s", "%s");
expect(u, "%", "%");
/* normal unit */
- expect(u, "%n", "blah.service");
- expect(u, "%f", "/blah");
- expect(u, "%N", "blah");
- expect(u, "%p", "blah");
- expect(u, "%P", "blah");
- expect(u, "%i", "");
- expect(u, "%I", "");
- expect(u, "%j", "blah");
- expect(u, "%J", "blah");
+ expect(u, "%a", architecture);
+ expect(u, "%A", os_image_version);
+ expect(u, "%b", boot_id);
+ expect(u, "%B", os_build_id);
+ expect(u, "%H", hostname);
+ expect(u, "%l", short_hostname);
+ expect(u, "%q", pretty_hostname);
+ expect(u, "%m", machine_id);
+ expect(u, "%M", os_image_id);
+ expect(u, "%o", os_id);
+ expect(u, "%w", os_version_id);
+ expect(u, "%W", os_variant_id);
expect(u, "%g", group);
expect(u, "%G", gid);
expect(u, "%u", user);
expect(u, "%U", uid);
+ expect(u, "%T", tmp_dir);
+ expect(u, "%V", var_tmp_dir);
+
+ expect(u, "%i", "");
+ expect(u, "%I", "");
+ expect(u, "%j", "blah");
+ expect(u, "%J", "blah");
+ expect(u, "%n", "blah.service");
+ expect(u, "%N", "blah");
+ expect(u, "%p", "blah");
+ expect(u, "%P", "blah");
+ expect(u, "%f", "/blah");
+ expect(u, "%y", filename);
+ expect(u, "%Y", "/tmp");
+ expect(u, "%C", m->prefix[EXEC_DIRECTORY_CACHE]);
+ expect(u, "%d", "*/credentials/blah.service");
+ expect(u, "%E", m->prefix[EXEC_DIRECTORY_CONFIGURATION]);
+ expect(u, "%L", m->prefix[EXEC_DIRECTORY_LOGS]);
+ expect(u, "%S", m->prefix[EXEC_DIRECTORY_STATE]);
+ expect(u, "%t", m->prefix[EXEC_DIRECTORY_RUNTIME]);
expect(u, "%h", home);
- expect(u, "%m", mid);
- expect(u, "%b", bid);
- expect(u, "%H", host);
- expect(u, "%t", "/run/user/*");
+ expect(u, "%s", shell);
+
+ /* deprecated */
+ expect(u, "%c", "/cgroup-root/app.slice/blah.service");
+ expect(u, "%r", "/cgroup-root/app.slice");
+ expect(u, "%R", "/cgroup-root");
/* templated */
assert_se(u = unit_new(m, sizeof(Service)));
assert_se(unit_add_name(u, "blah@foo-foo.service") == 0);
assert_se(unit_add_name(u, "blah@foo-foo.service") == 0);
- expect(u, "%n", "blah@foo-foo.service");
- expect(u, "%N", "blah@foo-foo");
- expect(u, "%f", "/foo/foo");
- expect(u, "%p", "blah");
- expect(u, "%P", "blah");
+ assert_se(free_and_strdup(&u->fragment_path, filename) == 1);
+
+ /* This sets the slice to /app.slice/app-blah.slice. */
+ assert_se(unit_set_default_slice(u) == 1);
+
expect(u, "%i", "foo-foo");
expect(u, "%I", "foo/foo");
expect(u, "%j", "blah");
expect(u, "%J", "blah");
- expect(u, "%g", group);
- expect(u, "%G", gid);
- expect(u, "%u", user);
- expect(u, "%U", uid);
+ expect(u, "%n", "blah@foo-foo.service");
+ expect(u, "%N", "blah@foo-foo");
+ expect(u, "%p", "blah");
+ expect(u, "%P", "blah");
+ expect(u, "%f", "/foo/foo");
+ expect(u, "%y", filename);
+ expect(u, "%Y", "/tmp");
+ expect(u, "%C", m->prefix[EXEC_DIRECTORY_CACHE]);
+ expect(u, "%d", "*/credentials/blah@foo-foo.service");
+ expect(u, "%E", m->prefix[EXEC_DIRECTORY_CONFIGURATION]);
+ expect(u, "%L", m->prefix[EXEC_DIRECTORY_LOGS]);
+ expect(u, "%S", m->prefix[EXEC_DIRECTORY_STATE]);
+ expect(u, "%t", m->prefix[EXEC_DIRECTORY_RUNTIME]);
expect(u, "%h", home);
- expect(u, "%m", mid);
- expect(u, "%b", bid);
- expect(u, "%H", host);
- expect(u, "%t", "/run/user/*");
+ expect(u, "%s", shell);
+
+ /* deprecated */
+ expect(u, "%c", "/cgroup-root/app.slice/app-blah.slice/blah@foo-foo.service");
+ expect(u, "%r", "/cgroup-root/app.slice/app-blah.slice");
+ expect(u, "%R", "/cgroup-root");
/* templated with components */
assert_se(u = unit_new(m, sizeof(Slice)));
assert_se(unit_add_name(u, "blah-blah\\x2d.slice") == 0);
- expect(u, "%n", "blah-blah\\x2d.slice");
- expect(u, "%N", "blah-blah\\x2d");
- expect(u, "%f", "/blah/blah-");
- expect(u, "%p", "blah-blah\\x2d");
- expect(u, "%P", "blah/blah-");
expect(u, "%i", "");
expect(u, "%I", "");
expect(u, "%j", "blah\\x2d");
expect(u, "%J", "blah-");
+ expect(u, "%n", "blah-blah\\x2d.slice");
+ expect(u, "%N", "blah-blah\\x2d");
+ expect(u, "%p", "blah-blah\\x2d");
+ expect(u, "%P", "blah/blah-");
+ expect(u, "%f", "/blah/blah-");
+
+ /* deprecated */
+ expect(u, "%c", "/cgroup-root/blah-blah\\x2d.slice");
+ expect(u, "%r", "/cgroup-root");
+ expect(u, "%R", "/cgroup-root");
#undef expect
SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
- SD_BUS_METHOD_WITH_NAMES("SetTime",
- "xbb",
- SD_BUS_PARAM(usec_utc)
- SD_BUS_PARAM(relative)
- SD_BUS_PARAM(interactive),
- NULL,,
- method_set_time,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetTimezone",
- "sb",
- SD_BUS_PARAM(timezone)
- SD_BUS_PARAM(interactive),
- NULL,,
- method_set_timezone,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetLocalRTC",
- "bbb",
- SD_BUS_PARAM(local_rtc)
- SD_BUS_PARAM(fix_system)
- SD_BUS_PARAM(interactive),
- NULL,,
- method_set_local_rtc,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("SetNTP",
- "bb",
- SD_BUS_PARAM(use_ntp)
- SD_BUS_PARAM(interactive),
- NULL,,
- method_set_ntp,
- SD_BUS_VTABLE_UNPRIVILEGED),
- SD_BUS_METHOD_WITH_NAMES("ListTimezones",
- NULL,,
- "as",
- SD_BUS_PARAM(timezones),
- method_list_timezones,
- SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetTime",
+ SD_BUS_ARGS("x", usec_utc, "b", relative, "b", interactive),
+ SD_BUS_NO_RESULT,
+ method_set_time,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetTimezone",
+ SD_BUS_ARGS("s", timezone, "b", interactive),
+ SD_BUS_NO_RESULT,
+ method_set_timezone,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetLocalRTC",
+ SD_BUS_ARGS("b", local_rtc, "b", fix_system, "b", interactive),
+ SD_BUS_NO_RESULT,
+ method_set_local_rtc,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetNTP",
+ SD_BUS_ARGS("b", use_ntp, "b", interactive),
+ SD_BUS_NO_RESULT,
+ method_set_ntp,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("ListTimezones",
+ SD_BUS_NO_ARGS,
+ SD_BUS_RESULT("as", timezones),
+ method_list_timezones,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END,
};
install_dir : dbussystemservicedir)
install_data('80-systemd-timesync.list',
install_dir : ntpservicelistdir)
+ install_data('org.freedesktop.timesync1.policy',
+ install_dir : polkitpolicydir)
endif
############################################################
send_interface="org.freedesktop.DBus.Properties"
send_member="GetAll"/>
+ <allow send_destination="org.freedesktop.timesync1"
+ send_interface="org.freedesktop.timesync1.Manager"
+ send_member="SetRuntimeNTPServers"/>
+
<allow receive_sender="org.freedesktop.timesync1"/>
</policy>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
+<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+
+<!--
+ SPDX-License-Identifier: LGPL-2.1-or-later
+
+ This file is part of systemd.
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+-->
+
+<policyconfig>
+
+ <vendor>The systemd Project</vendor>
+ <vendor_url>https://systemd.io</vendor_url>
+
+ <action id="org.freedesktop.timesync1.set-runtime-servers">
+ <description gettext-domain="systemd">Set runtime NTP servers</description>
+ <message gettext-domain="systemd">Authentication is required to set runtime NTP servers.</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>auth_admin_keep</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-timesync</annotate>
+ </action>
+
+</policyconfig>
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include <sys/capability.h>
+
#include "sd-bus.h"
#include "alloc-util.h"
#include "bus-get-properties.h"
#include "bus-internal.h"
#include "bus-log-control-api.h"
+#include "bus-polkit.h"
#include "bus-protocol.h"
#include "bus-util.h"
+#include "dns-domain.h"
#include "in-addr-util.h"
#include "log.h"
#include "macro.h"
+#include "strv.h"
#include "time-util.h"
#include "timesyncd-bus.h"
+#include "user-util.h"
static int property_get_servers(
sd_bus *bus,
return sd_bus_message_close_container(reply);
}
+static int method_set_runtime_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_strv_free_ char **msg_names = NULL;
+ Manager *m = userdata;
+ int r;
+
+ assert(m);
+ assert(message);
+
+ r = sd_bus_message_read_strv(message, &msg_names);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(name, msg_names) {
+ r = dns_name_is_valid_or_address(*name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check validity of NTP server name or address '%s': %m", *name);
+ if (r == 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid NTP server name or address, refusing: %s", *name);
+ }
+
+ r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+ "org.freedesktop.timesync1.set-runtime-servers",
+ NULL, true, UID_INVALID,
+ &m->polkit_registry, error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ /* Polkit will call us back */
+ return 1;
+
+ manager_flush_runtime_servers(m);
+
+ STRV_FOREACH(name, msg_names) {
+ r = server_name_new(m, NULL, SERVER_RUNTIME, *name);
+ if (r < 0) {
+ manager_flush_runtime_servers(m);
+
+ return log_error_errno(r, "Failed to add runtime server '%s': %m", *name);
+ }
+ }
+
+ m->exhausted_servers = true;
+ manager_set_server_name(m, NULL);
+ (void) manager_connect(m);
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
static int property_get_current_server_name(
sd_bus *bus,
const char *path,
SD_BUS_PROPERTY("LinkNTPServers", "as", property_get_servers, offsetof(Manager, link_servers), 0),
SD_BUS_PROPERTY("SystemNTPServers", "as", property_get_servers, offsetof(Manager, system_servers), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RuntimeNTPServers", "as", property_get_servers, offsetof(Manager, runtime_servers), 0),
SD_BUS_PROPERTY("FallbackNTPServers", "as", property_get_servers, offsetof(Manager, fallback_servers), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ServerName", "s", property_get_current_server_name, offsetof(Manager, current_server_name), 0),
SD_BUS_PROPERTY("ServerAddress", "(iay)", property_get_current_server_address, offsetof(Manager, current_server_address), 0),
SD_BUS_PROPERTY("NTPMessage", "(uuuuittayttttbtt)", property_get_ntp_message, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Frequency", "x", NULL, offsetof(Manager, drift_freq), 0),
+ SD_BUS_METHOD_WITH_ARGS("SetRuntimeNTPServers",
+ SD_BUS_ARGS("as", runtime_servers),
+ SD_BUS_NO_RESULT,
+ method_set_runtime_servers,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+
SD_BUS_VTABLE_END
};
#include "sd-messages.h"
#include "alloc-util.h"
+#include "bus-polkit.h"
#include "dns-domain.h"
#include "event-util.h"
#include "fd-util.h"
bool restart = true;
/* Our current server name list is exhausted,
- * let's find the next one to iterate. First
- * we try the system list, then the link list.
- * After having processed the link list we
- * jump back to the system list. However, if
- * both lists are empty, we change to the
- * fallback list. */
+ * let's find the next one to iterate. First we try the runtime list, then the system list,
+ * then the link list. After having processed the link list we jump back to the system list
+ * if no runtime server list.
+ * However, if all lists are empty, we change to the fallback list. */
if (!m->current_server_name || m->current_server_name->type == SERVER_LINK) {
- f = m->system_servers;
+ f = m->runtime_servers;
+ if (!f)
+ f = m->system_servers;
if (!f)
f = m->link_servers;
} else {
f = m->link_servers;
- if (!f)
- f = m->system_servers;
- else
+ if (f)
restart = false;
+ else {
+ f = m->runtime_servers;
+ if (!f)
+ f = m->system_servers;
+ }
}
if (!f)
return 0;
}
- if (restart && !m->exhausted_servers && m->poll_interval_usec) {
+ if (restart && !m->exhausted_servers && m->poll_interval_usec > 0) {
log_debug("Waiting after exhausting servers.");
r = sd_event_add_time_relative(m->event, &m->event_retry, CLOCK_BOOTTIME, m->poll_interval_usec, 0, manager_retry_connect, m);
if (r < 0)
if (t == SERVER_FALLBACK)
while (m->fallback_servers)
server_name_free(m->fallback_servers);
+
+ if (t == SERVER_RUNTIME)
+ manager_flush_runtime_servers(m);
+}
+
+void manager_flush_runtime_servers(Manager *m) {
+ assert(m);
+
+ while (m->runtime_servers)
+ server_name_free(m->runtime_servers);
}
Manager* manager_free(Manager *m) {
manager_disconnect(m);
manager_flush_server_names(m, SERVER_SYSTEM);
manager_flush_server_names(m, SERVER_LINK);
+ manager_flush_server_names(m, SERVER_RUNTIME);
manager_flush_server_names(m, SERVER_FALLBACK);
sd_event_source_unref(m->event_retry);
sd_bus_flush_close_unref(m->bus);
+ bus_verify_polkit_async_registry_free(m->polkit_registry);
+
return mfree(m);
}
return r;
}
-static bool manager_is_connected(Manager *m) {
+bool manager_is_connected(Manager *m) {
+ assert(m);
+
/* Return true when the manager is sending a request, resolving a server name, or
* in a poll interval. */
return m->server_socket >= 0 || m->resolve_query || m->event_timer;
#include "sd-network.h"
#include "sd-resolve.h"
+#include "hashmap.h"
#include "list.h"
#include "ratelimit.h"
#include "time-util.h"
LIST_HEAD(ServerName, system_servers);
LIST_HEAD(ServerName, link_servers);
+ LIST_HEAD(ServerName, runtime_servers);
LIST_HEAD(ServerName, fallback_servers);
bool have_fallbacks:1;
sd_event_source *event_timeout;
bool talking;
+ /* PolicyKit */
+ Hashmap *polkit_registry;
+
/* last sent packet */
struct timespec trans_time_mon;
struct timespec trans_time;
void manager_set_server_name(Manager *m, ServerName *n);
void manager_set_server_address(Manager *m, ServerAddress *a);
void manager_flush_server_names(Manager *m, ServerType t);
+void manager_flush_runtime_servers(Manager *m);
int manager_connect(Manager *m);
void manager_disconnect(Manager *m);
+bool manager_is_connected(Manager *m);
int manager_setup_save_time_event(Manager *m);
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "alloc-util.h"
+#include "string-table.h"
#include "timesyncd-server.h"
+static const char * const server_type_table[_SERVER_TYPE_MAX] = {
+ [SERVER_SYSTEM] = "system",
+ [SERVER_FALLBACK] = "fallback",
+ [SERVER_LINK] = "link",
+ [SERVER_RUNTIME] = "runtime",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(server_type, ServerType);
+
int server_address_new(
ServerName *n,
ServerAddress **ret,
ServerType type,
const char *string) {
- ServerName *n, *tail;
+ ServerName *n;
assert(m);
assert(string);
return -ENOMEM;
}
- if (type == SERVER_SYSTEM) {
- LIST_FIND_TAIL(names, m->system_servers, tail);
- LIST_INSERT_AFTER(names, m->system_servers, tail, n);
- } else if (type == SERVER_LINK) {
- LIST_FIND_TAIL(names, m->link_servers, tail);
- LIST_INSERT_AFTER(names, m->link_servers, tail, n);
- } else if (type == SERVER_FALLBACK) {
- LIST_FIND_TAIL(names, m->fallback_servers, tail);
- LIST_INSERT_AFTER(names, m->fallback_servers, tail, n);
- } else
+ switch (type) {
+ case SERVER_SYSTEM:
+ LIST_APPEND(names, m->system_servers, n);
+ break;
+ case SERVER_LINK:
+ LIST_APPEND(names, m->link_servers, n);
+ break;
+ case SERVER_FALLBACK:
+ LIST_APPEND(names, m->fallback_servers, n);
+ break;
+ case SERVER_RUNTIME:
+ LIST_APPEND(names, m->runtime_servers, n);
+ break;
+ default:
assert_not_reached();
+ }
if (type != SERVER_FALLBACK &&
m->current_server_name &&
m->current_server_name->type == SERVER_FALLBACK)
manager_set_server_name(m, NULL);
- log_debug("Added new server %s.", string);
+ log_debug("Added new %s server %s.", server_type_to_string(type), string);
if (ret)
*ret = n;
LIST_REMOVE(names, n->manager->link_servers, n);
else if (n->type == SERVER_FALLBACK)
LIST_REMOVE(names, n->manager->fallback_servers, n);
+ else if (n->type == SERVER_RUNTIME)
+ LIST_REMOVE(names, n->manager->runtime_servers, n);
else
assert_not_reached();
SERVER_SYSTEM,
SERVER_FALLBACK,
SERVER_LINK,
+ SERVER_RUNTIME,
+ _SERVER_TYPE_MAX,
+ _SERVER_TYPE_INVALID = -EINVAL,
} ServerType;
#include "timesyncd-manager.h"
#include "conf-files.h"
#include "copy.h"
#include "def.h"
+#include "devnum-util.h"
#include "dirent-util.h"
#include "dissect-image.h"
#include "env-util.h"
#include "set.h"
#include "sort-util.h"
#include "specifier.h"
-#include "stat-util.h"
#include "stdio-util.h"
#include "string-table.h"
#include "string-util.h"
{ 'a', specifier_architecture, NULL },
{ 'b', specifier_boot_id, NULL },
{ 'B', specifier_os_build_id, NULL },
- { 'H', specifier_host_name, NULL },
- { 'l', specifier_short_host_name, NULL },
+ { 'H', specifier_hostname, NULL },
+ { 'l', specifier_short_hostname, NULL },
{ 'm', specifier_machine_id_safe, NULL },
{ 'o', specifier_os_id, NULL },
{ 'v', specifier_kernel_release, NULL },
return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Device file requires argument.");
}
- r = parse_dev(i.argument, &i.major_minor);
+ r = parse_devnum(i.argument, &i.major_minor);
if (r < 0) {
*invalid_config = true;
return log_syntax(NULL, LOG_ERR, fname, line, r, "Can't parse device file major/minor '%s'.", i.argument);
struct LinkConfigContext {
LIST_HEAD(LinkConfig, configs);
int ethtool_fd;
- bool enable_name_policy;
usec_t network_dirs_ts_usec;
};
*ctx = (LinkConfigContext) {
.ethtool_fd = -1,
- .enable_name_policy = true,
};
*ret = TAKE_PTR(ctx);
return 0;
}
-static bool enable_name_policy(void) {
- bool b;
-
- return proc_cmdline_get_bool("net.ifnames", &b) <= 0 || b;
-}
-
static int device_unsigned_attribute(sd_device *device, const char *attr, unsigned *type) {
const char *s;
int r;
link_configs_free(ctx);
- if (!enable_name_policy()) {
- ctx->enable_name_policy = false;
- log_info("Network interface NamePolicy= disabled on kernel command line, ignoring.");
- }
-
/* update timestamp */
paths_check_timestamp(NETWORK_DIRS, &ctx->network_dirs_ts_usec, true);
return 0;
}
-static int link_generate_new_name(Link *link, bool enable_name_policy) {
+static bool enable_name_policy(void) {
+ static int cached = -1;
+ bool b;
+ int r;
+
+ if (cached >= 0)
+ return cached;
+
+ r = proc_cmdline_get_bool("net.ifnames", &b);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse net.ifnames= kernel command line option, ignoring: %m");
+ if (r <= 0)
+ return (cached = true);
+
+ if (!b)
+ log_info("Network interface NamePolicy= disabled on kernel command line.");
+
+ return (cached = b);
+}
+
+static int link_generate_new_name(Link *link) {
LinkConfig *config;
sd_device *device;
goto no_rename;
}
- if (enable_name_policy && config->name_policy)
+ if (enable_name_policy() && config->name_policy)
for (NamePolicy *policy = config->name_policy; *policy != _NAMEPOLICY_INVALID; policy++) {
const char *new_name = NULL;
if (r < 0)
return r;
- r = link_generate_new_name(link, ctx->enable_name_policy);
+ r = link_generate_new_name(link);
if (r < 0)
return r;
bool has_abs_coordinates = false;
bool has_rel_coordinates = false;
bool has_mt_coordinates = false;
- bool has_joystick_axes_or_buttons = false;
+ size_t num_joystick_axes = 0;
+ size_t num_joystick_buttons = 0;
bool has_pad_buttons = false;
bool is_direct = false;
bool has_touch = false;
* Catz Mad Catz M.M.O.TE). Skip those.
*/
if (!test_bit(BTN_JOYSTICK - 1, bitmask_key)) {
- for (int button = BTN_JOYSTICK; button < BTN_DIGI && !has_joystick_axes_or_buttons; button++)
- has_joystick_axes_or_buttons = test_bit(button, bitmask_key);
- for (int button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40 && !has_joystick_axes_or_buttons; button++)
- has_joystick_axes_or_buttons = test_bit(button, bitmask_key);
- for (int button = BTN_DPAD_UP; button <= BTN_DPAD_RIGHT && !has_joystick_axes_or_buttons; button++)
- has_joystick_axes_or_buttons = test_bit(button, bitmask_key);
+ for (int button = BTN_JOYSTICK; button < BTN_DIGI; button++)
+ if (test_bit(button, bitmask_key))
+ num_joystick_buttons++;
+ for (int button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40; button++)
+ if (test_bit(button, bitmask_key))
+ num_joystick_buttons++;
+ for (int button = BTN_DPAD_UP; button <= BTN_DPAD_RIGHT; button++)
+ if (test_bit(button, bitmask_key))
+ num_joystick_buttons++;
}
- for (int axis = ABS_RX; axis < ABS_PRESSURE && !has_joystick_axes_or_buttons; axis++)
- has_joystick_axes_or_buttons = test_bit(axis, bitmask_abs);
+ for (int axis = ABS_RX; axis < ABS_PRESSURE; axis++)
+ if (test_bit(axis, bitmask_abs))
+ num_joystick_axes++;
if (has_abs_coordinates) {
if (has_stylus || has_pen)
is_abs_mouse = true;
else if (has_touch || is_direct)
is_touchscreen = true;
- else if (has_joystick_axes_or_buttons)
+ else if (num_joystick_buttons > 0 || num_joystick_axes > 0)
is_joystick = true;
- } else if (has_joystick_axes_or_buttons)
+ } else if (num_joystick_buttons > 0 || num_joystick_axes > 0)
is_joystick = true;
if (has_mt_coordinates) {
if (is_mouse && id->bustype == BUS_I2C)
is_pointing_stick = true;
+ /* Joystick un-detection. Some keyboards have random joystick buttons
+ * set. Avoid those being labeled as ID_INPUT_JOYSTICK with some heuristics.
+ * The well-known keys represent a (randomly picked) set of key groups.
+ * A joystick may have one of those but probably not several. And a joystick with less than 2 buttons
+ * or axes is not a joystick either.
+ * libinput uses similar heuristics, any changes here should be added to libinput too.
+ */
+ if (is_joystick) {
+ static const unsigned int well_known_keyboard_keys[] = {
+ KEY_LEFTCTRL, KEY_CAPSLOCK, KEY_NUMLOCK, KEY_INSERT,
+ KEY_MUTE, KEY_CALC, KEY_FILE, KEY_MAIL, KEY_PLAYPAUSE,
+ KEY_BRIGHTNESSDOWN,
+ };
+ size_t num_well_known_keys = 0;
+
+ if (has_keys)
+ for (size_t i = 0; i < ELEMENTSOF(well_known_keyboard_keys); i++)
+ if (test_bit(well_known_keyboard_keys[i], bitmask_key))
+ num_well_known_keys++;
+
+ if (num_well_known_keys >= 4 || num_joystick_buttons + num_joystick_axes < 2) {
+ log_device_debug(dev, "Input device has %zd joystick buttons and %zd axes but also %zd keyboard key sets, "
+ "assuming this is a keyboard, not a joystick.",
+ num_joystick_buttons, num_joystick_axes, num_well_known_keys);
+ is_joystick = false;
+ }
+ }
+
if (is_pointing_stick)
udev_builtin_add_property(dev, test, "ID_INPUT_POINTINGSTICK", "1");
if (is_mouse || is_abs_mouse)
/* ACPI _SUN — slot user number */
r = sd_device_new_from_subsystem_sysname(&pci, "subsystem", "pci");
if (r < 0)
- return log_debug_errno(r, "sd_device_new_from_subsystem_sysname failed: %m");
+ return log_debug_errno(r, "sd_device_new_from_subsystem_sysname() failed: %m");
r = sd_device_get_syspath(pci, &syspath);
if (r < 0)
- return log_device_debug_errno(pci, r, "sd_device_get_syspath failed: %m");
+ return log_device_debug_errno(pci, r, "sd_device_get_syspath() failed: %m");
if (!snprintf_ok(slots, sizeof slots, "%s/slots", syspath))
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ENAMETOOLONG),
/* check if our direct parent is a VIO device with no other bus in-between */
r = sd_device_get_parent(dev, &parent);
if (r < 0)
- return log_device_debug_errno(dev, r, "sd_device_get_parent failed: %m");
+ return log_device_debug_errno(dev, r, "sd_device_get_parent() failed: %m");
r = sd_device_get_subsystem(parent, &subsystem);
if (r < 0)
- return log_device_debug_errno(parent, r, "sd_device_get_subsystem failed: %m");
+ return log_device_debug_errno(parent, r, "sd_device_get_subsystem() failed: %m");
if (!streq("vio", subsystem))
return -ENOENT;
log_device_debug(dev, "Parent device is in the vio subsystem.");
* there should only ever be one bus, and then remove leading zeros. */
r = sd_device_get_syspath(dev, &syspath);
if (r < 0)
- return log_device_debug_errno(dev, r, "sd_device_get_syspath failed: %m");
+ return log_device_debug_errno(dev, r, "sd_device_get_syspath() failed: %m");
r = sscanf(syspath, "/sys/devices/vio/%4x%4x/net/eth%u", &busid, &slotid, ðid);
log_device_debug(dev, "Parsing vio slot information from syspath \"%s\": %s",
/* check if our direct parent is a platform device with no other bus in-between */
r = sd_device_get_parent(dev, &parent);
if (r < 0)
- return log_device_debug_errno(dev, r, "sd_device_get_parent failed: %m");
+ return log_device_debug_errno(dev, r, "sd_device_get_parent() failed: %m");
r = sd_device_get_subsystem(parent, &subsystem);
if (r < 0)
- return log_device_debug_errno(parent, r, "sd_device_get_subsystem failed: %m");
+ return log_device_debug_errno(parent, r, "sd_device_get_subsystem() failed: %m");
if (!streq("platform", subsystem))
return -ENOENT;
r = sd_device_get_syspath(dev, &syspath);
if (r < 0)
- return log_device_debug_errno(dev, r, "sd_device_get_syspath failed: %m");
+ return log_device_debug_errno(dev, r, "sd_device_get_syspath() failed: %m");
/* syspath is too short, to have a valid ACPI instance */
if (strlen(syspath) < STRLEN(PLATFORM_TEST) + 1)
return 0;
}
+static int builtin_net_id_init(void) {
+ /* Load naming scheme here to suppress log messages in workers. */
+ naming_scheme();
+ return 0;
+}
+
const UdevBuiltin udev_builtin_net_id = {
.name = "net_id",
.cmd = builtin_net_id,
+ .init = builtin_net_id_init,
.help = "Network device properties",
};
};
void udev_builtin_init(void) {
- unsigned i;
-
if (initialized)
return;
- for (i = 0; i < _UDEV_BUILTIN_MAX; i++)
+ for (UdevBuiltinCommand i = 0; i < _UDEV_BUILTIN_MAX; i++)
if (builtins[i] && builtins[i]->init)
builtins[i]->init();
}
void udev_builtin_exit(void) {
- unsigned i;
-
if (!initialized)
return;
- for (i = 0; i < _UDEV_BUILTIN_MAX; i++)
+ for (UdevBuiltinCommand i = 0; i < _UDEV_BUILTIN_MAX; i++)
if (builtins[i] && builtins[i]->exit)
builtins[i]->exit();
}
bool udev_builtin_validate(void) {
- unsigned i;
-
- for (i = 0; i < _UDEV_BUILTIN_MAX; i++)
+ for (UdevBuiltinCommand i = 0; i < _UDEV_BUILTIN_MAX; i++)
if (builtins[i] && builtins[i]->validate && builtins[i]->validate())
return true;
return false;
}
void udev_builtin_list(void) {
- unsigned i;
-
- for (i = 0; i < _UDEV_BUILTIN_MAX; i++)
+ for (UdevBuiltinCommand i = 0; i < _UDEV_BUILTIN_MAX; i++)
if (builtins[i])
fprintf(stderr, " %-14s %s\n", builtins[i]->name, builtins[i]->help);
}
}
UdevBuiltinCommand udev_builtin_lookup(const char *command) {
- UdevBuiltinCommand i;
size_t n;
assert(command);
command += strspn(command, WHITESPACE);
n = strcspn(command, WHITESPACE);
- for (i = 0; i < _UDEV_BUILTIN_MAX; i++)
+ for (UdevBuiltinCommand i = 0; i < _UDEV_BUILTIN_MAX; i++)
if (builtins[i] && strneq(builtins[i]->name, command, n))
return i;
#include "sd-device.h"
#include "sd-netlink.h"
-typedef enum {
+typedef enum UdevBuiltinCommand {
#if HAVE_BLKID
UDEV_BUILTIN_BLKID,
#endif
#include "alloc-util.h"
#include "device-private.h"
#include "device-util.h"
+#include "devnum-util.h"
#include "dirent-util.h"
#include "escape.h"
#include "fd-util.h"
#include "dirent-util.h"
#include "errno-util.h"
#include "fd-util.h"
+#include "fileio.h"
+#include "glyph-util.h"
+#include "pager.h"
#include "sort-util.h"
#include "static-destruct.h"
#include "string-table.h"
ACTION_QUERY,
ACTION_ATTRIBUTE_WALK,
ACTION_DEVICE_ID_FILE,
+ ACTION_TREE,
} ActionType;
typedef enum QueryType {
static const char *arg_export_prefix = NULL;
static usec_t arg_wait_for_initialization_timeout = 0;
+/* Put a limit on --tree descent level to not exhaust our stack */
+#define TREE_DEPTH_MAX 64
+
static bool skip_attribute(const char *name) {
assert(name);
return 0;
}
-static int print_record(sd_device *device) {
+static int print_record(sd_device *device, const char *prefix) {
const char *str, *val, *subsys;
dev_t devnum;
uint64_t q;
assert(device);
+ prefix = strempty(prefix);
+
/* We don't show syspath here, because it's identical to devpath (modulo the "/sys" prefix).
*
* We don't show action/seqnum here because that only makes sense for records synthesized from
* • no color for regular properties */
assert_se(sd_device_get_devpath(device, &str) >= 0);
- printf("P: %s%s%s\n", ansi_highlight_white(), str, ansi_normal());
+ printf("%sP: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
if (sd_device_get_sysname(device, &str) >= 0)
- printf("M: %s%s%s\n", ansi_highlight_white(), str, ansi_normal());
+ printf("%sM: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
if (sd_device_get_sysnum(device, &str) >= 0)
- printf("R: %s%s%s\n", ansi_highlight_white(), str, ansi_normal());
+ printf("%sR: %s%s%s\n", prefix, ansi_highlight_white(), str, ansi_normal());
if (sd_device_get_subsystem(device, &subsys) >= 0)
- printf("U: %s%s%s\n", ansi_highlight_green(), subsys, ansi_normal());
+ printf("%sU: %s%s%s\n", prefix, ansi_highlight_green(), subsys, ansi_normal());
if (sd_device_get_devtype(device, &str) >= 0)
- printf("T: %s%s%s\n", ansi_highlight_green(), str, ansi_normal());
+ printf("%sT: %s%s%s\n", prefix, ansi_highlight_green(), str, ansi_normal());
if (sd_device_get_devnum(device, &devnum) >= 0)
- printf("D: %s%c %u:%u%s\n",
+ printf("%sD: %s%c %u:%u%s\n",
+ prefix,
ansi_highlight_cyan(),
streq_ptr(subsys, "block") ? 'b' : 'c', major(devnum), minor(devnum),
ansi_normal());
if (sd_device_get_ifindex(device, &ifi) >= 0)
- printf("I: %s%i%s\n", ansi_highlight_cyan(), ifi, ansi_normal());
+ printf("%sI: %s%i%s\n", prefix, ansi_highlight_cyan(), ifi, ansi_normal());
if (sd_device_get_devname(device, &str) >= 0) {
assert_se(val = path_startswith(str, "/dev/"));
- printf("N: %s%s%s\n", ansi_highlight_cyan(), val, ansi_normal());
+ printf("%sN: %s%s%s\n", prefix, ansi_highlight_cyan(), val, ansi_normal());
if (device_get_devlink_priority(device, &i) >= 0)
- printf("L: %s%i%s\n", ansi_highlight_cyan(), i, ansi_normal());
+ printf("%sL: %s%i%s\n", prefix, ansi_highlight_cyan(), i, ansi_normal());
FOREACH_DEVICE_DEVLINK(device, str) {
assert_se(val = path_startswith(str, "/dev/"));
- printf("S: %s%s%s\n", ansi_highlight_cyan(), val, ansi_normal());
+ printf("%sS: %s%s%s\n", prefix, ansi_highlight_cyan(), val, ansi_normal());
}
}
if (sd_device_get_diskseq(device, &q) >= 0)
- printf("Q: %s%" PRIu64 "%s\n", ansi_highlight_magenta(), q, ansi_normal());
+ printf("%sQ: %s%" PRIu64 "%s\n", prefix, ansi_highlight_magenta(), q, ansi_normal());
if (sd_device_get_driver(device, &str) >= 0)
- printf("V: %s%s%s\n", ansi_highlight_yellow4(), str, ansi_normal());
+ printf("%sV: %s%s%s\n", prefix, ansi_highlight_yellow4(), str, ansi_normal());
FOREACH_DEVICE_PROPERTY(device, str, val)
- printf("E: %s=%s\n", str, val);
+ printf("%sE: %s=%s\n", prefix, str, val);
- puts("");
+ if (isempty(prefix))
+ puts("");
return 0;
}
return log_error_errno(r, "Failed to scan devices: %m");
FOREACH_DEVICE_AND_SUBSYSTEM(e, d)
- (void) print_record(d);
+ (void) print_record(d, NULL);
return 0;
}
if ((stats.st_mode & mask) != 0)
continue;
if (S_ISDIR(stats.st_mode)) {
- _cleanup_closedir_ DIR *dir2 = NULL;
+ _cleanup_closedir_ DIR *subdir = NULL;
- dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
- if (dir2)
- cleanup_dir(dir2, mask, depth-1);
+ subdir = xopendirat(dirfd(dir), dent->d_name, O_NOFOLLOW);
+ if (!subdir)
+ log_debug_errno(errno, "Failed to open subdirectory '%s', ignoring: %m", dent->d_name);
+ else
+ cleanup_dir(subdir, mask, depth-1);
(void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
} else
if (fstatat(dirfd(dir), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) < 0)
continue;
if (S_ISDIR(stats.st_mode)) {
- _cleanup_closedir_ DIR *dir2 = NULL;
+ _cleanup_closedir_ DIR *subdir = NULL;
- dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
- if (dir2)
- cleanup_dir_after_db_cleanup(dir2, datadir);
+ subdir = xopendirat(dirfd(dir), dent->d_name, O_NOFOLLOW);
+ if (!subdir)
+ log_debug_errno(errno, "Failed to open subdirectory '%s', ignoring: %m", dent->d_name);
+ else
+ cleanup_dir_after_db_cleanup(subdir, datadir);
(void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR);
} else
}
case QUERY_ALL:
- return print_record(device);
+ return print_record(device, NULL);
default:
assert_not_reached();
" -r --root Prepend dev directory to path names\n"
" -a --attribute-walk Print all key matches walking along the chain\n"
" of parent devices\n"
+ " -t --tree Show tree of devices\n"
" -d --device-id-of-file=FILE Print major:minor of device containing this file\n"
" -x --export Export key/value pairs\n"
" -P --export-prefix Export the key name with a prefix\n"
return 0;
}
+static int draw_tree(
+ sd_device *parent,
+ sd_device *const array[], size_t n,
+ const char *prefix,
+ unsigned level);
+
+static int output_tree_device(
+ sd_device *device,
+ const char *str,
+ const char *prefix,
+ bool more,
+ sd_device *const array[], size_t n,
+ unsigned level) {
+
+ _cleanup_free_ char *subprefix = NULL, *subsubprefix = NULL;
+
+ assert(device);
+ assert(str);
+
+ prefix = strempty(prefix);
+
+ printf("%s%s%s\n", prefix, special_glyph(more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT), str);
+
+ subprefix = strjoin(prefix, special_glyph(more ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE));
+ if (!subprefix)
+ return log_oom();
+
+ subsubprefix = strjoin(subprefix, special_glyph(SPECIAL_GLYPH_VERTICAL_DOTTED), " ");
+ if (!subsubprefix)
+ return log_oom();
+
+ (void) print_record(device, subsubprefix);
+
+ return draw_tree(device, array, n, subprefix, level + 1);
+}
+
+static int draw_tree(
+ sd_device *parent,
+ sd_device *const array[], size_t n,
+ const char *prefix,
+ unsigned level) {
+
+ const char *parent_path;
+ size_t i = 0;
+ int r;
+
+ if (n == 0)
+ return 0;
+
+ assert(array);
+
+ if (parent) {
+ r = sd_device_get_devpath(parent, &parent_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get sysfs path of parent device: %m");
+ } else
+ parent_path = NULL;
+
+ if (level > TREE_DEPTH_MAX) {
+ log_warning("Eliding tree below '%s', too deep.", strna(parent_path));
+ return 0;
+ }
+
+ while (i < n) {
+ sd_device *device = array[i];
+ const char *device_path, *str;
+ bool more = false;
+ size_t j;
+
+ r = sd_device_get_devpath(device, &device_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get sysfs path of enumerated device: %m");
+
+ /* Scan through the subsequent devices looking children of the device we are looking at. */
+ for (j = i + 1; j < n; j++) {
+ sd_device *next = array[j];
+ const char *next_path;
+
+ r = sd_device_get_devpath(next, &next_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get sysfs of child device: %m");
+
+ if (!path_startswith(next_path, device_path)) {
+ more = !parent_path || path_startswith(next_path, parent_path);
+ break;
+ }
+ }
+
+ /* Determine the string to display for this node. If we are at the top of the tree, the full
+ * device path so far, otherwise just the part suffixing the parent's device path. */
+ str = parent ? ASSERT_PTR(path_startswith(device_path, parent_path)) : device_path;
+
+ r = output_tree_device(device, str, prefix, more, array + i + 1, j - i - 1, level);
+ if (r < 0)
+ return r;
+
+ i = j;
+ }
+
+ return 0;
+}
+
+static int print_tree(sd_device* below) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ const char *below_path;
+ sd_device **array;
+ size_t n = 0;
+ int r;
+
+ if (below) {
+ r = sd_device_get_devpath(below, &below_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get sysfs path of device: %m");
+
+ } else
+ below_path = NULL;
+
+ r = sd_device_enumerator_new(&e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate device enumerator: %m");
+
+ if (below) {
+ r = sd_device_enumerator_add_match_parent(e, below);
+ if (r < 0)
+ return log_error_errno(r, "Failed to install parent enumerator match: %m");
+ }
+
+ r = sd_device_enumerator_allow_uninitialized(e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to enable enumeration of uninitialized devices: %m");
+
+ r = device_enumerator_scan_devices_and_subsystems(e);
+ if (r < 0)
+ return log_error_errno(r, "Failed to scan for devices and subsystems: %m");
+
+ assert_se(array = device_enumerator_get_devices(e, &n));
+
+ if (n == 0) {
+ log_info("No items.");
+ return 0;
+ }
+
+ r = draw_tree(NULL, array, n, NULL, 0);
+ if (r < 0)
+ return r;
+
+ printf("\n%zu items shown.\n", n);
+ return 0;
+}
+
int info_main(int argc, char *argv[], void *userdata) {
_cleanup_strv_free_ char **devices = NULL;
_cleanup_free_ char *name = NULL;
static const struct option options[] = {
{ "attribute-walk", no_argument, NULL, 'a' },
+ { "tree", no_argument, NULL, 't' },
{ "cleanup-db", no_argument, NULL, 'c' },
{ "device-id-of-file", required_argument, NULL, 'd' },
{ "export", no_argument, NULL, 'x' },
ActionType action = ACTION_QUERY;
QueryType query = QUERY_ALL;
- while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "atced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
switch (c) {
case ARG_PROPERTY:
/* Make sure that if the empty property list was specified, we won't show any
case 'a':
action = ACTION_ATTRIBUTE_WALK;
break;
+ case 't':
+ action = ACTION_TREE;
+ break;
case 'e':
return export_devices();
case 'c':
if (r < 0)
return log_error_errno(r, "Failed to build argument list: %m");
- if (strv_isempty(devices))
+ if (action != ACTION_TREE && strv_isempty(devices))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"A device name or path is required");
- if (action == ACTION_ATTRIBUTE_WALK && strv_length(devices) > 1)
+ if (IN_SET(action, ACTION_ATTRIBUTE_WALK, ACTION_TREE) && strv_length(devices) > 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Only one device may be specified with -a/--attribute-walk");
+ "Only one device may be specified with -a/--attribute-walk and -t/--tree");
if (arg_export && arg_value)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"-x/--export or -P/--export-prefix cannot be used with --value");
+ if (strv_isempty(devices)) {
+ assert(action == ACTION_TREE);
+ pager_open(0);
+ return print_tree(NULL);
+ }
+
ret = 0;
STRV_FOREACH(p, devices) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
r = query_device(query, device);
else if (action == ACTION_ATTRIBUTE_WALK)
r = print_device_chain(device);
+ else if (action == ACTION_TREE)
+ r = print_tree(device);
else
assert_not_reached();
if (r < 0)
#include "blockdev-util.h"
#include "btrfs-util.h"
+#include "devnum-util.h"
#include "fd-util.h"
#include "fdset.h"
#include "main-func.h"
#include "process-util.h"
#include "signal-util.h"
#include "sort-util.h"
-#include "stat-util.h"
#include "strv.h"
#include "time-util.h"
#include "udevadm.h"
typedef struct Worker {
Manager *manager;
pid_t pid;
+ sd_event_source *child_event_source;
sd_device_monitor *monitor;
WorkerState state;
Event *event;
typedef enum EventResult {
EVENT_RESULT_NERRNO_MIN = -ERRNO_MAX,
EVENT_RESULT_NERRNO_MAX = -1,
+ EVENT_RESULT_SUCCESS = 0,
EVENT_RESULT_EXIT_STATUS_BASE = 0,
EVENT_RESULT_EXIT_STATUS_MAX = 255,
EVENT_RESULT_TRY_AGAIN = 256, /* when the block device is locked by another process. */
if (!worker)
return NULL;
- assert(worker->manager);
+ if (worker->manager)
+ hashmap_remove(worker->manager->workers, PID_TO_PTR(worker->pid));
- hashmap_remove(worker->manager->workers, PID_TO_PTR(worker->pid));
+ sd_event_source_unref(worker->child_event_source);
sd_device_monitor_unref(worker->monitor);
event_free(worker->event);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
+static int on_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata);
+
static int worker_new(Worker **ret, Manager *manager, sd_device_monitor *worker_monitor, pid_t pid) {
_cleanup_(worker_freep) Worker *worker = NULL;
int r;
return -ENOMEM;
*worker = (Worker) {
- .manager = manager,
.monitor = sd_device_monitor_ref(worker_monitor),
.pid = pid,
};
+ r = sd_event_add_child(manager->event, &worker->child_event_source, pid, WEXITED, on_sigchld, worker);
+ if (r < 0)
+ return r;
+
r = hashmap_ensure_put(&manager->workers, &worker_hash_op, PID_TO_PTR(pid), worker);
if (r < 0)
return r;
- *ret = TAKE_PTR(worker);
+ worker->manager = manager;
+ *ret = TAKE_PTR(worker);
return 0;
}
return 1;
}
-static void device_broadcast(sd_device_monitor *monitor, sd_device *dev, int result) {
+static void device_broadcast(sd_device_monitor *monitor, sd_device *dev, EventResult result) {
int r;
assert(dev);
if (!monitor)
return;
- if (result != 0) {
+ if (result != EVENT_RESULT_SUCCESS) {
(void) device_add_property(dev, "UDEV_WORKER_FAILED", "1");
switch (result) {
"Failed to broadcast event to libudev listeners, ignoring: %m");
}
-static int worker_send_result(Manager *manager, int result) {
+static int worker_send_result(Manager *manager, EventResult result) {
assert(manager);
assert(manager->worker_watch[WRITE_END] >= 0);
assert(manager);
for (;;) {
- int result;
+ EventResult result;
struct iovec iovec = IOVEC_MAKE(&result, sizeof(result));
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
struct msghdr msghdr = {
return 1;
}
-static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
- Manager *manager = userdata;
+static int on_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
+ Worker *worker = ASSERT_PTR(userdata);
+ Manager *manager = ASSERT_PTR(worker->manager);
+ sd_device *dev = worker->event ? ASSERT_PTR(worker->event->dev) : NULL;
+ EventResult result;
int r;
- assert(manager);
-
- for (;;) {
- pid_t pid;
- int status;
- Worker *worker;
-
- pid = waitpid(-1, &status, WNOHANG);
- if (pid <= 0)
- break;
-
- worker = hashmap_get(manager->workers, PID_TO_PTR(pid));
- if (!worker) {
- log_warning("Worker ["PID_FMT"] is unknown, ignoring", pid);
- continue;
- }
+ assert(si);
- if (WIFEXITED(status)) {
- if (WEXITSTATUS(status) == 0)
- log_debug("Worker ["PID_FMT"] exited", pid);
- else
- log_warning("Worker ["PID_FMT"] exited with return code %i", pid, WEXITSTATUS(status));
- } else if (WIFSIGNALED(status))
- log_warning("Worker ["PID_FMT"] terminated by signal %i (%s)", pid, WTERMSIG(status), signal_to_string(WTERMSIG(status)));
- else if (WIFSTOPPED(status)) {
- log_info("Worker ["PID_FMT"] stopped", pid);
- continue;
- } else if (WIFCONTINUED(status)) {
- log_info("Worker ["PID_FMT"] continued", pid);
- continue;
- } else
- log_warning("Worker ["PID_FMT"] exit with status 0x%04x", pid, status);
+ switch (si->si_code) {
+ case CLD_EXITED:
+ if (si->si_status == 0)
+ log_device_debug(dev, "Worker ["PID_FMT"] exited.", si->si_pid);
+ else
+ log_device_warning(dev, "Worker ["PID_FMT"] exited with return code %i.",
+ si->si_pid, si->si_status);
+ result = EVENT_RESULT_EXIT_STATUS_BASE + si->si_status;
+ break;
- if ((!WIFEXITED(status) || WEXITSTATUS(status) != 0) && worker->event) {
- log_device_error(worker->event->dev, "Worker ["PID_FMT"] failed", pid);
+ case CLD_KILLED:
+ case CLD_DUMPED:
+ log_device_warning(dev, "Worker ["PID_FMT"] terminated by signal %i (%s).",
+ si->si_pid, si->si_status, signal_to_string(si->si_status));
+ result = EVENT_RESULT_SIGNAL_BASE + si->si_status;
+ break;
- /* delete state from disk */
- device_delete_db(worker->event->dev);
- device_tag_index(worker->event->dev, NULL, false);
+ default:
+ assert_not_reached();
+ }
- /* Forward kernel event to libudev listeners */
- device_broadcast(manager->monitor, worker->event->dev,
- WIFEXITED(status) ? EVENT_RESULT_EXIT_STATUS_BASE + WEXITSTATUS(status):
- WIFSIGNALED(status) ? EVENT_RESULT_SIGNAL_BASE + WTERMSIG(status) : 0);
- }
+ if (result != EVENT_RESULT_SUCCESS && dev) {
+ /* delete state from disk */
+ device_delete_db(dev);
+ device_tag_index(dev, NULL, false);
- worker_free(worker);
+ /* Forward kernel event to libudev listeners */
+ device_broadcast(manager->monitor, dev, result);
}
+ worker_free(worker);
+
/* we can start new workers, try to schedule events */
event_queue_start(manager);
if (r < 0)
return log_error_errno(r, "Failed to create SIGHUP event source: %m");
- r = sd_event_add_signal(manager->event, NULL, SIGCHLD, on_sigchld, manager);
- if (r < 0)
- return log_error_errno(r, "Failed to create SIGCHLD event source: %m");
-
r = sd_event_set_watchdog(manager->event, true);
if (r < 0)
return log_error_errno(r, "Failed to create watchdog event source: %m");
#include "alloc-util.h"
#include "blockdev-util.h"
#include "chase-symlinks.h"
+#include "devnum-util.h"
#include "escape.h"
#include "main-func.h"
#include "mkdir.h"
#include "mount-util.h"
#include "mountpoint-util.h"
#include "path-util.h"
-#include "stat-util.h"
#include "string-util.h"
#include "volatile-util.h"
def tearDownModule():
global tmpmounts
for d in tmpmounts:
- subprocess.check_call(["umount", d])
+ subprocess.check_call(["umount", "--lazy", d])
for u in stopped_units:
subprocess.call(["systemctl", "stop", u])
for u in running_units:
}
}
+# Relax sd-device's sysfs verification, since we want to provide a fake sysfs
+# here that actually is a tmpfs.
+$ENV{"SYSTEMD_DEVICE_VERIFY_SYSFS"}="0";
+
my $udev_bin = "./test-udev";
my $valgrind = 0;
my $gdb = 0;