Compare commits

...

38 Commits

Author SHA1 Message Date
Lennart Poettering ed42306c58
Merge 67591fd782 into 321c202e7c 2024-11-25 23:29:33 +08:00
Luca Boccassi 321c202e7c
man: assorted fixes (#35326)
Closes #35307.
2024-11-25 15:02:08 +00:00
Daan De Meyer e3b5a0c32d test: Use env in testsuite readme
Let's make sure we use env when we're setting environment variables
to rely less on shell specifics.
2024-11-25 14:54:23 +00:00
Zbigniew Jędrzejewski-Szmek 766d74fd8b
core/device: ignore ID_PROCESSING udev property on enumerate (#35332)
Fixes #35329.
2024-11-25 14:21:36 +01:00
Zbigniew Jędrzejewski-Szmek d293fade24
Check inode number to see if we are in init namespace (#35306)
This is a more comprehensive fix compared to #35273. Also adds a minimal
test only.

Based on Luca's #35273 but generalizes the code a bit.

In v258 we really should get rid of the old heuristics around userns and
cgroupns detection, but given we are late in the v257 cycle this keeps
them in.
2024-11-25 14:13:36 +01:00
Daan De Meyer 4a346b779a test: Dump coredumps from journal in the integration test wrapper
Fixes #35277
2024-11-25 19:12:11 +09:00
Yu Watanabe 0e42004f3e networkd-test.py: disable IPv6AcceptRA= if not necessary
To speed up the test. Otherwise, it takes about few seconds interfaces
to enter the configured state. And may networkd-wait-online timeouts.
2024-11-25 10:07:26 +00:00
Yu Watanabe 675feaf521 TEST-17: add reproducer for issue #35329
Without the previous commit, the test case will fail.
2024-11-25 15:33:48 +09:00
Yu Watanabe c4fc22c4de core/device: ignore ID_PROCESSING udev property on enumerate
This partially reverts the commit 405be62f05
"tree-wide: refuse enumerated device with ID_PROCESSING=1".

Otherwise, when systemd-udev-trigger.service is (re)started just before
daemon-reexec, which can be easily happen on systemd package update, then
udev database files for many devices may have ID_PROCESSING=1 property,
thus devices may not be enumerated on daemon-reexec. That causes many
units especially mount units being deactivated after daemon-reexec.

Fixes #35329.
2024-11-25 15:33:48 +09:00
Yu Watanabe d07fbf22ed man: update documentation about basic .netdev file handling
Follow-up for #34909 and later PRs.
2024-11-24 01:11:46 +09:00
Yu Watanabe 4ebbb5bfe8 man: asorted fixes
Closes #35307.
2024-11-24 01:11:42 +09:00
Lennart Poettering 67591fd782 pam-systemd: talk to logind via varlink
This makes sure we now use Varlink per default as transport for
allocating sessions.

This reduces the time it takes to do one run0 cycle by roughly ~10% on my
completely synthetic test setup (assuming the target user's service
manager is already started)

The D-Bus codepaths are kept in place for two reasons:
* To make upgrades easy
* If the user actually sets resource properties on the PAM session we
  fall back to the D-Bus codepaths, as we currently have no way to
  encode the scope properties in JSON, this is only supported for D-Bus
  serialization.

The latter should be revisited once it is possible to allocate a scope
unit from PID1 via varlink.
2024-11-23 00:21:44 +01:00
Lennart Poettering 759036b42f logind: add basic Varlink API
For now this only covers CreateSession() and ReleaseSession(), i.e. the
two operations pam_systemd cares about.
2024-11-23 00:18:46 +01:00
Lennart Poettering 3ccbb4e518 logind: split create session reply handling in two
This prepares ground so that later on we can reply with either D-Bus or
Varlink depending on the client's request.
2024-11-23 00:18:46 +01:00
Lennart Poettering 70731b1425 logind: also potentially GC the session if we cannot send reply 2024-11-23 00:18:46 +01:00
Lennart Poettering a098cdbefe logind: indicate that 'error' parameter is input by making it const 2024-11-23 00:18:46 +01:00
Lennart Poettering b5dd575cb3 logind: rework session creation logic, to be more reusable for varlink codepaths
This separates the preparatory checks that generate D-Bus errors from
the code that actually allocates the session. This make the logic easier
to follow and prepares ground so that we can reuse the 2nd part later
when exposing session creation via Varlink.
2024-11-23 00:18:46 +01:00
Lennart Poettering c771a195b6 logind: split out logic that finds free session ID into helper call
Just some refactoring to make an overly large function a bit smaller.
2024-11-23 00:18:46 +01:00
Lennart Poettering d30ae4c4d2 logind: normalize parameter to create_session()
We can pass a properly typed Manager object here, no reason to pass it
as void*.
2024-11-23 00:18:46 +01:00
Lennart Poettering 49e778d40a sd-varlink: fix bug when enqueuing messages with fds asynchronously
When determining the poll events to wait for we need to take the queue
of pending messages that carry fds into account. Otherwise we might end
up not waking up if such an fd-carrying message is enqueued
asynchronously (i.e. not from a dispatch callback).
2024-11-23 00:18:46 +01:00
Lennart Poettering 1f377e6f73 sd-varlink: add flag for sd_varlink_server for creating connections with fd passing enabled
Let's add a simple flag that enables fd passing for all connections of a
server. It's much easier to use this than to install a connect handler
which manually enables this for each connection.
2024-11-23 00:18:46 +01:00
Lennart Poettering 69c59f707c terminal-util: modernize vtnr_from_tty() a bit 2024-11-23 00:18:46 +01:00
Lennart Poettering ccfdaa7117 sd-login: make use of getpeerpidref() and cg_pidref_get_*() 2024-11-23 00:18:46 +01:00
Lennart Poettering 32f31fa6bf socket-util: introduce getpeerpidref()
This combines getpeercred() and getpeerpidfd() and returns a PidRef
2024-11-23 00:18:46 +01:00
Lennart Poettering eb87a43e9d cgroup-util: add pidref counterparts for cg_pid_get_session() + cg_pid_get_owner_uid() 2024-11-23 00:18:46 +01:00
Lennart Poettering 05e4039436 tree-wide: use pidref_is_self() at more places 2024-11-23 00:18:46 +01:00
Lennart Poettering b9e124b84c sd-json: add json_dispatch_const_path() helper
The new json_dispatch_const_path() is to json_dispatch_path() what
sd_json_dispatch_const_string() is to sd_json_dispatch_ string(), i.e.
doesn't implicitly strdup() the string, but gives you the pointer into
the JSON structure, and thus requires you to keep it pinned.
2024-11-23 00:18:46 +01:00
Lennart Poettering eb543b192e pam_systemd: introduce pam_get_data_many() helper and make use of it
This is to pam_get_data() what pam_get_item() is to pam_get_item_many().
2024-11-23 00:18:46 +01:00
Lennart Poettering 19159b60c9 pam_systemd: fix error code confusion when prepping D-Bus message
We got confused by the error codes here, and sometimes return PAM errors
where the caller propagated them unconverted as negative errno errors. Fix that.
2024-11-23 00:18:46 +01:00
Lennart Poettering b5f653430e pam_systemd: split pam_sm_open_session() into more digestable blocks
Let's separate four different parts of pam_sm_open_session():

1. Acquiring of our various parameters from pam env, pam data, pam items
2. Mangling of that data to clean it up
3. Registering of the service with logind
4. Importing shell credentials into environment variables
5. Enforcement of user record data

This makes the code a lot more readable, and gets rid of an ugly got
label.

It also corrects things: if step 3 doesnt work because logind is not
around, we'll now still do step 4, which we previously erroneously
skipped.

Besodes that no real code changes.
2024-11-23 00:18:46 +01:00
Lennart Poettering 293efc0a01 pam_systemd: split out setting of shell env vars from credentials and move it later
Let's shorten the code of pam_sm_open_session() a bit, and also make
sure the importing of the env vars from the creds also happens if the
session registration with logind is skipped.
2024-11-23 00:18:46 +01:00
Lennart Poettering a14cc13650 pam_systemd: drop "uid" field from SessionContext
Let's instead just pass over the UserRecord, it's a much more useful
object with lots more information we'll sooner or later need
(preparation for later commits).
2024-11-23 00:18:46 +01:00
Lennart Poettering 796c8d3ecf pam_systemd: drop "pid" field from SessionContext
We never use the field and this is not going to change...

This addresses a weird asymmetry, as create_session_message() always
went to the process' own PID when doing pidfds but otherwise (i.e.
without pidfds) would honour the PID specified as function parameter.
2024-11-23 00:18:46 +01:00
Lennart Poettering dea25e026f pam-systemd: normalize parsing of XDG_VTNR
Let's make it more like the parsing of the "incomplete" boolean env var,
to streamline things.
2024-11-23 00:18:46 +01:00
Lennart Poettering 4b4af14a98 test-namespace: tweak log message a bit 2024-11-23 00:14:20 +01:00
Lennart Poettering a2429f507c virt: make use of ns inode check in running_in_userns() and running_in_cgroupns() too 2024-11-23 00:14:20 +01:00
Luca Boccassi 193bf42ab0 detect-virt: check the inode number of the pid namespace
The indoe number of root pid namespace is hardcoded in the kernel to
0xEFFFFFFC since 3.8, so check the inode number of our pid namespace
if all else fails. If it's not 0xEFFFFFFC then we are in a pid
namespace, hence a container environment.

Fixes https://github.com/systemd/systemd/issues/35249

[Reworked by Lennart, to make use of namespace_is_init()]
2024-11-23 00:14:20 +01:00
Lennart Poettering 18ead2b03d namespace-util: add generic namespace_is_init() call 2024-11-23 00:14:20 +01:00
95 changed files with 2036 additions and 790 deletions

View File

@ -114,10 +114,10 @@
invoked, for example from the system service manager or via a PAM module.</para> invoked, for example from the system service manager or via a PAM module.</para>
<para>Specifically, for ssh logins, the <para>Specifically, for ssh logins, the
<citerefentry project='die-net'><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry> <citerefentry project='man-pages'><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
service builds an environment that is a combination of variables forwarded from the remote system and service builds an environment that is a combination of variables forwarded from the remote system and
defined by <command>sshd</command>, see the discussion in defined by <command>sshd</command>, see the discussion in
<citerefentry project='die-net'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>. <citerefentry project='man-pages'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
A graphical display session will have an analogous mechanism to define the environment. Note that some A graphical display session will have an analogous mechanism to define the environment. Note that some
managers query the systemd user instance for the exported environment and inject this configuration into managers query the systemd user instance for the exported environment and inject this configuration into
programs they start, using <command>systemctl show-environment</command> or the underlying D-Bus call. programs they start, using <command>systemctl show-environment</command> or the underlying D-Bus call.

View File

@ -215,8 +215,8 @@
below this directory is subject to specifications that ensure interoperability.</para> below this directory is subject to specifications that ensure interoperability.</para>
<para>Note that resources placed in this directory typically are under shared ownership, <para>Note that resources placed in this directory typically are under shared ownership,
i.e. multiple different packages have provide and consume these resources, on equal footing, without i.e. multiple different packages have provided and consumed these resources, on equal footing, without
any obvious primary owner. This makes makes things systematically different from any obvious primary owner. This makes things systematically different from
<filename>/usr/lib/</filename>, where ownership is generally not shared.</para></listitem> <filename>/usr/lib/</filename>, where ownership is generally not shared.</para></listitem>
</varlistentry> </varlistentry>

View File

@ -378,7 +378,7 @@
<listitem><para>Takes a comma- or colon-separated list of languages preferred by the user, ordered <listitem><para>Takes a comma- or colon-separated list of languages preferred by the user, ordered
by descending priority. The <varname>$LANG</varname> and <varname>$LANGUAGE</varname> environment by descending priority. The <varname>$LANG</varname> and <varname>$LANGUAGE</varname> environment
variables are initialized from this value on login, and thus values suitible for these environment variables are initialized from this value on login, and thus values suitable for these environment
variables are accepted here, for example <option>--language=de_DE.UTF-8</option>. This option may variables are accepted here, for example <option>--language=de_DE.UTF-8</option>. This option may
be used more than once, in which case the language lists are concatenated.</para> be used more than once, in which case the language lists are concatenated.</para>

View File

@ -40,7 +40,7 @@
<citerefentry><refentrytitle>systemd-importd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para> <citerefentry><refentrytitle>systemd-importd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para><command>importctl</command> operates both on block-level disk images (such as DDIs) as well as <para><command>importctl</command> operates both on block-level disk images (such as DDIs) as well as
file-system-level images (tarballs). It supports disk images are one of the four following file-system-level images (tarballs). It supports disk images in one of the four following
classes:</para> classes:</para>
<itemizedlist> <itemizedlist>
@ -50,7 +50,7 @@
managed via managed via
<citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para></listitem> <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para></listitem>
<listitem><para>Portable service images, that may be attached an managed via <listitem><para>Portable service images, that may be attached and managed via
<citerefentry><refentrytitle>portablectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para></listitem> <citerefentry><refentrytitle>portablectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para></listitem>
<listitem><para>System extension (sysext) images, that may be activated via <listitem><para>System extension (sysext) images, that may be activated via
@ -133,7 +133,7 @@
multiple downloads are not necessary. In order to create only the read-only image, and avoid creating multiple downloads are not necessary. In order to create only the read-only image, and avoid creating
its writable snapshot, specify <literal>-</literal> as local name.</para> its writable snapshot, specify <literal>-</literal> as local name.</para>
<para>Note that pressing C-c during execution of this command will not abort the download. Use <para>Note that pressing Control-c during execution of this command will not abort the download. Use
<command>cancel-transfer</command>, described below.</para> <command>cancel-transfer</command>, described below.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem> <xi:include href="version-info.xml" xpointer="v256"/></listitem>
@ -145,14 +145,14 @@
<listitem><para>Downloads a <filename>.raw</filename> disk image from the specified URL, and makes it <listitem><para>Downloads a <filename>.raw</filename> disk image from the specified URL, and makes it
available under the specified local name in the image directory for the selected available under the specified local name in the image directory for the selected
<option>--class=</option>. The URL must be of type <literal>http://</literal> or <option>--class=</option>. The URL must be of type <literal>http://</literal> or
<literal>https://</literal>. The image must either be a <filename>.qcow2</filename> or raw disk <literal>https://</literal>. The image must either be a qcow2 or raw disk
image, optionally compressed as <filename>.gz</filename>, <filename>.xz</filename>, or image, optionally compressed as <filename>.gz</filename>, <filename>.xz</filename>, or
<filename>.bz2</filename>. If the local name is omitted, it is automatically derived from the last <filename>.bz2</filename>. If the local name is omitted, it is automatically derived from the last
component of the URL, with its suffix removed.</para> component of the URL, with its suffix removed.</para>
<para>Image verification is identical for raw and tar images (see above).</para> <para>Image verification is identical for raw and tar images (see above).</para>
<para>If the downloaded image is in <filename>.qcow2</filename> format it is converted into a raw <para>If the downloaded image is in qcow2 format it is converted into a raw
image file before it is made available.</para> image file before it is made available.</para>
<para>If <option>-keep-download=yes</option> is specified the image will be downloaded and stored in <para>If <option>-keep-download=yes</option> is specified the image will be downloaded and stored in
@ -162,7 +162,7 @@
necessary. In order to create only the read-only image, and avoid creating its writable copy, necessary. In order to create only the read-only image, and avoid creating its writable copy,
specify <literal>-</literal> as local name.</para> specify <literal>-</literal> as local name.</para>
<para>Note that pressing C-c during execution of this command will not abort the download. Use <para>Note that pressing Control-c during execution of this command will not abort the download. Use
<command>cancel-transfer</command>, described below.</para> <command>cancel-transfer</command>, described below.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem> <xi:include href="version-info.xml" xpointer="v256"/></listitem>
@ -174,8 +174,14 @@
<listitem><para>Imports a TAR or RAW image, and places it under the specified name in the image <listitem><para>Imports a TAR or RAW image, and places it under the specified name in the image
directory for the image class selected via <option>--class=</option>. When directory for the image class selected via <option>--class=</option>. When
<command>import-tar</command> is used, the file specified as the first argument should be a tar <command>import-tar</command> is used, the file specified as the first argument should be a
archive, possibly compressed with xz, gzip or bzip2. It will then be unpacked into its own <citerefentry project='die-net'><refentrytitle>tar</refentrytitle><manvolnum>1</manvolnum></citerefentry>
archive, possibly compressed with
<citerefentry project='die-net'><refentrytitle>xz</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry project='die-net'><refentrytitle>gzip</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
or
<citerefentry project='die-net'><refentrytitle>bzip2</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
It will then be unpacked into its own
subvolume/directory. When <command>import-raw</command> is used, the file should be a qcow2 or raw subvolume/directory. When <command>import-raw</command> is used, the file should be a qcow2 or raw
disk image, possibly compressed with xz, gzip or bzip2. If the second argument (the resulting image disk image, possibly compressed with xz, gzip or bzip2. If the second argument (the resulting image
name) is not specified, it is automatically derived from the file name. If the filename is passed as name) is not specified, it is automatically derived from the file name. If the filename is passed as
@ -196,7 +202,9 @@
<listitem><para>Imports an image stored in a local directory into the image directory for the image <listitem><para>Imports an image stored in a local directory into the image directory for the image
class selected via <option>--class=</option> and operates similarly to <command>import-tar</command> class selected via <option>--class=</option> and operates similarly to <command>import-tar</command>
or <command>import-raw</command>, but the first argument is the source directory. If supported, this or <command>import-raw</command>, but the first argument is the source directory. If supported, this
command will create a btrfs snapshot or subvolume for the new image.</para> command will create a
<citerefentry project="url"><refentrytitle url="https://btrfs.readthedocs.io/en/latest/btrfs.html">btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
snapshot or subvolume for the new image.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem> <xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry> </varlistentry>
@ -207,9 +215,13 @@
<listitem><para>Exports a TAR or RAW image and stores it in the specified file. The first parameter <listitem><para>Exports a TAR or RAW image and stores it in the specified file. The first parameter
should be an image name. The second parameter should be a file path the TAR or RAW should be an image name. The second parameter should be a file path the TAR or RAW
image is written to. If the path ends in <literal>.gz</literal>, the file is compressed with gzip, if image is written to. If the path ends in <literal>.gz</literal>, the file is compressed with
it ends in <literal>.xz</literal>, with xz, and if it ends in <literal>.bz2</literal>, with bzip2. If <citerefentry project='die-net'><refentrytitle>gzip</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
the path ends in neither, the file is left uncompressed. If the second argument is missing, the image if it ends in <literal>.xz</literal>, with
<citerefentry project='die-net'><refentrytitle>xz</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
and if it ends in <literal>.bz2</literal>, with
<citerefentry project='die-net'><refentrytitle>bzip2</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
If the path ends in neither, the file is left uncompressed. If the second argument is missing, the image
is written to standard output. The compression may also be explicitly selected with the is written to standard output. The compression may also be explicitly selected with the
<option>--format=</option> switch. This is in particular useful if the second parameter is left <option>--format=</option> switch. This is in particular useful if the second parameter is left
unspecified.</para> unspecified.</para>

View File

@ -113,11 +113,11 @@
</row> </row>
<row> <row>
<entry><constant>user-early</constant></entry> <entry><constant>user-early</constant></entry>
<entry>Similar to <literal>user</literal> but sessions of this class are not ordered after <filename>systemd-user-sessions.service</filename>, i.e. may be started before regular sessions are allowed to be established. This session class is the default for sessions of the root user that would otherwise qualify for the <constant>user</constant> class, see above. (Added in v256.)</entry> <entry>Similar to <literal>user</literal> but sessions of this class are not ordered after <citerefentry><refentrytitle>systemd-user-sessions.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>, i.e. may be started before regular sessions are allowed to be established. This session class is the default for sessions of the root user that would otherwise qualify for the <constant>user</constant> class, see above. (Added in v256.)</entry>
</row> </row>
<row> <row>
<entry><constant>user-incomplete</constant></entry> <entry><constant>user-incomplete</constant></entry>
<entry>Similar to <literal>user</literal> but for sessions which are not fully set up yet, i.e. have no home directory mounted or similar. This is used by <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> to allow users to log in via <command>ssh</command> before their home directory is mounted, delaying the mount until the user provided the unlock password. Sessions of this class are upgraded to the regular <constant>user</constant> class once the home directory is activated.</entry> <entry>Similar to <literal>user</literal> but for sessions which are not fully set up yet, i.e. have no home directory mounted or similar. This is used by <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> to allow users to log in via <citerefentry project='man-pages'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry> before their home directory is mounted, delaying the mount until the user provided the unlock password. Sessions of this class are upgraded to the regular <constant>user</constant> class once the home directory is activated.</entry>
</row> </row>
<row> <row>
<entry><constant>greeter</constant></entry> <entry><constant>greeter</constant></entry>
@ -129,15 +129,15 @@
</row> </row>
<row> <row>
<entry><constant>background</constant></entry> <entry><constant>background</constant></entry>
<entry>Used for background sessions, such as those invoked by <command>cron</command> and similar tools. This is the default class for sessions for which no TTY or X display is known at session registration time.</entry> <entry>Used for background sessions, such as those invoked by <citerefentry project='die-net'><refentrytitle>cron</refentrytitle><manvolnum>8</manvolnum></citerefentry> and similar tools. This is the default class for sessions for which no TTY or X display is known at session registration time.</entry>
</row> </row>
<row> <row>
<entry><constant>background-light</constant></entry> <entry><constant>background-light</constant></entry>
<entry>Similar to <constant>background</constant>, but sessions of this class will not pull in the <filename>user@.service</filename> of the user, and thus possibly have no services of the user running. (Added in v256.)</entry> <entry>Similar to <constant>background</constant>, but sessions of this class will not pull in the <citerefentry><refentrytitle>user@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> of the user, and thus possibly have no services of the user running. (Added in v256.)</entry>
</row> </row>
<row> <row>
<entry><constant>manager</constant></entry> <entry><constant>manager</constant></entry>
<entry>The <filename>user@.service</filename> service of the user is registered under this session class. (Added in v256.)</entry> <entry>The <citerefentry><refentrytitle>user@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry> service of the user is registered under this session class. (Added in v256.)</entry>
</row> </row>
<row> <row>
<entry><constant>manager-early</constant></entry> <entry><constant>manager-early</constant></entry>
@ -445,6 +445,8 @@ session required pam_unix.so</programlisting>
<title>See Also</title> <title>See Also</title>
<para><simplelist type="inline"> <para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-user-sessions.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>user@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-logind.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>systemd-logind.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>loginctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>loginctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>

View File

@ -112,7 +112,8 @@
during boot.</para> during boot.</para>
<para>You need to set the password of your Gnome Keyring/KWallet to the same as your LUKS passphrase. <para>You need to set the password of your Gnome Keyring/KWallet to the same as your LUKS passphrase.
Then add the following lines to your display manager's PAM config under <filename>/etc/pam.d/</filename> (e.g. <filename>sddm-autologin</filename>):</para> Then add the following lines to your display manager's PAM config under <filename>/etc/pam.d/</filename> (e.g.
<filename>sddm-autologin</filename>):</para>
<programlisting> <programlisting>
-auth optional pam_systemd_loadkey.so -auth optional pam_systemd_loadkey.so
@ -131,8 +132,9 @@ KeyringMode=inherit
<para>In this setup, early during the boot process, <para>In this setup, early during the boot process,
<citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> <citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
will ask for the passphrase and store it in the kernel keyring with the keyname <literal>cryptsetup</literal>. will ask for the passphrase and store it in the kernel keyring with the keyname <literal>cryptsetup</literal>.
Then when the display manager does the autologin, pam_systemd_loadkey will read the passphrase from the kernel keyring, Then when the display manager does the autologin, <command>pam_systemd_loadkey</command> will read the passphrase
set it as the PAM authtok, and then pam_gnome_keyring and pam_kwallet5 will unlock with the same passphrase.</para> from the kernel keyring, set it as the PAM authtok, and then <command>pam_gnome_keyring</command> and
<command>pam_kwallet5</command> will unlock with the same passphrase.</para>
</refsect1> </refsect1>
</refentry> </refentry>

View File

@ -48,7 +48,7 @@
and transfer them as a whole between systems. When these images are attached to the local system, the contained units and transfer them as a whole between systems. When these images are attached to the local system, the contained units
may run in most ways like regular system-provided units, either with full privileges or inside strict sandboxing, may run in most ways like regular system-provided units, either with full privileges or inside strict sandboxing,
depending on the selected configuration. For more details, see depending on the selected configuration. For more details, see
<ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services Documentation</ulink>.</para> <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink>.</para>
<para>Portable service images may be of the following kinds:</para> <para>Portable service images may be of the following kinds:</para>
@ -417,7 +417,7 @@
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>. <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
Images can be block images, btrfs subvolumes or directories. For more information on portable Images can be block images, btrfs subvolumes or directories. For more information on portable
services with extensions, see the <literal>Extension Images</literal> paragraph on services with extensions, see the <literal>Extension Images</literal> paragraph on
<ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services Documentation</ulink>. <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink>.
</para> </para>
<para>Note that the same extensions have to be specified, in the same order, when attaching <para>Note that the same extensions have to be specified, in the same order, when attaching

View File

@ -606,7 +606,8 @@
<varname>Subvolumes=</varname>.</para> <varname>Subvolumes=</varname>.</para>
<para>Note that this option only takes effect if the target filesystem supports subvolumes, such as <para>Note that this option only takes effect if the target filesystem supports subvolumes, such as
<literal>btrfs</literal>.</para> <citerefentry project="url"><refentrytitle url="https://btrfs.readthedocs.io/en/latest/btrfs.html">btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
</para>
<para>Note that this option is only supported in combination with <option>--offline=yes</option> <para>Note that this option is only supported in combination with <option>--offline=yes</option>
since btrfs-progs 6.11 or newer.</para> since btrfs-progs 6.11 or newer.</para>
@ -686,7 +687,7 @@
<listitem><para>Configures the data block size of the generated verity hash partition. Must be between 512 and <listitem><para>Configures the data block size of the generated verity hash partition. Must be between 512 and
4096 bytes and must be a power of 2. Defaults to the sector size if configured explicitly, or the underlying 4096 bytes and must be a power of 2. Defaults to the sector size if configured explicitly, or the underlying
block device sector size, or 4K if systemd-repart is not operating on a block device. block device sector size, or 4K if <command>systemd-repart</command> is not operating on a block device.
</para> </para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem> <xi:include href="version-info.xml" xpointer="v255"/></listitem>
@ -697,7 +698,7 @@
<listitem><para>Configures the hash block size of the generated verity hash partition. Must be between 512 and <listitem><para>Configures the hash block size of the generated verity hash partition. Must be between 512 and
4096 bytes and must be a power of 2. Defaults to the sector size if configured explicitly, or the underlying 4096 bytes and must be a power of 2. Defaults to the sector size if configured explicitly, or the underlying
block device sector size, or 4K if systemd-repart is not operating on a block device. block device sector size, or 4K if <command>systemd-repart</command> is not operating on a block device.
</para> </para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem> <xi:include href="version-info.xml" xpointer="v255"/></listitem>
@ -807,7 +808,9 @@
mount options. These fields correspond to the second and fourth column of the mount options. These fields correspond to the second and fourth column of the
<citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry> <citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>
format. This setting may be specified multiple times to mount the partition multiple times. This can format. This setting may be specified multiple times to mount the partition multiple times. This can
be used to add mounts for different btrfs subvolumes located on the same btrfs partition.</para> be used to add mounts for different
<citerefentry project="url"><refentrytitle url="https://btrfs.readthedocs.io/en/latest/btrfs.html">btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
subvolumes located on the same btrfs partition.</para>
<para>Note that this setting is only taken into account when <option>--generate-fstab=</option> is <para>Note that this setting is only taken into account when <option>--generate-fstab=</option> is
specified on the <command>systemd-repart</command> command line.</para> specified on the <command>systemd-repart</command> command line.</para>
@ -818,7 +821,7 @@
<varlistentry> <varlistentry>
<term><varname>EncryptedVolume=</varname></term> <term><varname>EncryptedVolume=</varname></term>
<listitem><para>Specify how the encrypted partition should be set up. Takes at least one and at most <listitem><para>Specifies how the encrypted partition should be set up. Takes at least one and at most
three fields separated with a colon (<literal>:</literal>). The first field specifies the encrypted three fields separated with a colon (<literal>:</literal>). The first field specifies the encrypted
volume name under <filename>/dev/mapper/</filename>. If not specified, <literal>luks-UUID</literal> volume name under <filename>/dev/mapper/</filename>. If not specified, <literal>luks-UUID</literal>
will be used where <literal>UUID</literal> is the LUKS UUID. The second field specifies the keyfile will be used where <literal>UUID</literal> is the LUKS UUID. The second field specifies the keyfile
@ -837,13 +840,14 @@
<varlistentry> <varlistentry>
<term><varname>Compression=</varname></term> <term><varname>Compression=</varname></term>
<listitem><para>Specify the compression algorithm to use for the filesystem configured with <listitem><para>Specifies the compression algorithm to use for the filesystem configured with
<varname>Format=</varname>. Takes a single argument specifying the compression algorithm.</para> <varname>Format=</varname>. Takes a single argument specifying the compression algorithm.</para>
<para>Note that this setting is only taken into account when the filesystem configured with <para>Note that this setting is only taken into account when the filesystem configured with
<varname>Format=</varname> supports compression (btrfs, squashfs, erofs). Here's an incomplete list <varname>Format=</varname> supports compression (
of compression algorithms supported by the filesystems known to <citerefentry project="url"><refentrytitle url="https://btrfs.readthedocs.io/en/latest/btrfs.html">btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<command>systemd-repart</command>:</para> squashfs, erofs). Here's an incomplete list of compression algorithms supported by the filesystems
known to <command>systemd-repart</command>:</para>
<table> <table>
<title>File System Compression Algorithms</title> <title>File System Compression Algorithms</title>
@ -883,7 +887,7 @@
<varlistentry> <varlistentry>
<term><varname>CompressionLevel=</varname></term> <term><varname>CompressionLevel=</varname></term>
<listitem><para>Specify the compression level to use for the filesystem configured with <listitem><para>Specifies the compression level to use for the filesystem configured with
<varname>Format=</varname>. Takes a single argument specifying the compression level to use for the <varname>Format=</varname>. Takes a single argument specifying the compression level to use for the
configured compression algorithm. The possible compression levels and their meaning are filesystem configured compression algorithm. The possible compression levels and their meaning are filesystem
specific (refer to the filesystem's documentation for the exact meaning of a particular compression specific (refer to the filesystem's documentation for the exact meaning of a particular compression

View File

@ -485,7 +485,7 @@
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If <listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If
true, rules regarding routing of single-label names are relaxed. Defaults to false. By default, true, rules regarding routing of single-label names are relaxed. Defaults to false. By default,
lookups of single label names are assumed to refer to local hosts to be resolved via local resolution lookups of single-label names are assumed to refer to local hosts to be resolved via local resolution
such as LLMNR or via search domain qualification and are not routed to upstream servers as is. If such as LLMNR or via search domain qualification and are not routed to upstream servers as is. If
this option is enabled these rules are disabled and the queries are routed upstream anyway. Also see this option is enabled these rules are disabled and the queries are routed upstream anyway. Also see
the <varname>ResolveUnicastSingleLabel=</varname> option in the <varname>ResolveUnicastSingleLabel=</varname> option in

View File

@ -81,7 +81,7 @@
<varlistentry> <varlistentry>
<term><option>--property=</option></term> <term><option>--property=</option></term>
<listitem><para>Sets a property on the service unit that is created. This option takes an assignment <listitem><para>Sets a property of the service unit that is created. This option takes an assignment
in the same format as in the same format as
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<command>set-property</command> command.</para> <command>set-property</command> command.</para>
@ -225,7 +225,7 @@
<term><option>--machine=</option></term> <term><option>--machine=</option></term>
<listitem> <listitem>
<para>Execute operation on a local container. Specify a container name to connect to.</para> <para>Execute operation in a local container. Specify a container name to connect to.</para>
<xi:include href="version-info.xml" xpointer="v256"/> <xi:include href="version-info.xml" xpointer="v256"/>
</listitem> </listitem>

View File

@ -1397,7 +1397,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<para>Note that this shows the <emphasis>effective</emphasis> block, i.e. the combination of <para>Note that this shows the <emphasis>effective</emphasis> block, i.e. the combination of
environment variables configured via configuration files, environment generators and via IPC environment variables configured via configuration files, environment generators and via IPC
(i.e. via the <command>set-environment</command> described below). At the moment a unit process (i.e. via the <command>set-environment</command> described below). At the moment a unit process
is forked off this combined environment block will be further combined with per-unit environment is forked off, this combined environment block will be further combined with per-unit environment
variables, which are not visible in this command.</para> variables, which are not visible in this command.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -54,7 +54,7 @@
<listitem><para>The EFI Shell binary, if installed.</para></listitem> <listitem><para>The EFI Shell binary, if installed.</para></listitem>
<listitem><para>A <literal>Reboot Into Firmware Interface option</literal>, if supported by the UEFI <listitem><para>A <literal>Reboot Into Firmware Interface</literal> option, if supported by the UEFI
firmware.</para></listitem> firmware.</para></listitem>
<listitem><para>Secure Boot variables enrollment if the UEFI firmware is in setup-mode and files are provided <listitem><para>Secure Boot variables enrollment if the UEFI firmware is in setup-mode and files are provided

View File

@ -299,7 +299,7 @@
<varlistentry> <varlistentry>
<term><option>--unlock-tpm2-device=<replaceable>PATH</replaceable></option></term> <term><option>--unlock-tpm2-device=<replaceable>PATH</replaceable></option></term>
<listitem><para>Use a TPM2 device instead of a password/passhprase read from stdin to unlock the <listitem><para>Use a TPM2 device instead of a password/passphrase read from stdin to unlock the
volume. Expects a device node path referring to the TPM2 chip (e.g. <filename>/dev/tpmrm0</filename>). volume. Expects a device node path referring to the TPM2 chip (e.g. <filename>/dev/tpmrm0</filename>).
Alternatively the special value <literal>auto</literal> may be specified, in order to automatically Alternatively the special value <literal>auto</literal> may be specified, in order to automatically
determine the device node of a currently discovered TPM2 device (of which there must be exactly one). determine the device node of a currently discovered TPM2 device (of which there must be exactly one).

View File

@ -32,7 +32,7 @@
<arg choice="plain">VOLUME</arg> <arg choice="plain">VOLUME</arg>
<arg choice="plain">SOURCE-DEVICE</arg> <arg choice="plain">SOURCE-DEVICE</arg>
<arg choice="opt">KEY-FILE</arg> <arg choice="opt">KEY-FILE</arg>
<arg choice="opt">CONFIG</arg> <arg choice="opt">CRYPTTAB-OPTIONS</arg>
</cmdsynopsis> </cmdsynopsis>
<cmdsynopsis> <cmdsynopsis>
@ -150,7 +150,7 @@
<varlistentry> <varlistentry>
<term><varname>cryptsetup.luks2-pin</varname></term> <term><varname>cryptsetup.luks2-pin</varname></term>
<listitem><para>This credential specifies the PIN requested by generic LUKS2 token modules.</para> <listitem><para>This credential specifies the pin requested by generic LUKS2 token modules.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem> <xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry> </varlistentry>

View File

@ -57,7 +57,9 @@
last check, number of mounts, unclean unmount, etc.</para> last check, number of mounts, unclean unmount, etc.</para>
<para><filename>systemd-fsck-root.service</filename> and <filename>systemd-fsck-usr.service</filename> <para><filename>systemd-fsck-root.service</filename> and <filename>systemd-fsck-usr.service</filename>
will activate <filename>reboot.target</filename> if <command>fsck</command> returns the "System will activate <filename>reboot.target</filename> if
<citerefentry project='man-pages'><refentrytitle>fsck</refentrytitle><manvolnum>8</manvolnum></citerefentry>
returns the "System
should reboot" condition, or <filename>emergency.target</filename> if <command>fsck</command> should reboot" condition, or <filename>emergency.target</filename> if <command>fsck</command>
returns the "Filesystem errors left uncorrected" condition.</para> returns the "Filesystem errors left uncorrected" condition.</para>

View File

@ -164,9 +164,10 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
used to view the log stream of a specific namespace. If the switch is not used the log stream of the used to view the log stream of a specific namespace. If the switch is not used the log stream of the
default namespace is shown, i.e. log data from other namespaces is not visible.</para> default namespace is shown, i.e. log data from other namespaces is not visible.</para>
<para>Services associated with a specific log namespace may log via syslog, the native logging protocol <para>Services associated with a specific log namespace may log via
of the journal and via stdout/stderr; the logging from all three transports is associated with the <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
namespace.</para> the native logging protocol of the journal and via stdout/stderr; the logging from all three transports
is associated with the namespace.</para>
<para>By default only the default namespace will collect kernel and audit log messages.</para> <para>By default only the default namespace will collect kernel and audit log messages.</para>
@ -288,8 +289,11 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
<term><varname>systemd.journald.max_level_socket=</varname></term> <term><varname>systemd.journald.max_level_socket=</varname></term>
<listitem><para>Controls the maximum log level of messages that are stored in the journal, forwarded <listitem><para>Controls the maximum log level of messages that are stored in the journal, forwarded
to syslog, kmsg, the console, the wall, or a socket. This kernel command line options override the to
settings of the same names in the <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
kmsg, the console,
<citerefentry project='man-pages'><refentrytitle>wall</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
or a socket. This kernel command line options override the settings of the same names in the
<citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> <citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
file.</para> file.</para>

View File

@ -136,6 +136,7 @@
<member><citerefentry><refentrytitle>nss-mymachines</refentrytitle><manvolnum>8</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>nss-mymachines</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>org.freedesktop.machine1</refentrytitle><manvolnum>5</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>org.freedesktop.machine1</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry project='man-pages'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
</simplelist></para> </simplelist></para>
</refsect1> </refsect1>

View File

@ -57,7 +57,9 @@
<para>The returned mounts are automatically allowlisted in the per-user-namespace allowlist maintained by <para>The returned mounts are automatically allowlisted in the per-user-namespace allowlist maintained by
<citerefentry><refentrytitle>systemd-nsresourced.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para> <citerefentry><refentrytitle>systemd-nsresourced.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para>The file systems are automatically fsck'ed before mounting.</para> <para>The file systems are automatically
<citerefentry project='man-pages'><refentrytitle>fsck</refentrytitle><manvolnum>8</manvolnum></citerefentry>'ed
before mounting.</para>
</refsect1> </refsect1>
<refsect1> <refsect1>

View File

@ -140,7 +140,7 @@
<para>When running in unprivileged mode, some needed functionality is provided via <para>When running in unprivileged mode, some needed functionality is provided via
<citerefentry><refentrytitle>systemd-mountfsd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> <citerefentry><refentrytitle>systemd-mountfsd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
and and
<citerefentry><refentrytitle>systemd-nsresourced.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></para> <citerefentry><refentrytitle>systemd-nsresourced.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
</refsect1> </refsect1>
<refsect1> <refsect1>

View File

@ -106,7 +106,7 @@
<listitem><para>This reads the combined TPM2 event log and writes it to STDOUT in <ulink <listitem><para>This reads the combined TPM2 event log and writes it to STDOUT in <ulink
url="https://trustedcomputinggroup.org/resource/canonical-event-log-format/">TCG Canonical Event Log url="https://trustedcomputinggroup.org/resource/canonical-event-log-format/">TCG Canonical Event Log
Format (CEL-JSON)</ulink> format.</para> Format (CEL-JSON)</ulink>.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem> <xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry> </varlistentry>
@ -387,8 +387,10 @@
<listitem><para>Generates/removes a <filename>.pcrlock</filename> file based on a kernel initrd cpio <listitem><para>Generates/removes a <filename>.pcrlock</filename> file based on a kernel initrd cpio
archive. This is useful for predicting measurements the Linux kernel makes to PCR 9 archive. This is useful for predicting measurements the Linux kernel makes to PCR 9
("kernel-initrd"). Do not use for <command>systemd-stub</command> UKIs, as the initrd is combined ("kernel-initrd"). Do not use for
dynamically from various sources and hence does not take a single input, like this command.</para> <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>
UKIs, as the initrd is combined dynamically from various sources and hence does not take a single
input, like this command.</para>
<para>This writes/removes the file <para>This writes/removes the file
<filename>/var/lib/pcrlock.d/720-kernel-initrd.pcrlock/generated.pcrlock</filename>.</para> <filename>/var/lib/pcrlock.d/720-kernel-initrd.pcrlock/generated.pcrlock</filename>.</para>
@ -521,7 +523,7 @@
<varlistentry> <varlistentry>
<term><option>--pcrlock=</option></term> <term><option>--pcrlock=</option></term>
<listitem><para>Takes a file system path as argument. If specified overrides where to write the <listitem><para>Takes a file system path as argument. If specified, configures where to write the
generated pcrlock data to. Honoured by the various <command>lock-*</command> commands. If not generated pcrlock data to. Honoured by the various <command>lock-*</command> commands. If not
specified, a default path is generally used, as documented above.</para> specified, a default path is generally used, as documented above.</para>
@ -531,7 +533,7 @@
<varlistentry> <varlistentry>
<term><option>--policy=</option></term> <term><option>--policy=</option></term>
<listitem><para>Takes a file system path as argument. If specified overrides where to write pcrlock <listitem><para>Takes a file system path as argument. If specified, configures where to write pcrlock
policy metadata to. If not specified defaults to policy metadata to. If not specified defaults to
<filename>/var/lib/systemd/pcrlock.json</filename>.</para> <filename>/var/lib/systemd/pcrlock.json</filename>.</para>

View File

@ -53,7 +53,7 @@
might be broken — the running PID 1 could still depend on libraries which are not available any more, might be broken — the running PID 1 could still depend on libraries which are not available any more,
thus keeping the file system busy, which then cannot be re-mounted read-only.</para> thus keeping the file system busy, which then cannot be re-mounted read-only.</para>
<para>Shortly before executing the actual system power-off/halt/reboot/kexec <para>Shortly before executing the actual system power-off/halt/reboot/kexec,
<filename>systemd-shutdown</filename> will run all executables in <filename>systemd-shutdown</filename> will run all executables in
<filename>/usr/lib/systemd/system-shutdown/</filename> and pass one arguments to them: either <filename>/usr/lib/systemd/system-shutdown/</filename> and pass one arguments to them: either
<literal>poweroff</literal>, <literal>halt</literal>, <literal>reboot</literal>, or <literal>poweroff</literal>, <literal>halt</literal>, <literal>reboot</literal>, or

View File

@ -569,7 +569,7 @@
(sysext, see (sysext, see
<citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for details), configuration extension (confext) or <ulink for details), configuration extension (confext) or <ulink
url="https://systemd.io/PORTABLE_SERVICES">portable service</ulink>. The generated image will consist url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink>. The generated image will consist
of a signed Verity <literal>erofs</literal> file system as root partition. In this mode of operation of a signed Verity <literal>erofs</literal> file system as root partition. In this mode of operation
the partition definitions in <filename>/usr/lib/repart.d/*.conf</filename> and related directories the partition definitions in <filename>/usr/lib/repart.d/*.conf</filename> and related directories
are not read, and <option>--definitions=</option> is not supported, as appropriate definitions for are not read, and <option>--definitions=</option> is not supported, as appropriate definitions for
@ -605,10 +605,11 @@
<varlistentry> <varlistentry>
<term><option>--generate-fstab=<replaceable>PATH</replaceable></option></term> <term><option>--generate-fstab=<replaceable>PATH</replaceable></option></term>
<listitem><para>Specifies a path where to write fstab entries for the mountpoints configured with <listitem><para>Specifies a path where to write
<option>MountPoint=</option> in the root directory specified with <option>--copy-source=</option> or <citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>
<option>--root=</option> or in the host's root directory if neither is specified. Disabled by entries for the mountpoints configured with <option>MountPoint=</option> in the root directory
default.</para> specified with <option>--copy-source=</option> or <option>--root=</option> or in the host's root
directory if neither is specified. Disabled by default.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem> <xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry> </varlistentry>
@ -680,7 +681,7 @@ systemd-confext refresh</programlisting>
<title>Generate a system extension image and sign it via PKCS11</title> <title>Generate a system extension image and sign it via PKCS11</title>
<para>The following creates a system extension DDI (sysext) for an <para>The following creates a system extension DDI (sysext) for an
<filename>/usr/foo</filename> update and signs it with a hardware token via PKCS11.</para> <filename>/usr/foo</filename> update and signs it with a hardware token via PKCS11:</para>
<programlisting>mkdir -p tree/usr/lib/extension-release.d <programlisting>mkdir -p tree/usr/lib/extension-release.d
echo "Hello World" >tree/usr/foo echo "Hello World" >tree/usr/foo

View File

@ -343,10 +343,10 @@ search foobar.com barbar.com
<listitem><para><command>systemd-resolved</command> maintains the <listitem><para><command>systemd-resolved</command> maintains the
<filename>/run/systemd/resolve/stub-resolv.conf</filename> file for compatibility with traditional <filename>/run/systemd/resolve/stub-resolv.conf</filename> file for compatibility with traditional
Linux programs. This file lists the 127.0.0.53 DNS stub (see above) as the only DNS server. It also Linux programs. This file lists the 127.0.0.53 DNS stub (see above) as the only DNS server. It also
contains a list of search domains that are in use by systemd-resolved. The list of search domains is contains a list of search domains that are in use by <command>systemd-resolved</command>. The list of
always kept up-to-date. Note that <filename>/run/systemd/resolve/stub-resolv.conf</filename> should not search domains is always kept up-to-date. Note that
be used directly by applications, but only through a symlink from <filename>/run/systemd/resolve/stub-resolv.conf</filename> should not be used directly by applications,
<filename>/etc/resolv.conf</filename>. This file may be symlinked from but only through a symlink from <filename>/etc/resolv.conf</filename>. This file may be symlinked from
<filename>/etc/resolv.conf</filename> in order to connect all local clients that bypass local DNS APIs <filename>/etc/resolv.conf</filename> in order to connect all local clients that bypass local DNS APIs
to <command>systemd-resolved</command> with correct search domains settings. This mode of operation is to <command>systemd-resolved</command> with correct search domains settings. This mode of operation is
recommended.</para></listitem> recommended.</para></listitem>

View File

@ -139,7 +139,8 @@ DefaultDependencies=no</programlisting>
<varname>Conflicts=umount.target</varname>)</para></listitem> <varname>Conflicts=umount.target</varname>)</para></listitem>
<listitem><para>If the unit publishes a service over D-Bus, the connection needs to be re-established <listitem><para>If the unit publishes a service over D-Bus, the connection needs to be re-established
after soft-reboot as the D-Bus broker will be stopped and then started again. When using the sd-bus after soft-reboot as the D-Bus broker will be stopped and then started again. When using the
<citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>
library this can be achieved by adapting the following example. library this can be achieved by adapting the following example.
<programlisting><xi:include href="sd_bus_service_reconnect.c" parse="text"/></programlisting> <programlisting><xi:include href="sd_bus_service_reconnect.c" parse="text"/></programlisting>
</para></listitem> </para></listitem>

View File

@ -34,9 +34,9 @@
<para><command>systemd-ssh-generator</command> binds a socket-activated SSH server to local <para><command>systemd-ssh-generator</command> binds a socket-activated SSH server to local
<constant>AF_VSOCK</constant> and <constant>AF_UNIX</constant> sockets under certain conditions. It only <constant>AF_VSOCK</constant> and <constant>AF_UNIX</constant> sockets under certain conditions. It only
has an effect if the <citerefentry has an effect if the
project="man-pages"><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry> binary is <citerefentry project="man-pages"><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
installed. Specifically, it does the following:</para> binary is installed. Specifically, it does the following:</para>
<itemizedlist> <itemizedlist>
<listitem><para>If invoked in a VM with <constant>AF_VSOCK</constant> support, a socket-activated SSH <listitem><para>If invoked in a VM with <constant>AF_VSOCK</constant> support, a socket-activated SSH
@ -71,14 +71,14 @@
<para>The generator will use a packaged <filename>sshd@.service</filename> service template file if one <para>The generator will use a packaged <filename>sshd@.service</filename> service template file if one
exists, and otherwise generate a suitable service template file.</para> exists, and otherwise generate a suitable service template file.</para>
<para><filename>systemd-ssh-generator</filename> implements <para><command>systemd-ssh-generator</command> implements
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para> <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
</refsect1> </refsect1>
<refsect1> <refsect1>
<title>Kernel Command Line</title> <title>Kernel Command Line</title>
<para><filename>systemd-ssh-generator</filename> understands the following <para><command>systemd-ssh-generator</command> understands the following
<citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry> <citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry>
parameters:</para> parameters:</para>
@ -102,8 +102,9 @@
times to bind multiple sockets. The syntax should follow the one of <varname>ListenStream=</varname>, times to bind multiple sockets. The syntax should follow the one of <varname>ListenStream=</varname>,
see see
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details. This functionality supports all socket families systemd supports, including for details. This functionality supports all socket families
<constant>AF_INET</constant> and <constant>AF_INET6</constant>.</para> <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> supports,
including <constant>AF_INET</constant> and <constant>AF_INET6</constant>.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem> <xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry> </varlistentry>

View File

@ -77,7 +77,7 @@ Host .host
<para>This tool is supposed to be used together with <para>This tool is supposed to be used together with
<citerefentry><refentrytitle>systemd-ssh-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry> <citerefentry><refentrytitle>systemd-ssh-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
which when run inside a VM or container will bind SSH to suitable which when run inside a VM or container will bind SSH to suitable
addresses. <command>systemd-ssh-generator</command> is supposed to run in the container of VM guest, and addresses. <command>systemd-ssh-generator</command> is supposed to run in the container or VM guest, and
<command>systemd-ssh-proxy</command> is run on the host, in order to connect to the container or VM <command>systemd-ssh-proxy</command> is run on the host, in order to connect to the container or VM
guest.</para> guest.</para>
</refsect1> </refsect1>

View File

@ -43,7 +43,7 @@
<para><citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry> uses <para><citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry> uses
<command>systemd-stdio-bridge</command> to forward D-Bus connections over <command>systemd-stdio-bridge</command> to forward D-Bus connections over
<citerefentry project='die-net'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
or to connect to the bus of a different user, see or to connect to the bus of a different user, see
<citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>. <citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
</para> </para>

View File

@ -209,7 +209,7 @@
images to the initrd. See images to the initrd. See
<citerefentry><refentrytitle>systemd-confext</refentrytitle><manvolnum>8</manvolnum></citerefentry> for <citerefentry><refentrytitle>systemd-confext</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
details on configuration extension images. The generated <command>cpio</command> archive containing details on configuration extension images. The generated <command>cpio</command> archive containing
these system extension images is measured into TPM PCR 12 (if a TPM is present).</para></listitem> these configuration extension images is measured into TPM PCR 12 (if a TPM is present).</para></listitem>
<listitem><para>Similarly, files <listitem><para>Similarly, files
<filename><replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename> are loaded and verified as <filename><replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename> are loaded and verified as

View File

@ -141,7 +141,7 @@
but the used architecture identifiers are the same as for <varname>ConditionArchitecture=</varname> but the used architecture identifiers are the same as for <varname>ConditionArchitecture=</varname>
described in <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. described in <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
<varname>EXTENSION_RELOAD_MANAGER=</varname> can be set to 1 if the extension requires a service manager reload after application <varname>EXTENSION_RELOAD_MANAGER=</varname> can be set to 1 if the extension requires a service manager reload after application
of the extension. Note that for the reasons mentioned earlier: of the extension. Note that for the reasons mentioned earlier,
<ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink> remain <ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink> remain
the recommended way to ship system services. the recommended way to ship system services.
@ -206,13 +206,13 @@
the underlying host <filename>/usr/</filename> is managed as immutable disk image or is a traditional the underlying host <filename>/usr/</filename> is managed as immutable disk image or is a traditional
package manager controlled (i.e. writable) tree.</para> package manager controlled (i.e. writable) tree.</para>
<para>With systemd-confext one can perform runtime reconfiguration of OS services. <para>With <command>systemd-confext</command> one can perform runtime reconfiguration of OS services.
Sometimes, there is a need to swap certain configuration parameter values or restart only a specific Sometimes, there is a need to swap certain configuration parameter values or restart only a specific
service without deployment of new code or a complete OS deployment. In other words, we want to be able service without deployment of new code or a complete OS deployment. In other words, we want to be able
to tie the most frequently configured options to runtime updateable flags that can be changed without a to tie the most frequently configured options to runtime updateable flags that can be changed without a
system reboot. This will help reduce servicing times when there is a need for changing the OS configuration. system reboot. This will help reduce servicing times when there is a need for changing the OS configuration.
It also provides a reliable tool for managing configuration because all old configuration files disappear when It also provides a reliable tool for managing configuration because all old configuration files disappear when
the systemd-confext image is removed.</para></refsect1> the <command>systemd-confext</command> image is removed.</para></refsect1>
<refsect1> <refsect1>
<title>Mutability</title> <title>Mutability</title>

View File

@ -30,7 +30,7 @@
<refsect1> <refsect1>
<title>Description</title> <title>Description</title>
<para><filename>systemd-tpm2-generator</filename> is a generator that adds a <varname>Wants=</varname> <para><command>systemd-tpm2-generator</command> is a generator that adds a <varname>Wants=</varname>
dependency from <filename>sysinit.target</filename> to <filename>tpm2.target</filename> when it detects dependency from <filename>sysinit.target</filename> to <filename>tpm2.target</filename> when it detects
that the firmware discovered a TPM2 device but the OS kernel so far did that the firmware discovered a TPM2 device but the OS kernel so far did
not. <filename>tpm2.target</filename> is supposed to act as synchronization point for all services that not. <filename>tpm2.target</filename> is supposed to act as synchronization point for all services that
@ -45,7 +45,7 @@
for it yet. The latter might be useful in environments where a suitable TPM2 driver for the available for it yet. The latter might be useful in environments where a suitable TPM2 driver for the available
hardware is not available.</para> hardware is not available.</para>
<para><filename>systemd-tpm2-generator</filename> implements <para><command>systemd-tpm2-generator</command> implements
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para> <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
</refsect1> </refsect1>

View File

@ -45,7 +45,7 @@
file descriptors must be passed with the names <literal>kvm</literal> and <literal>vhost-vsock</literal> file descriptors must be passed with the names <literal>kvm</literal> and <literal>vhost-vsock</literal>
respectively.</para> respectively.</para>
<para>Note: on Ubuntu/Debian derivatives systemd-vmspawn requires the user to be in the <para>Note: on Ubuntu/Debian derivatives <command>systemd-vmspawn</command> requires the user to be in the
<literal>kvm</literal> group to use the VSOCK options.</para> <literal>kvm</literal> group to use the VSOCK options.</para>
</refsect1> </refsect1>
@ -420,7 +420,8 @@
for more information.</para> for more information.</para>
<para>By default <literal>ed25519</literal> keys are generated, however <literal>rsa</literal> keys <para>By default <literal>ed25519</literal> keys are generated, however <literal>rsa</literal> keys
may also be useful if the VM has a particularly old version of <command>sshd</command>.</para> may also be useful if the VM has a particularly old version of
<citerefentry project='man-pages'><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<xi:include href="version-info.xml" xpointer="v256"/> <xi:include href="version-info.xml" xpointer="v256"/>
</listitem> </listitem>

View File

@ -46,7 +46,7 @@
<para>If the specified path does not reference a <literal>.v/</literal> path (i.e. neither the final <para>If the specified path does not reference a <literal>.v/</literal> path (i.e. neither the final
component ends in <literal>.v</literal>, nor the penultimate does or the final one does contain a triple component ends in <literal>.v</literal>, nor the penultimate does or the final one does contain a triple
underscore) it specified path is written unmodified to standard output.</para> underscore) its specified path is written unmodified to standard output.</para>
</refsect1> </refsect1>
<refsect1> <refsect1>

View File

@ -378,7 +378,7 @@
<para>This setting is useful to configure the <literal>ID_NET_MANAGED_BY=</literal> property which <para>This setting is useful to configure the <literal>ID_NET_MANAGED_BY=</literal> property which
declares which network management service shall manage the interface, which is respected by declares which network management service shall manage the interface, which is respected by
systemd-networkd and others. Use <command>systemd-networkd</command> and others. Use
<programlisting>Property=ID_NET_MANAGED_BY=io.systemd.Network</programlisting> <programlisting>Property=ID_NET_MANAGED_BY=io.systemd.Network</programlisting>
to declare explicitly that <command>systemd-networkd</command> shall manage the interface, or set to declare explicitly that <command>systemd-networkd</command> shall manage the interface, or set
the property to something else to declare explicitly it shall not do so. See the property to something else to declare explicitly it shall not do so. See
@ -974,10 +974,10 @@
<listitem> <listitem>
<para>Configures Receive Packet Steering (RPS) list of CPUs to which RPS may forward traffic. <para>Configures Receive Packet Steering (RPS) list of CPUs to which RPS may forward traffic.
Takes a list of CPU indices or ranges separated by either whitespace or commas. Alternatively, Takes a list of CPU indices or ranges separated by either whitespace or commas. Alternatively,
takes the special value <literal>all</literal> in which will include all available CPUs in the mask. takes the special value <literal>all</literal>, which will include all available CPUs in the mask.
CPU ranges are specified by the lower and upper CPU indices separated by a dash (e.g. <literal>2-6</literal>). CPU ranges are specified by the lower and upper CPU indices separated by a dash (e.g. <literal>2-6</literal>).
This option may be specified more than once, in which case the specified CPU affinity masks are merged. This option may be specified more than once, in which case the specified list of CPU ranges are merged.
If an empty string is assigned, the mask is reset, all assignments prior to this will have no effect. If an empty string is assigned, the list is reset, all assignments prior to this will have no effect.
Defaults to unset and RPS CPU list is unchanged. To disable RPS when it was previously enabled, use the Defaults to unset and RPS CPU list is unchanged. To disable RPS when it was previously enabled, use the
special value <literal>disable</literal>.</para> special value <literal>disable</literal>.</para>

View File

@ -293,7 +293,7 @@
comes from unit fragments, i.e. generated from <filename>/etc/fstab</filename> by <citerefentry> comes from unit fragments, i.e. generated from <filename>/etc/fstab</filename> by <citerefentry>
<refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry> or loaded from <refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry> or loaded from
a manually configured mount unit, a combination of <varname>Requires=</varname> and <varname>StopPropagatedFrom=</varname> a manually configured mount unit, a combination of <varname>Requires=</varname> and <varname>StopPropagatedFrom=</varname>
dependencies is set on the backing device. If doesn't, only <varname>Requires=</varname> is used.</para> dependencies is set on the backing device, otherwise only <varname>Requires=</varname> is used.</para>
<xi:include href="version-info.xml" xpointer="v233"/></listitem> <xi:include href="version-info.xml" xpointer="v233"/></listitem>
</varlistentry> </varlistentry>
@ -556,7 +556,7 @@
for details. This setting is optional.</para> for details. This setting is optional.</para>
<para>If the type is <literal>overlay</literal>, and <literal>upperdir=</literal> or <para>If the type is <literal>overlay</literal>, and <literal>upperdir=</literal> or
<literal>workdir=</literal> are specified as options and they don't exist, they will be created. <literal>workdir=</literal> are specified as options and the directories don't exist, they will be created.
</para></listitem> </para></listitem>
</varlistentry> </varlistentry>

View File

@ -27,18 +27,19 @@
attributes and the use of this information is configured. This page describes interface naming, i.e. what attributes and the use of this information is configured. This page describes interface naming, i.e. what
possible names may be generated. Those names are generated by the possible names may be generated. Those names are generated by the
<citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
builtin <command>net_id</command> and exported as udev properties builtin <command>net_id</command> and exported as
(<varname>ID_NET_NAME_ONBOARD=</varname>, <varname>ID_NET_LABEL_ONBOARD=</varname>, <citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry>
properties (<varname>ID_NET_NAME_ONBOARD=</varname>, <varname>ID_NET_LABEL_ONBOARD=</varname>,
<varname>ID_NET_NAME_PATH=</varname>, <varname>ID_NET_NAME_SLOT=</varname>).</para> <varname>ID_NET_NAME_PATH=</varname>, <varname>ID_NET_NAME_SLOT=</varname>).</para>
<para>Names and MAC addresses are derived from various stable device metadata attributes. Newer versions <para>Names and MAC addresses are derived from various stable device metadata attributes. Newer versions
of udev take more of these attributes into account, improving (and thus possibly changing) the names and of <command>systemd-udevd</command> take more of these attributes into account, improving (and thus
addresses used for the same devices. Different versions of those generation rules are called "naming possibly changing) the names and addresses used for the same devices. Different versions of those
schemes". The default naming scheme is chosen at compilation time. Usually this will be the latest generation rules are called "naming schemes". The default naming scheme is chosen at compilation time.
implemented version, but it is also possible to set one of the older versions to preserve Usually this will be the latest implemented version, but it is also possible to set one of the older
compatibility. This may be useful for example for distributions, which may introduce new versions of versions to preserve compatibility. This may be useful for example for distributions, which may introduce
systemd in stable releases without changing the naming scheme. The naming scheme may also be overridden new versions of systemd in stable releases without changing the naming scheme. The naming scheme may also
using the <varname>net.naming_scheme=</varname> kernel command line switch, see be overridden using the <varname>net.naming_scheme=</varname> kernel command line switch, see
<citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
Available naming schemes are described below.</para> Available naming schemes are described below.</para>
@ -521,7 +522,8 @@
change introduced in <constant>v254</constant> by default.</para> change introduced in <constant>v254</constant> by default.</para>
<para>If we detect that a PCI device associated with a slot is a PCI bridge, we no longer set <para>If we detect that a PCI device associated with a slot is a PCI bridge, we no longer set
<varname>ID_NET_NAME_SLOT</varname>, reverting a change that was introduced in v251.</para> <varname>ID_NET_NAME_SLOT</varname>, reverting a change that was introduced in
<constant>v251</constant>.</para>
<xi:include href="version-info.xml" xpointer="v255"/> <xi:include href="version-info.xml" xpointer="v255"/>
</listitem> </listitem>
@ -708,6 +710,7 @@ net:naming:drvirtio_net:*
<para><simplelist type="inline"> <para><simplelist type="inline">
<member><citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>udev</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>udevadm</refentrytitle><manvolnum>8</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>udevadm</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><ulink url="https://systemd.io/PREDICTABLE_INTERFACE_NAMES">Predictable Network Interface Names</ulink></member> <member><ulink url="https://systemd.io/PREDICTABLE_INTERFACE_NAMES">Predictable Network Interface Names</ulink></member>
<member><citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
</simplelist></para> </simplelist></para>

View File

@ -34,10 +34,16 @@
for a general description of the syntax.</para> for a general description of the syntax.</para>
<para>The main Virtual Network Device file must have the extension <filename>.netdev</filename>; <para>The main Virtual Network Device file must have the extension <filename>.netdev</filename>;
other extensions are ignored. Virtual network devices are created as soon as networkd is other extensions are ignored. Virtual network devices are created as soon as
started. If a netdev with the specified name already exists, networkd will use that as-is rather <command>systemd-networkd</command> is started if possible. If a netdev with the specified name already
than create its own. Note that the settings of the pre-existing netdev will not be changed by exists, <command>systemd-networkd</command> will try to update the config if the kind of the existing
networkd.</para> netdev is equivalent to the requested one, otherwise (e.g. when bridge device <filename>foo</filename>
exists but bonding device with the same name is configured in a .netdev file) use the existing netdev
as-is rather than replacing with the requested netdev. Note, several settings (e.g. vlan ID) cannot be
changed after the netdev is created. To change such settings, it is necessary to first remove the
existing netdev, and then run <command>networkctl reload</command> command or restart
<command>systemd-networkd</command>. See also
<citerefentry><refentrytitle>networkctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
<para>The <filename>.netdev</filename> files are read from the files located in the system network <para>The <filename>.netdev</filename> files are read from the files located in the system network
directory <filename>/usr/lib/systemd/network</filename> and directory <filename>/usr/lib/systemd/network</filename> and
@ -588,7 +594,7 @@
<para>Controls the threshold for broadcast queueing of the macvlan device. Takes the special value <para>Controls the threshold for broadcast queueing of the macvlan device. Takes the special value
<literal>no</literal>, or an integer in the range 0…2147483647. When <literal>no</literal> is <literal>no</literal>, or an integer in the range 0…2147483647. When <literal>no</literal> is
specified, the broadcast queueing is disabled altogether. When an integer is specified, a multicast specified, the broadcast queueing is disabled altogether. When an integer is specified, a multicast
address will be queued as broadcast if the number of devices using it is greater than the given address will be queued as broadcast if the number of devices using the macvlan is greater than the given
value. Defaults to unset, and the kernel default will be used.</para> value. Defaults to unset, and the kernel default will be used.</para>
<xi:include href="version-info.xml" xpointer="v256"/> <xi:include href="version-info.xml" xpointer="v256"/>
@ -1929,7 +1935,8 @@
the <command>wg genkey</command> command the <command>wg genkey</command> command
(see <citerefentry project='man-pages'><refentrytitle>wg</refentrytitle><manvolnum>8</manvolnum></citerefentry>). (see <citerefentry project='man-pages'><refentrytitle>wg</refentrytitle><manvolnum>8</manvolnum></citerefentry>).
Specially, if the specified key is prefixed with <literal>@</literal>, it is interpreted as Specially, if the specified key is prefixed with <literal>@</literal>, it is interpreted as
the name of the credential from which the actual key shall be read. <command>systemd-networkd.service</command> the name of the credential from which the actual key shall be read.
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
automatically imports credentials matching <literal>network.wireguard.*</literal>. For more details automatically imports credentials matching <literal>network.wireguard.*</literal>. For more details
on credentials, refer to on credentials, refer to
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>. <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
@ -2083,7 +2090,7 @@
i.e. the packets that pass through the tunnel itself. To cause packets to be sent via the tunnel in i.e. the packets that pass through the tunnel itself. To cause packets to be sent via the tunnel in
the first place, an appropriate route needs to be added as well — either in the the first place, an appropriate route needs to be added as well — either in the
<literal>[Routes]</literal> section on the <literal>.network</literal> matching the wireguard <literal>[Routes]</literal> section on the <literal>.network</literal> matching the wireguard
interface, or externally to <filename>systemd-networkd</filename>.</para> interface, or externally to <command>systemd-networkd</command>.</para>
<xi:include href="version-info.xml" xpointer="v237"/> <xi:include href="version-info.xml" xpointer="v237"/>
</listitem> </listitem>
@ -2970,7 +2977,7 @@ Independent=yes</programlisting>
<title>See Also</title> <title>See Also</title>
<para><simplelist type="inline"> <para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-networkd</refentrytitle><manvolnum>8</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-network-generator.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member> <member><citerefentry><refentrytitle>systemd-network-generator.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>

View File

@ -887,7 +887,7 @@ DuplicateAddressDetection=none</programlisting></para>
from the network interface will be appear as coming from the local host. Typically, this should be from the network interface will be appear as coming from the local host. Typically, this should be
enabled on the downstream interface of routers. Takes one of <literal>ipv4</literal>, enabled on the downstream interface of routers. Takes one of <literal>ipv4</literal>,
<literal>ipv6</literal>, <literal>both</literal>, or <literal>no</literal>. Defaults to <literal>ipv6</literal>, <literal>both</literal>, or <literal>no</literal>. Defaults to
<literal>no</literal>. Note. Any positive boolean values such as <literal>yes</literal> or <literal>no</literal>. Note that any positive boolean values such as <literal>yes</literal> or
<literal>true</literal> are now deprecated. Please use one of the values above. Specifying <literal>true</literal> are now deprecated. Please use one of the values above. Specifying
<literal>ipv4</literal> or <literal>both</literal> implies <varname>IPv4Forwarding=</varname> <literal>ipv4</literal> or <literal>both</literal> implies <varname>IPv4Forwarding=</varname>
settings in both .network file for this interface and the global settings in both .network file for this interface and the global
@ -928,8 +928,8 @@ DuplicateAddressDetection=none</programlisting></para>
<para>Takes a boolean. Controls IPv6 Router Advertisement (RA) reception support for the interface. <para>Takes a boolean. Controls IPv6 Router Advertisement (RA) reception support for the interface.
If true, RAs are accepted; if false, RAs are ignored. When RAs are accepted, they may trigger the If true, RAs are accepted; if false, RAs are ignored. When RAs are accepted, they may trigger the
start of the DHCPv6 client if the relevant flags are set in the RA data, or if no routers are found start of the DHCPv6 client if the relevant flags are set in the RA data, or if no routers are found
on the link. Defaults to false for bridge devices, when IP forwarding is enabled, on the link. Defaults to false for bridge devices, when <varname>IPv6Forwarding=</varname>,
<varname>IPv6SendRA=</varname> or <varname>KeepMaster=</varname> is enabled. Otherwise, enabled by <varname>IPv6SendRA=</varname>, or <varname>KeepMaster=</varname> is enabled. Otherwise, enabled by
default. Cannot be enabled on devices aggregated in a bond device or when link-local addressing is default. Cannot be enabled on devices aggregated in a bond device or when link-local addressing is
disabled.</para> disabled.</para>
@ -993,9 +993,9 @@ DuplicateAddressDetection=none</programlisting></para>
whether the <emphasis>source</emphasis> of the packet would be routed through the interface it came in. If there is no whether the <emphasis>source</emphasis> of the packet would be routed through the interface it came in. If there is no
route to the source on that interface, the machine will drop the packet. Takes one of route to the source on that interface, the machine will drop the packet. Takes one of
<literal>no</literal>, <literal>strict</literal>, or <literal>loose</literal>. When <literal>no</literal>, <literal>no</literal>, <literal>strict</literal>, or <literal>loose</literal>. When <literal>no</literal>,
no source validation will be done. When <literal>strict</literal>, mode each incoming packet is tested against the FIB and no source validation will be done. When <literal>strict</literal>, each incoming packet is tested against the FIB and
if the incoming interface is not the best reverse path, the packet check will fail. By default failed packets are discarded. if the incoming interface is not the best reverse path, the packet check will fail. By default failed packets are discarded.
When <literal>loose</literal>, mode each incoming packet's source address is tested against the FIB. The packet is dropped When <literal>loose</literal>, each incoming packet's source address is tested against the FIB. The packet is dropped
only if the source address is not reachable via any interface on that router. only if the source address is not reachable via any interface on that router.
See <ulink url="https://tools.ietf.org/html/rfc1027">RFC 3704</ulink>. See <ulink url="https://tools.ietf.org/html/rfc1027">RFC 3704</ulink>.
When unset, the kernel's default will be used.</para> When unset, the kernel's default will be used.</para>
@ -1084,9 +1084,10 @@ DuplicateAddressDetection=none</programlisting></para>
Advertisement messages intended for another machine by offering its own MAC address as Advertisement messages intended for another machine by offering its own MAC address as
destination. Unlike proxy ARP for IPv4, it is not enabled globally, but will only send destination. Unlike proxy ARP for IPv4, it is not enabled globally, but will only send
Neighbour Advertisement messages for addresses in the IPv6 neighbor proxy table, which can Neighbour Advertisement messages for addresses in the IPv6 neighbor proxy table, which can
also be shown by <command>ip -6 neighbour show proxy</command>. systemd-networkd will control also be shown by <command>ip -6 neighbour show proxy</command>.
the per-interface `proxy_ndp` switch for each configured interface depending on this option. <command>systemd-networkd</command> will control the per-interface `proxy_ndp` switch for each
When unset, the kernel's default will be used.</para> configured interface depending on this option. When unset, the kernel's default will be used.
</para>
<xi:include href="version-info.xml" xpointer="v234"/> <xi:include href="version-info.xml" xpointer="v234"/>
</listitem> </listitem>
@ -1096,7 +1097,7 @@ DuplicateAddressDetection=none</programlisting></para>
<term><varname>IPv6ProxyNDPAddress=</varname></term> <term><varname>IPv6ProxyNDPAddress=</varname></term>
<listitem> <listitem>
<para>An IPv6 address, for which Neighbour Advertisement messages will be proxied. This <para>An IPv6 address, for which Neighbour Advertisement messages will be proxied. This
option may be specified more than once. systemd-networkd will add the option may be specified more than once. <command>systemd-networkd</command> will add the
<varname>IPv6ProxyNDPAddress=</varname> entries to the kernel's IPv6 neighbor proxy table. <varname>IPv6ProxyNDPAddress=</varname> entries to the kernel's IPv6 neighbor proxy table.
This setting implies <varname>IPv6ProxyNDP=yes</varname> but has no effect if This setting implies <varname>IPv6ProxyNDP=yes</varname> but has no effect if
<varname>IPv6ProxyNDP=</varname> has been set to false. When unset, the kernel's default will <varname>IPv6ProxyNDP=</varname> has been set to false. When unset, the kernel's default will
@ -1225,9 +1226,9 @@ DuplicateAddressDetection=none</programlisting></para>
<varlistentry> <varlistentry>
<term><varname>ConfigureWithoutCarrier=</varname></term> <term><varname>ConfigureWithoutCarrier=</varname></term>
<listitem> <listitem>
<para>Takes a boolean. Allows networkd to configure a specific link even if it has no <para>Takes a boolean. Allows <command>systemd-networkd</command> to configure a specific link even
carrier. Defaults to false. If enabled, and the <varname>IgnoreCarrierLoss=</varname> setting if it has no carrier. Defaults to false. If enabled, and the <varname>IgnoreCarrierLoss=</varname>
is not explicitly set, then it is enabled as well.</para> setting is not explicitly set, then it is enabled as well.</para>
<para>With this enabled, to make the interface enter the <literal>configured</literal> state, <para>With this enabled, to make the interface enter the <literal>configured</literal> state,
which is required to make <command>systemd-networkd-wait-online</command> work properly for the which is required to make <command>systemd-networkd-wait-online</command> work properly for the
@ -1455,11 +1456,11 @@ DuplicateAddressDetection=none</programlisting></para>
<command>ip maddr</command> command would not work if we have an Ethernet switch that does <command>ip maddr</command> command would not work if we have an Ethernet switch that does
IGMP snooping since the switch would not replicate multicast packets on ports that did not IGMP snooping since the switch would not replicate multicast packets on ports that did not
have IGMP reports for the multicast addresses. Linux vxlan interfaces created via have IGMP reports for the multicast addresses. Linux vxlan interfaces created via
<command>ip link add vxlan</command> or networkd's netdev kind vxlan have the group option <command>ip link add vxlan</command> or <command>systemd-networkd</command>'s netdev kind vxlan
that enables them to do the required join. By extending <command>ip address</command> command have the group option that enables them to do the required join. By extending
with option <literal>autojoin</literal> we can get similar functionality for openvswitch (OVS) <command>ip address</command> command with option <literal>autojoin</literal> we can get similar
vxlan interfaces as well as other tunneling mechanisms that need to receive multicast traffic. functionality for openvswitch (OVS) vxlan interfaces as well as other tunneling mechanisms that
Defaults to <literal>no</literal>.</para> need to receive multicast traffic. Defaults to <literal>no</literal>.</para>
<xi:include href="version-info.xml" xpointer="v232"/> <xi:include href="version-info.xml" xpointer="v232"/>
</listitem> </listitem>
@ -1785,7 +1786,7 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
<varlistentry> <varlistentry>
<term><varname>L3MasterDevice=</varname></term> <term><varname>L3MasterDevice=</varname></term>
<listitem> <listitem>
<para>A boolean. Specifies whether the rule is to direct lookups to the tables associated with <para>Takes a boolean. Specifies whether the rule is to direct lookups to the tables associated with
level 3 master devices (also known as Virtual Routing and Forwarding or VRF devices). level 3 master devices (also known as Virtual Routing and Forwarding or VRF devices).
For further details see <ulink url="https://docs.kernel.org/networking/vrf.html"> For further details see <ulink url="https://docs.kernel.org/networking/vrf.html">
Virtual Routing and Forwarding (VRF)</ulink>. Defaults to false.</para> Virtual Routing and Forwarding (VRF)</ulink>. Defaults to false.</para>
@ -2903,7 +2904,7 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
Note that if <varname>AllowList=</varname> is configured then <varname>DenyList=</varname> is Note that if <varname>AllowList=</varname> is configured then <varname>DenyList=</varname> is
ignored.</para> ignored.</para>
<para>Note that this filters only DHCP offers, so the filtering might not work when <para>Note that this filters only DHCP offers, so the filtering might not work when
<varname>RapidCommit=</varname> is enabled. See also <varname>RapidCommit=</varname> in the above. <varname>RapidCommit=</varname> is enabled. See also <varname>RapidCommit=</varname> above.
</para> </para>
<xi:include href="version-info.xml" xpointer="v246"/> <xi:include href="version-info.xml" xpointer="v246"/>
@ -3339,7 +3340,7 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
<term><varname>UseRedirect=</varname></term> <term><varname>UseRedirect=</varname></term>
<listitem> <listitem>
<para>When true (the default), Redirect message sent by the current first-hop router will be <para>When true (the default), Redirect message sent by the current first-hop router will be
accepted, and configures routes to redirected nodes will be configured.</para> accepted, and routes to redirected nodes will be configured.</para>
<xi:include href="version-info.xml" xpointer="v256"/> <xi:include href="version-info.xml" xpointer="v256"/>
</listitem> </listitem>
@ -4076,7 +4077,8 @@ ServerAddress=192.168.0.1/24</programlisting>
<para>Takes a boolean. When true, the DHCP server will load and save leases in the persistent <para>Takes a boolean. When true, the DHCP server will load and save leases in the persistent
storage. When false, the DHCP server will neither load nor save leases in the persistent storage. storage. When false, the DHCP server will neither load nor save leases in the persistent storage.
Hence, bound leases will be lost when the interface is reconfigured e.g. by Hence, bound leases will be lost when the interface is reconfigured e.g. by
<command>networkctl reconfigure</command>, or <filename>systemd-networkd.service</filename> <command>networkctl reconfigure</command>, or
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
is restarted. That may cause address conflict on the network. So, please take an extra care when is restarted. That may cause address conflict on the network. So, please take an extra care when
disable this setting. When unspecified, the value specified in the same setting in disable this setting. When unspecified, the value specified in the same setting in
<citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>networkd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
@ -4260,7 +4262,7 @@ ServerAddress=192.168.0.1/24</programlisting>
<varlistentry> <varlistentry>
<term><varname>HomeAgent=</varname></term> <term><varname>HomeAgent=</varname></term>
<listitem><para>Takes a boolean. Specifies that IPv6 router advertisements which indicate to hosts that <listitem><para>Takes a boolean. Specifies that IPv6 router advertisements indicate to hosts that
the router acts as a Home Agent and includes a Home Agent option. Defaults to false. See the router acts as a Home Agent and includes a Home Agent option. Defaults to false. See
<ulink url="https://tools.ietf.org/html/rfc6275">RFC 6275</ulink> for further details.</para> <ulink url="https://tools.ietf.org/html/rfc6275">RFC 6275</ulink> for further details.</para>
@ -4584,10 +4586,9 @@ ServerAddress=192.168.0.1/24</programlisting>
<varlistentry> <varlistentry>
<term><varname>Priority=</varname></term> <term><varname>Priority=</varname></term>
<listitem> <listitem>
<para>Sets the "priority" of sending packets on this interface. <para>Sets the "priority" of sending packets on this interface. Each port in a bridge may have a
Each port in a bridge may have a different priority which is used different priority which is used to decide which link to use. Lower value means higher priority.
to decide which link to use. Lower value means higher priority. It is an integer value between 0 to 63. <command>systemd-networkd</command> does not set any
It is an integer value between 0 to 63. Networkd does not set any
default, meaning the kernel default value of 32 is used.</para> default, meaning the kernel default value of 32 is used.</para>
<xi:include href="version-info.xml" xpointer="v234"/> <xi:include href="version-info.xml" xpointer="v234"/>

View File

@ -896,7 +896,7 @@ CPUWeight=20 DisableControllers=cpu / \
<listitem> <listitem>
<para>Configures restrictions on the ability of unit processes to invoke <citerefentry <para>Configures restrictions on the ability of unit processes to invoke <citerefentry
project='man-pages'><refentrytitle>bind</refentrytitle><manvolnum>2</manvolnum></citerefentry> on a project='man-pages'><refentrytitle>bind</refentrytitle><manvolnum>2</manvolnum></citerefentry> on a
socket. Both allow and deny rules may defined that restrict which addresses a socket may be bound socket. Both allow and deny rules to be defined that restrict which addresses a socket may be bound
to.</para> to.</para>
<para><replaceable>bind-rule</replaceable> describes socket properties such as <replaceable>address-family</replaceable>, <para><replaceable>bind-rule</replaceable> describes socket properties such as <replaceable>address-family</replaceable>,
@ -1673,7 +1673,8 @@ DeviceAllow=/dev/loop-control
<para>When <command>systemd-coredump</command> is handling a coredump for a process from a container, <para>When <command>systemd-coredump</command> is handling a coredump for a process from a container,
if the container's leader process is a descendant of a cgroup with <varname>CoredumpReceive=yes</varname> if the container's leader process is a descendant of a cgroup with <varname>CoredumpReceive=yes</varname>
and <varname>Delegate=yes</varname>, then <command>systemd-coredump</command> will attempt to forward and <varname>Delegate=yes</varname>, then <command>systemd-coredump</command> will attempt to forward
the coredump to <command>systemd-coredump</command> within the container.</para> the coredump to <command>systemd-coredump</command> within the container. See also
<citerefentry><refentrytitle>systemd-coredump</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem> <xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry> </varlistentry>

View File

@ -1437,7 +1437,7 @@
<para>The command line accepts <literal>%</literal> specifiers as described in <para>The command line accepts <literal>%</literal> specifiers as described in
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
<para>An argument solely consisting of <literal>;</literal> must be escaped, i.e. specified as <literal>\;</literal></para> <para>An argument solely consisting of <literal>;</literal> must be escaped, i.e. specified as <literal>\;</literal>.</para>
<para>Basic environment variable substitution is supported. Use <para>Basic environment variable substitution is supported. Use
<literal>${FOO}</literal> as part of a word, or as a word of its <literal>${FOO}</literal> as part of a word, or as a word of its

View File

@ -120,9 +120,8 @@
<para>The timezone defaults to the current timezone if not specified explicitly. <para>The timezone defaults to the current timezone if not specified explicitly.
It may be given after a space, like above, in which case it can be: It may be given after a space, like above, in which case it can be:
<literal>UTC</literal>, <literal>UTC</literal>,
an entry in the installed IANA timezone database (<literal>CET</literal>, <literal>Asia/Tokyo</literal>, &amp;c.; an entry in the installed IANA timezone database (e.g. <literal>CET</literal>, <literal>Asia/Tokyo</literal>,
complete list obtainable with <literal>timedatectl where the complete list can be obtained with <command>timedatectl list-timezones</command> (see
list-timezones</literal> (see
<citerefentry><refentrytitle>timedatectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>)), <citerefentry><refentrytitle>timedatectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>)),
or <literal>±<replaceable>05</replaceable></literal>, or <literal>±<replaceable>05</replaceable></literal>,
<literal>±<replaceable>05</replaceable><replaceable>30</replaceable></literal>, <literal>±<replaceable>05</replaceable><replaceable>30</replaceable></literal>,

View File

@ -1238,9 +1238,9 @@
</itemizedlist> </itemizedlist>
<para>Signals sent to PID 1 before this message is sent might not be handled correctly yet. A consumer <para>Signals sent to PID 1 before this message is sent might not be handled correctly yet. A consumer
of these messages should parse the value as an unsigned integer indication the level of support. For of these messages should parse the value as an unsigned integer that indicates the level of support.
now only the mentioned level 2 is defined, but later on additional levels might be defined with higher For now only the mentioned level 2 is defined, but later on additional levels might be defined with
integers, that will implement a superset of the currently defined behaviour.</para> higher integers, that will implement a superset of the currently defined behaviour.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem> <xi:include href="version-info.xml" xpointer="v256"/></listitem>
@ -1389,8 +1389,8 @@
<term><option>--crash-action=</option></term> <term><option>--crash-action=</option></term>
<listitem><para>Specify what to do when the system manager (PID 1) crashes. This switch has no <listitem><para>Specify what to do when the system manager (PID 1) crashes. This switch has no
effect when systemd is running as user instance. See <varname>systemd.crash_action=</varname> effect when <command>systemd</command> is running as user instance. See
above.</para> <varname>systemd.crash_action=</varname> above.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem> <xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry> </varlistentry>

View File

@ -220,7 +220,8 @@
<para>For the <command>inspect</command> verb, the second syntax is used. <para>For the <command>inspect</command> verb, the second syntax is used.
The section <replaceable>NAME</replaceable> will be inspected (if found). The section <replaceable>NAME</replaceable> will be inspected (if found).
If the second argument is <literal>text</literal>, the contents will be printed. If the second argument is <literal>text</literal>, the contents will be printed.
If the third argument is given, the contents will be saved to file <replaceable>PATH</replaceable>. If the third argument is given, the contents will be saved to the file named
<replaceable>PATH</replaceable>.
</para> </para>
<para>Note that the name is used as-is, and if the section name should start with a dot, it must be <para>Note that the name is used as-is, and if the section name should start with a dot, it must be

View File

@ -52,7 +52,7 @@
<para>User processes may be started by the <filename>user@.service</filename> instance, in which <para>User processes may be started by the <filename>user@.service</filename> instance, in which
case they will be part of that unit in the system hierarchy. They may also be started elsewhere, case they will be part of that unit in the system hierarchy. They may also be started elsewhere,
for example by for example by
<citerefentry project='die-net'><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry> or a <citerefentry project='man-pages'><refentrytitle>sshd</refentrytitle><manvolnum>8</manvolnum></citerefentry> or a
display manager like <command>gdm</command>, in which case they form a .scope unit (see display manager like <command>gdm</command>, in which case they form a .scope unit (see
<citerefentry><refentrytitle>systemd.scope</refentrytitle><manvolnum>5</manvolnum></citerefentry>). <citerefentry><refentrytitle>systemd.scope</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
Both <filename>user@<replaceable>UID</replaceable>.service</filename> and the scope units are Both <filename>user@<replaceable>UID</replaceable>.service</filename> and the scope units are
@ -145,7 +145,7 @@ Control group /:
</programlisting> </programlisting>
<para>User with UID 1000 is logged in using <command>gdm</command> (<filename <para>User with UID 1000 is logged in using <command>gdm</command> (<filename
index="false">session-4.scope</filename>) and index="false">session-4.scope</filename>) and
<citerefentry project='die-net'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry> <citerefentry project='man-pages'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>
(<filename index="false">session-19.scope</filename>), and also has a user manager instance (<filename index="false">session-19.scope</filename>), and also has a user manager instance
running (<filename index="false">user@1000.service</filename>). User with UID 1001 is logged running (<filename index="false">user@1000.service</filename>). User with UID 1001 is logged
in using <command>ssh</command> (<filename index="false">session-20.scope</filename>) and in using <command>ssh</command> (<filename index="false">session-20.scope</filename>) and

View File

@ -416,7 +416,7 @@
<para>The <command>userdbctl</command> tool may be used to make the list of SSH authorized keys possibly <para>The <command>userdbctl</command> tool may be used to make the list of SSH authorized keys possibly
contained in a user record available to the SSH daemon for authentication. For that configure the contained in a user record available to the SSH daemon for authentication. For that configure the
following in <citerefentry following in <citerefentry
project='die-net'><refentrytitle>sshd_config</refentrytitle><manvolnum>5</manvolnum></citerefentry>:</para> project='man-pages'><refentrytitle>sshd_config</refentrytitle><manvolnum>5</manvolnum></citerefentry>:</para>
<programlisting> <programlisting>
AuthorizedKeysCommand /usr/bin/userdbctl ssh-authorized-keys %u AuthorizedKeysCommand /usr/bin/userdbctl ssh-authorized-keys %u

View File

@ -1418,6 +1418,26 @@ int cg_pid_get_session(pid_t pid, char **ret_session) {
return cg_path_get_session(cgroup, ret_session); return cg_path_get_session(cgroup, ret_session);
} }
int cg_pidref_get_session(const PidRef *pidref, char **ret) {
int r;
if (!pidref_is_set(pidref))
return -ESRCH;
_cleanup_free_ char *session = NULL;
r = cg_pid_get_session(pidref->pid, &session);
if (r < 0)
return r;
r = pidref_verify(pidref);
if (r < 0)
return r;
if (ret)
*ret = TAKE_PTR(session);
return 0;
}
int cg_path_get_owner_uid(const char *path, uid_t *ret_uid) { int cg_path_get_owner_uid(const char *path, uid_t *ret_uid) {
_cleanup_free_ char *slice = NULL; _cleanup_free_ char *slice = NULL;
char *start, *end; char *start, *end;
@ -1455,6 +1475,27 @@ int cg_pid_get_owner_uid(pid_t pid, uid_t *ret_uid) {
return cg_path_get_owner_uid(cgroup, ret_uid); return cg_path_get_owner_uid(cgroup, ret_uid);
} }
int cg_pidref_get_owner_uid(const PidRef *pidref, uid_t *ret) {
int r;
if (!pidref_is_set(pidref))
return -ESRCH;
uid_t uid;
r = cg_pid_get_owner_uid(pidref->pid, &uid);
if (r < 0)
return r;
r = pidref_verify(pidref);
if (r < 0)
return r;
if (ret)
*ret = uid;
return 0;
}
int cg_path_get_slice(const char *p, char **ret_slice) { int cg_path_get_slice(const char *p, char **ret_slice) {
const char *e = NULL; const char *e = NULL;

View File

@ -280,7 +280,9 @@ int cg_shift_path(const char *cgroup, const char *cached_root, const char **ret_
int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **ret_cgroup); int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **ret_cgroup);
int cg_pid_get_session(pid_t pid, char **ret_session); int cg_pid_get_session(pid_t pid, char **ret_session);
int cg_pidref_get_session(const PidRef *pidref, char **ret_session);
int cg_pid_get_owner_uid(pid_t pid, uid_t *ret_uid); int cg_pid_get_owner_uid(pid_t pid, uid_t *ret_uid);
int cg_pidref_get_owner_uid(const PidRef *pidref, uid_t *ret_uid);
int cg_pid_get_unit(pid_t pid, char **ret_unit); int cg_pid_get_unit(pid_t pid, char **ret_unit);
int cg_pidref_get_unit(const PidRef *pidref, char **ret); int cg_pidref_get_unit(const PidRef *pidref, char **ret);
int cg_pid_get_user_unit(pid_t pid, char **ret_unit); int cg_pid_get_user_unit(pid_t pid, char **ret_unit);

View File

@ -0,0 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
/* Root namespace inode numbers, as per include/linux/proc_ns.h in the kernel source tree, since v3.8:
* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=98f842e675f96ffac96e6c50315790912b2812be */
#define PROC_IPC_INIT_INO ((ino_t) UINT32_C(0xEFFFFFFF))
#define PROC_UTS_INIT_INO ((ino_t) UINT32_C(0xEFFFFFFE))
#define PROC_USER_INIT_INO ((ino_t) UINT32_C(0xEFFFFFFD))
#define PROC_PID_INIT_INO ((ino_t) UINT32_C(0xEFFFFFFC))
#define PROC_CGROUP_INIT_INO ((ino_t) UINT32_C(0xEFFFFFFB))
#define PROC_TIME_INIT_INO ((ino_t) UINT32_C(0xEFFFFFFA))

View File

@ -12,6 +12,7 @@
#include "fileio.h" #include "fileio.h"
#include "missing_fs.h" #include "missing_fs.h"
#include "missing_magic.h" #include "missing_magic.h"
#include "missing_namespace.h"
#include "missing_sched.h" #include "missing_sched.h"
#include "missing_syscall.h" #include "missing_syscall.h"
#include "mountpoint-util.h" #include "mountpoint-util.h"
@ -23,17 +24,17 @@
#include "user-util.h" #include "user-util.h"
const struct namespace_info namespace_info[_NAMESPACE_TYPE_MAX + 1] = { const struct namespace_info namespace_info[_NAMESPACE_TYPE_MAX + 1] = {
[NAMESPACE_CGROUP] = { "cgroup", "ns/cgroup", CLONE_NEWCGROUP, }, [NAMESPACE_CGROUP] = { "cgroup", "ns/cgroup", CLONE_NEWCGROUP, PROC_CGROUP_INIT_INO },
[NAMESPACE_IPC] = { "ipc", "ns/ipc", CLONE_NEWIPC, }, [NAMESPACE_IPC] = { "ipc", "ns/ipc", CLONE_NEWIPC, PROC_IPC_INIT_INO },
[NAMESPACE_NET] = { "net", "ns/net", CLONE_NEWNET, }, [NAMESPACE_NET] = { "net", "ns/net", CLONE_NEWNET, 0 },
/* So, the mount namespace flag is called CLONE_NEWNS for historical /* So, the mount namespace flag is called CLONE_NEWNS for historical
* reasons. Let's expose it here under a more explanatory name: "mnt". * reasons. Let's expose it here under a more explanatory name: "mnt".
* This is in-line with how the kernel exposes namespaces in /proc/$PID/ns. */ * This is in-line with how the kernel exposes namespaces in /proc/$PID/ns. */
[NAMESPACE_MOUNT] = { "mnt", "ns/mnt", CLONE_NEWNS, }, [NAMESPACE_MOUNT] = { "mnt", "ns/mnt", CLONE_NEWNS, 0 },
[NAMESPACE_PID] = { "pid", "ns/pid", CLONE_NEWPID, }, [NAMESPACE_PID] = { "pid", "ns/pid", CLONE_NEWPID, PROC_PID_INIT_INO },
[NAMESPACE_USER] = { "user", "ns/user", CLONE_NEWUSER, }, [NAMESPACE_USER] = { "user", "ns/user", CLONE_NEWUSER, PROC_USER_INIT_INO },
[NAMESPACE_UTS] = { "uts", "ns/uts", CLONE_NEWUTS, }, [NAMESPACE_UTS] = { "uts", "ns/uts", CLONE_NEWUTS, PROC_UTS_INIT_INO },
[NAMESPACE_TIME] = { "time", "ns/time", CLONE_NEWTIME, }, [NAMESPACE_TIME] = { "time", "ns/time", CLONE_NEWTIME, PROC_TIME_INIT_INO },
{ /* Allow callers to iterate over the array without using _NAMESPACE_TYPE_MAX. */ }, { /* Allow callers to iterate over the array without using _NAMESPACE_TYPE_MAX. */ },
}; };
@ -479,6 +480,28 @@ int namespace_open_by_type(NamespaceType type) {
return fd; return fd;
} }
int namespace_is_init(NamespaceType type) {
int r;
assert(type >= 0);
assert(type <= _NAMESPACE_TYPE_MAX);
if (namespace_info[type].root_inode == 0)
return -EBADR; /* Cannot answer this question */
const char *p = pid_namespace_path(0, type);
struct stat st;
r = RET_NERRNO(stat(p, &st));
if (r == -ENOENT)
/* If the /proc/ns/<type> API is not around in /proc/ then ns is off in the kernel and we are in the init ns */
return proc_mounted() == 0 ? -ENOSYS : true;
if (r < 0)
return r;
return st.st_ino == namespace_info[type].root_inode;
}
int is_our_namespace(int fd, NamespaceType request_type) { int is_our_namespace(int fd, NamespaceType request_type) {
int clone_flag; int clone_flag;

View File

@ -24,6 +24,7 @@ extern const struct namespace_info {
const char *proc_name; const char *proc_name;
const char *proc_path; const char *proc_path;
unsigned int clone_flag; unsigned int clone_flag;
ino_t root_inode;
} namespace_info[_NAMESPACE_TYPE_MAX + 1]; } namespace_info[_NAMESPACE_TYPE_MAX + 1];
int pidref_namespace_open( int pidref_namespace_open(
@ -74,6 +75,8 @@ int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_r
int namespace_open_by_type(NamespaceType type); int namespace_open_by_type(NamespaceType type);
int namespace_is_init(NamespaceType type);
int is_our_namespace(int fd, NamespaceType type); int is_our_namespace(int fd, NamespaceType type);
int is_idmapping_supported(const char *path); int is_idmapping_supported(const char *path);

View File

@ -429,7 +429,7 @@ int pidref_wait(const PidRef *pidref, siginfo_t *ret, int options) {
if (pidref_is_remote(pidref)) if (pidref_is_remote(pidref))
return -EREMOTE; return -EREMOTE;
if (pidref->pid == 1 || pidref->pid == getpid_cached()) if (pidref->pid == 1 || pidref_is_self(pidref))
return -ECHILD; return -ECHILD;
siginfo_t si = {}; siginfo_t si = {};

View File

@ -975,6 +975,28 @@ int getpeerpidfd(int fd) {
return pidfd; return pidfd;
} }
int getpeerpidref(int fd, PidRef *ret) {
int r;
assert(fd >= 0);
assert(ret);
int pidfd = getpeerpidfd(fd);
if (pidfd < 0) {
if (!ERRNO_IS_NEG_NOT_SUPPORTED(pidfd) && pidfd != -EINVAL)
return pidfd;
struct ucred ucred;
r = getpeercred(fd, &ucred);
if (r < 0)
return r;
return pidref_set_pid(ret, ucred.pid);
}
return pidref_set_pidfd_consume(ret, pidfd);
}
ssize_t send_many_fds_iov_sa( ssize_t send_many_fds_iov_sa(
int transport_fd, int transport_fd,
int *fds_array, size_t n_fds_array, int *fds_array, size_t n_fds_array,

View File

@ -20,6 +20,7 @@
#include "macro.h" #include "macro.h"
#include "missing_network.h" #include "missing_network.h"
#include "missing_socket.h" #include "missing_socket.h"
#include "pidref.h"
#include "sparse-endian.h" #include "sparse-endian.h"
union sockaddr_union { union sockaddr_union {
@ -154,6 +155,7 @@ int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret); int getpeersec(int fd, char **ret);
int getpeergroups(int fd, gid_t **ret); int getpeergroups(int fd, gid_t **ret);
int getpeerpidfd(int fd); int getpeerpidfd(int fd);
int getpeerpidref(int fd, PidRef *ret);
ssize_t send_many_fds_iov_sa( ssize_t send_many_fds_iov_sa(
int transport_fd, int transport_fd,

View File

@ -761,26 +761,24 @@ bool tty_is_console(const char *tty) {
} }
int vtnr_from_tty(const char *tty) { int vtnr_from_tty(const char *tty) {
int i, r; int r;
assert(tty); assert(tty);
tty = skip_dev_prefix(tty); tty = skip_dev_prefix(tty);
if (!startswith(tty, "tty") ) const char *e = startswith(tty, "tty");
if (!e)
return -EINVAL; return -EINVAL;
if (!ascii_isdigit(tty[3])) unsigned u;
return -EINVAL; r = safe_atou(e, &u);
r = safe_atoi(tty+3, &i);
if (r < 0) if (r < 0)
return r; return r;
if (!vtnr_is_valid(u))
return -ERANGE;
if (i < 0 || i > 63) return (int) u;
return -EINVAL;
return i;
} }
int resolve_dev_console(char **ret) { int resolve_dev_console(char **ret) {

View File

@ -157,3 +157,7 @@ int terminal_is_pty_fd(int fd);
int pty_open_peer_racefree(int fd, int mode); int pty_open_peer_racefree(int fd, int mode);
int pty_open_peer(int fd, int mode); int pty_open_peer(int fd, int mode);
static inline bool vtnr_is_valid(unsigned n) {
return n >= 1 && n <= 63;
}

View File

@ -585,6 +585,14 @@ static int running_in_cgroupns(void) {
if (!cg_ns_supported()) if (!cg_ns_supported())
return false; return false;
r = namespace_is_init(NAMESPACE_CGROUP);
if (r < 0)
log_debug_errno(r, "Failed to test if in root cgroup namespace, ignoring: %m");
else if (r > 0)
return false;
// FIXME: We really should drop the heuristics below.
r = cg_all_unified(); r = cg_all_unified();
if (r < 0) if (r < 0)
return r; return r;
@ -645,6 +653,16 @@ static int running_in_cgroupns(void) {
} }
} }
static int running_in_pidns(void) {
int r;
r = namespace_is_init(NAMESPACE_PID);
if (r < 0)
return log_debug_errno(r, "Failed to test if in root PID namespace, ignoring: %m");
return !r;
}
static Virtualization detect_container_files(void) { static Virtualization detect_container_files(void) {
static const struct { static const struct {
const char *file_path; const char *file_path;
@ -790,12 +808,21 @@ check_files:
r = running_in_cgroupns(); r = running_in_cgroupns();
if (r > 0) { if (r > 0) {
log_debug("Running in a cgroup namespace, assuming unknown container manager.");
v = VIRTUALIZATION_CONTAINER_OTHER; v = VIRTUALIZATION_CONTAINER_OTHER;
goto finish; goto finish;
} }
if (r < 0) if (r < 0)
log_debug_errno(r, "Failed to detect cgroup namespace: %m"); log_debug_errno(r, "Failed to detect cgroup namespace: %m");
/* Finally, the root pid namespace has an hardcoded inode number of 0xEFFFFFFC since kernel 3.8, so
* if all else fails we can check the inode number of our pid namespace and compare it. */
if (running_in_pidns() > 0) {
log_debug("Running in a pid namespace, assuming unknown container manager.");
v = VIRTUALIZATION_CONTAINER_OTHER;
goto finish;
}
/* If none of that worked, give up, assume no container manager. */ /* If none of that worked, give up, assume no container manager. */
v = VIRTUALIZATION_NONE; v = VIRTUALIZATION_NONE;
goto finish; goto finish;
@ -863,6 +890,14 @@ int running_in_userns(void) {
_cleanup_free_ char *line = NULL; _cleanup_free_ char *line = NULL;
int r; int r;
r = namespace_is_init(NAMESPACE_USER);
if (r < 0)
log_debug_errno(r, "Failed to test if in root user namespace, ignoring: %m");
else if (r > 0)
return false;
// FIXME: We really should drop the heuristics below.
r = userns_has_mapping("/proc/self/uid_map"); r = userns_has_mapping("/proc/self/uid_map");
if (r != 0) if (r != 0)
return r; return r;

View File

@ -1048,9 +1048,6 @@ static void device_enumerate(Manager *m) {
_cleanup_set_free_ Set *ready_units = NULL, *not_ready_units = NULL; _cleanup_set_free_ Set *ready_units = NULL, *not_ready_units = NULL;
Device *d; Device *d;
if (device_is_processed(dev) <= 0)
continue;
if (device_setup_units(m, dev, &ready_units, &not_ready_units) < 0) if (device_setup_units(m, dev, &ready_units, &not_ready_units) < 0)
continue; continue;

View File

@ -81,7 +81,7 @@ static void *server(void *p) {
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL; _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
assert_se(pidref_set_pidfd_take(&pidref, pidfd) >= 0); assert_se(pidref_set_pidfd_take(&pidref, pidfd) >= 0);
assert_se(pidref.pid == getpid_cached()); assert_se(pidref_is_self(&pidref));
} }
const gid_t *gl = NULL; const gid_t *gl = NULL;

View File

@ -125,14 +125,13 @@ int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_di
return 0; return 0;
} }
int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) { int json_dispatch_const_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
char **p = ASSERT_PTR(userdata); const char **p = ASSERT_PTR(userdata), *path;
const char *path;
assert(variant); assert(variant);
if (sd_json_variant_is_null(variant)) { if (sd_json_variant_is_null(variant)) {
*p = mfree(*p); *p = NULL;
return 0; return 0;
} }
@ -145,8 +144,24 @@ int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispa
if (!path_is_absolute(path)) if (!path_is_absolute(path))
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an absolute file system path.", strna(name)); return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an absolute file system path.", strna(name));
if (free_and_strdup(p, path) < 0) *p = path;
return json_log_oom(variant, flags); return 0;
}
int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
char **s = ASSERT_PTR(userdata);
const char *p;
int r;
assert_return(variant, -EINVAL);
r = json_dispatch_const_path(name, variant, flags, &p);
if (r < 0)
return r;
r = free_and_strdup(s, p);
if (r < 0)
return json_log(variant, flags, r, "Failed to allocate path string: %m");
return 0; return 0;
} }

View File

@ -113,6 +113,7 @@ int json_dispatch_user_group_name(const char *name, sd_json_variant *variant, sd
int json_dispatch_const_user_group_name(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_const_user_group_name(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_in_addr(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_const_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
int json_dispatch_ifindex(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata); int json_dispatch_ifindex(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);

View File

@ -334,46 +334,46 @@ _public_ int sd_pidfd_get_cgroup(int pidfd, char **ret_cgroup) {
return 0; return 0;
} }
_public_ int sd_peer_get_session(int fd, char **session) { _public_ int sd_peer_get_session(int fd, char **ret) {
struct ucred ucred = UCRED_INVALID;
int r; int r;
assert_return(fd >= 0, -EBADF); assert_return(fd >= 0, -EBADF);
assert_return(session, -EINVAL); assert_return(ret, -EINVAL);
r = getpeercred(fd, &ucred); _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
r = getpeerpidref(fd, &pidref);
if (r < 0) if (r < 0)
return r; return r;
return cg_pid_get_session(ucred.pid, session); return cg_pidref_get_session(&pidref, ret);
} }
_public_ int sd_peer_get_owner_uid(int fd, uid_t *uid) { _public_ int sd_peer_get_owner_uid(int fd, uid_t *ret) {
struct ucred ucred;
int r; int r;
assert_return(fd >= 0, -EBADF); assert_return(fd >= 0, -EBADF);
assert_return(uid, -EINVAL); assert_return(ret, -EINVAL);
r = getpeercred(fd, &ucred); _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
r = getpeerpidref(fd, &pidref);
if (r < 0) if (r < 0)
return r; return r;
return cg_pid_get_owner_uid(ucred.pid, uid); return cg_pidref_get_owner_uid(&pidref, ret);
} }
_public_ int sd_peer_get_unit(int fd, char **unit) { _public_ int sd_peer_get_unit(int fd, char **ret) {
struct ucred ucred;
int r; int r;
assert_return(fd >= 0, -EBADF); assert_return(fd >= 0, -EBADF);
assert_return(unit, -EINVAL); assert_return(ret, -EINVAL);
r = getpeercred(fd, &ucred); _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
r = getpeerpidref(fd, &pidref);
if (r < 0) if (r < 0)
return r; return r;
return cg_pid_get_unit(ucred.pid, unit); return cg_pidref_get_unit(&pidref, ret);
} }
_public_ int sd_peer_get_user_unit(int fd, char **unit) { _public_ int sd_peer_get_user_unit(int fd, char **unit) {

View File

@ -1698,7 +1698,8 @@ _public_ int sd_varlink_get_events(sd_varlink *v) {
ret |= EPOLLIN; ret |= EPOLLIN;
if (!v->write_disconnected && if (!v->write_disconnected &&
v->output_buffer_size > 0) (v->output_queue ||
v->output_buffer_size > 0))
ret |= EPOLLOUT; ret |= EPOLLOUT;
return ret; return ret;
@ -3218,7 +3219,13 @@ _public_ int sd_varlink_server_new(sd_varlink_server **ret, sd_varlink_server_fl
int r; int r;
assert_return(ret, -EINVAL); assert_return(ret, -EINVAL);
assert_return((flags & ~(SD_VARLINK_SERVER_ROOT_ONLY|SD_VARLINK_SERVER_MYSELF_ONLY|SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA|SD_VARLINK_SERVER_INPUT_SENSITIVE)) == 0, -EINVAL); assert_return((flags & ~(SD_VARLINK_SERVER_ROOT_ONLY|
SD_VARLINK_SERVER_MYSELF_ONLY|
SD_VARLINK_SERVER_ACCOUNT_UID|
SD_VARLINK_SERVER_INHERIT_USERDATA|
SD_VARLINK_SERVER_INPUT_SENSITIVE|
SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT|
SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT)) == 0, -EINVAL);
s = new(sd_varlink_server, 1); s = new(sd_varlink_server, 1);
if (!s) if (!s)
@ -3399,6 +3406,9 @@ _public_ int sd_varlink_server_add_connection_pair(
if (asprintf(&desc, "%s-%i-%i", varlink_server_description(server), input_fd, output_fd) >= 0) if (asprintf(&desc, "%s-%i-%i", varlink_server_description(server), input_fd, output_fd) >= 0)
v->description = TAKE_PTR(desc); v->description = TAKE_PTR(desc);
(void) sd_varlink_set_allow_fd_passing_input(v, FLAGS_SET(server->flags, SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT));
(void) sd_varlink_set_allow_fd_passing_output(v, FLAGS_SET(server->flags, SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT));
/* Link up the server and the connection, and take reference in both directions. Note that the /* Link up the server and the connection, and take reference in both directions. Note that the
* reference on the connection is left dangling. It will be dropped when the connection is closed, * reference on the connection is left dangling. It will be dropped when the connection is closed,
* which happens in varlink_close(), including in the event loop quit callback. */ * which happens in varlink_close(), including in the event loop quit callback. */

View File

@ -85,6 +85,8 @@ static int get_sender_session(
const char *name; const char *name;
int r; int r;
assert(m);
/* Acquire the sender's session. This first checks if the sending process is inside a session itself, /* Acquire the sender's session. This first checks if the sending process is inside a session itself,
* and returns that. If not and 'consult_display' is true, this returns the display session of the * and returns that. If not and 'consult_display' is true, this returns the display session of the
* owning user of the caller. */ * owning user of the caller. */
@ -827,189 +829,24 @@ static int method_list_inhibitors(sd_bus_message *message, void *userdata, sd_bu
return sd_bus_send(NULL, reply, NULL); return sd_bus_send(NULL, reply, NULL);
} }
static int create_session( static int manager_choose_session_id(
sd_bus_message *message, Manager *m,
void *userdata, PidRef *leader,
sd_bus_error *error, char **ret_id) {
uid_t uid,
pid_t leader_pid,
int leader_pidfd,
const char *service,
const char *type,
const char *class,
const char *desktop,
const char *cseat,
uint32_t vtnr,
const char *tty,
const char *display,
int remote,
const char *remote_user,
const char *remote_host,
uint64_t flags) {
_cleanup_(pidref_done) PidRef leader = PIDREF_NULL;
Manager *m = ASSERT_PTR(userdata);
_cleanup_free_ char *id = NULL;
Session *session = NULL;
uint32_t audit_id = 0;
User *user = NULL;
Seat *seat = NULL;
SessionType t;
SessionClass c;
int r; int r;
assert(message); assert(m);
assert(pidref_is_set(leader));
assert(ret_id);
if (!uid_is_valid(uid)) /* Try to keep our session IDs and the audit session IDs in sync */
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID"); _cleanup_free_ char *id = NULL;
uint32_t audit_id = AUDIT_SESSION_INVALID;
if (flags != 0) r = audit_session_from_pid(leader, &audit_id);
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be zero.");
if (leader_pidfd >= 0)
r = pidref_set_pidfd(&leader, leader_pidfd);
else if (leader_pid == 0)
r = bus_query_sender_pidref(message, &leader);
else {
if (leader_pid < 0)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Leader PID is not valid");
r = pidref_set_pid(&leader, leader_pid);
}
if (r < 0) if (r < 0)
return r; log_debug_errno(r, "Failed to read audit session ID of process " PID_FMT ", ignoring: %m", leader->pid);
if (leader.pid == 1 || leader.pid == getpid_cached())
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
if (isempty(type))
t = _SESSION_TYPE_INVALID;
else { else {
t = session_type_from_string(type);
if (t < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid session type %s", type);
}
if (isempty(class))
c = _SESSION_CLASS_INVALID;
else {
c = session_class_from_string(class);
if (c < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid session class %s", class);
}
if (isempty(desktop))
desktop = NULL;
else {
if (!string_is_safe(desktop))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid desktop string %s", desktop);
}
if (isempty(cseat))
seat = NULL;
else {
seat = hashmap_get(m->seats, cseat);
if (!seat)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT,
"No seat '%s' known", cseat);
}
if (tty_is_vc(tty)) {
int v;
if (!seat)
seat = m->seat0;
else if (seat != m->seat0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"TTY %s is virtual console but seat %s is not seat0", tty, seat->id);
v = vtnr_from_tty(tty);
if (v <= 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Cannot determine VT number from virtual console TTY %s", tty);
if (vtnr == 0)
vtnr = (uint32_t) v;
else if (vtnr != (uint32_t) v)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Specified TTY and VT number do not match");
} else if (tty_is_console(tty)) {
if (!seat)
seat = m->seat0;
else if (seat != m->seat0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Console TTY specified but seat is not seat0");
if (vtnr != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Console TTY specified but VT number is not 0");
}
if (seat) {
if (seat_has_vts(seat)) {
if (vtnr <= 0 || vtnr > 63)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"VT number out of range");
} else {
if (vtnr != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Seat has no VTs but VT number not 0");
}
}
if (t == _SESSION_TYPE_INVALID) {
if (!isempty(display))
t = SESSION_X11;
else if (!isempty(tty))
t = SESSION_TTY;
else
t = SESSION_UNSPECIFIED;
}
if (c == _SESSION_CLASS_INVALID) {
if (t == SESSION_UNSPECIFIED)
c = SESSION_BACKGROUND;
else
c = SESSION_USER;
}
/* Check if we are already in a logind session, and if so refuse. */
r = manager_get_session_by_pidref(m, &leader, /* ret_session= */ NULL);
if (r < 0)
return log_debug_errno(
r,
"Failed to check if process " PID_FMT " is already in a session: %m",
leader.pid);
if (r > 0)
return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY,
"Already running in a session or user slice");
/* Old gdm and lightdm start the user-session on the same VT as the greeter session. But they destroy
* the greeter session after the user-session and want the user-session to take over the VT. We need
* to support this for backwards-compatibility, so make sure we allow new sessions on a VT that a
* greeter is running on. Furthermore, to allow re-logins, we have to allow a greeter to take over a
* used VT for the exact same reasons. */
if (c != SESSION_GREETER &&
vtnr > 0 &&
vtnr < MALLOC_ELEMENTSOF(m->seat0->positions) &&
m->seat0->positions[vtnr] &&
m->seat0->positions[vtnr]->class != SESSION_GREETER)
return sd_bus_error_set(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session");
if (hashmap_size(m->sessions) >= m->sessions_max)
return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED,
"Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.",
m->sessions_max);
(void) audit_session_from_pid(&leader, &audit_id);
if (audit_session_is_valid(audit_id)) {
/* Keep our session IDs and the audit session IDs in sync */
if (asprintf(&id, "%"PRIu32, audit_id) < 0) if (asprintf(&id, "%"PRIu32, audit_id) < 0)
return -ENOMEM; return -ENOMEM;
@ -1017,12 +854,11 @@ static int create_session(
* not trust the audit data and let's better register a new ID */ * not trust the audit data and let's better register a new ID */
if (hashmap_contains(m->sessions, id)) { if (hashmap_contains(m->sessions, id)) {
log_warning("Existing logind session ID %s used by new audit session, ignoring.", id); log_warning("Existing logind session ID %s used by new audit session, ignoring.", id);
audit_id = AUDIT_SESSION_INVALID;
id = mfree(id); id = mfree(id);
} }
} }
if (!id) { if (!id)
do { do {
id = mfree(id); id = mfree(id);
@ -1030,15 +866,97 @@ static int create_session(
return -ENOMEM; return -ENOMEM;
} while (hashmap_contains(m->sessions, id)); } while (hashmap_contains(m->sessions, id));
}
/* The generated names should not clash with 'auto' or 'self' */ /* The generated names should not clash with 'auto' or 'self' */
assert(!SESSION_IS_SELF(id)); assert(!SESSION_IS_SELF(id));
assert(!SESSION_IS_AUTO(id)); assert(!SESSION_IS_AUTO(id));
*ret_id = TAKE_PTR(id);
return 0;
}
int manager_create_session(
Manager *m,
uid_t uid,
PidRef *leader, /* consumed */
const char *service,
SessionType type,
SessionClass class,
const char *desktop,
Seat *seat,
unsigned vtnr,
const char *tty,
const char *display,
bool remote,
const char *remote_user,
const char *remote_host,
Session **ret_session) {
int r;
assert(m);
assert(uid_is_valid(uid));
assert(pidref_is_set(leader));
assert(ret_session);
/* Returns:
* -EBUSY client is already in a session
* -EADDRNOTAVAIL VT is already taken
* -EUSERS limit of sessions reached
*/
if (type == _SESSION_TYPE_INVALID) {
if (!isempty(display))
type = SESSION_X11;
else if (!isempty(tty))
type = SESSION_TTY;
else
type = SESSION_UNSPECIFIED;
}
if (class == _SESSION_CLASS_INVALID) {
if (type == SESSION_UNSPECIFIED)
class = SESSION_BACKGROUND;
else
class = SESSION_USER;
}
/* Check if we are already in a logind session, and if so refuse. */
r = manager_get_session_by_pidref(m, leader, /* ret_session= */ NULL);
if (r < 0)
return log_debug_errno(
r,
"Failed to check if process " PID_FMT " is already in a session: %m",
leader->pid);
if (r > 0)
return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "Client is already in a session.");
/* Old gdm and lightdm start the user-session on the same VT as the greeter session. But they destroy
* the greeter session after the user-session and want the user-session to take over the VT. We need
* to support this for backwards-compatibility, so make sure we allow new sessions on a VT that a
* greeter is running on. Furthermore, to allow re-logins, we have to allow a greeter to take over a
* used VT for the exact same reasons. */
if (class != SESSION_GREETER &&
vtnr > 0 &&
vtnr < MALLOC_ELEMENTSOF(m->seat0->positions) &&
m->seat0->positions[vtnr] &&
m->seat0->positions[vtnr]->class != SESSION_GREETER)
return log_debug_errno(SYNTHETIC_ERRNO(EADDRNOTAVAIL), "VT already occupied by a session.");
if (hashmap_size(m->sessions) >= m->sessions_max)
return log_debug_errno(SYNTHETIC_ERRNO(EUSERS), "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max);
_cleanup_free_ char *id = NULL;
r = manager_choose_session_id(m, leader, &id);
if (r < 0)
return r;
/* If we are not watching utmp already, try again */ /* If we are not watching utmp already, try again */
manager_reconnect_utmp(m); manager_reconnect_utmp(m);
User *user = NULL;
Session *session = NULL;
r = manager_add_user_by_uid(m, uid, &user); r = manager_add_user_by_uid(m, uid, &user);
if (r < 0) if (r < 0)
goto fail; goto fail;
@ -1048,21 +966,21 @@ static int create_session(
goto fail; goto fail;
session_set_user(session, user); session_set_user(session, user);
r = session_set_leader_consume(session, TAKE_PIDREF(leader)); r = session_set_leader_consume(session, TAKE_PIDREF(*leader));
if (r < 0) if (r < 0)
goto fail; goto fail;
session->original_type = session->type = t; session->original_type = session->type = type;
session->remote = remote; session->remote = remote;
session->vtnr = vtnr; session->vtnr = vtnr;
session->class = c; session->class = class;
/* Once the first session that is of a pinning class shows up we'll change the GC mode for the user /* Once the first session that is of a pinning class shows up we'll change the GC mode for the user
* from USER_GC_BY_ANY to USER_GC_BY_PIN, so that the user goes away once the last pinning session * from USER_GC_BY_ANY to USER_GC_BY_PIN, so that the user goes away once the last pinning session
* goes away. Background: we want that user@.service when started manually remains around (which * goes away. Background: we want that user@.service when started manually remains around (which
* itself is a non-pinning session), but gets stopped when the last pinning session goes away. */ * itself is a non-pinning session), but gets stopped when the last pinning session goes away. */
if (SESSION_CLASS_PIN_USER(c)) if (SESSION_CLASS_PIN_USER(class))
user->gc_mode = USER_GC_BY_PIN; user->gc_mode = USER_GC_BY_PIN;
if (!isempty(tty)) { if (!isempty(tty)) {
@ -1109,14 +1027,184 @@ static int create_session(
goto fail; goto fail;
} }
*ret_session = session;
return 0;
fail:
if (session)
session_add_to_gc_queue(session);
if (user)
user_add_to_gc_queue(user);
return r;
}
static int manager_create_session_by_bus(
Manager *m,
sd_bus_message *message,
sd_bus_error *error,
uid_t uid,
pid_t leader_pid,
int leader_pidfd,
const char *service,
const char *type,
const char *class,
const char *desktop,
const char *cseat,
uint32_t vtnr,
const char *tty,
const char *display,
int remote,
const char *remote_user,
const char *remote_host,
uint64_t flags) {
int r;
assert(m);
assert(message);
if (!uid_is_valid(uid))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid UID");
if (flags != 0)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Flags must be zero.");
_cleanup_(pidref_done) PidRef leader = PIDREF_NULL;
if (leader_pidfd >= 0)
r = pidref_set_pidfd(&leader, leader_pidfd);
else if (leader_pid == 0)
r = bus_query_sender_pidref(message, &leader);
else {
if (leader_pid < 0)
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Leader PID is not valid");
r = pidref_set_pid(&leader, leader_pid);
}
if (r < 0)
return r;
if (leader.pid == 1 || pidref_is_self(&leader))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
SessionType t;
if (isempty(type))
t = _SESSION_TYPE_INVALID;
else {
t = session_type_from_string(type);
if (t < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid session type %s", type);
}
SessionClass c;
if (isempty(class))
c = _SESSION_CLASS_INVALID;
else {
c = session_class_from_string(class);
if (c < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid session class %s", class);
}
if (isempty(desktop))
desktop = NULL;
else {
if (!string_is_safe(desktop))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid desktop string %s", desktop);
}
Seat *seat = NULL;
if (isempty(cseat))
seat = NULL;
else {
seat = hashmap_get(m->seats, cseat);
if (!seat)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT,
"No seat '%s' known", cseat);
}
if (isempty(tty))
tty = NULL;
else if (tty_is_vc(tty)) {
int v;
if (!seat)
seat = m->seat0;
else if (seat != m->seat0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"TTY %s is virtual console but seat %s is not seat0", tty, seat->id);
v = vtnr_from_tty(tty);
if (v <= 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Cannot determine VT number from virtual console TTY %s", tty);
if (vtnr == 0)
vtnr = (uint32_t) v;
else if (vtnr != (uint32_t) v)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Specified TTY and VT number do not match");
} else if (tty_is_console(tty)) {
if (!seat)
seat = m->seat0;
else if (seat != m->seat0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Console TTY specified but seat is not seat0");
if (vtnr != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Console TTY specified but VT number is not 0");
}
if (seat) {
if (seat_has_vts(seat)) {
if (!vtnr_is_valid(vtnr))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"VT number out of range");
} else {
if (vtnr != 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Seat has no VTs but VT number not 0");
}
}
Session *session;
r = manager_create_session(
m,
uid,
&leader,
service,
t,
c,
desktop,
seat,
vtnr,
tty,
display,
remote,
remote_user,
remote_host,
&session);
if (r == -EBUSY)
return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already running in a session or user slice");
if (r == -EADDRNOTAVAIL)
return sd_bus_error_set(error, BUS_ERROR_SESSION_BUSY, "Virtual terminal already occupied by a session");
if (r == -EUSERS)
return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max);
if (r < 0)
return r;
r = sd_bus_message_enter_container(message, 'a', "(sv)"); r = sd_bus_message_enter_container(message, 'a', "(sv)");
if (r < 0) if (r < 0)
goto fail; goto fail;
r = session_start(session, message, error); r = session_start(session, message, error);
if (r < 0) if (r < 0)
goto fail; goto fail;
r = sd_bus_message_exit_container(message); r = sd_bus_message_exit_container(message);
if (r < 0) if (r < 0)
goto fail; goto fail;
@ -1130,7 +1218,7 @@ static int create_session(
* all is complete - or wait again. */ * all is complete - or wait again. */
r = session_send_create_reply(session, /* error= */ NULL); r = session_send_create_reply(session, /* error= */ NULL);
if (r < 0) if (r < 0)
return r; goto fail;
return 1; return 1;
@ -1138,9 +1226,6 @@ fail:
if (session) if (session)
session_add_to_gc_queue(session); session_add_to_gc_queue(session);
if (user)
user_add_to_gc_queue(user);
return r; return r;
} }
@ -1175,9 +1260,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
if (r < 0) if (r < 0)
return r; return r;
return create_session( return manager_create_session_by_bus(
message,
userdata, userdata,
message,
error, error,
uid, uid,
leader_pid, leader_pid,
@ -1223,9 +1308,9 @@ static int method_create_session_pidfd(sd_bus_message *message, void *userdata,
if (r < 0) if (r < 0)
return r; return r;
return create_session( return manager_create_session_by_bus(
message,
userdata, userdata,
message,
error, error,
uid, uid,
/* leader_pid = */ 0, /* leader_pid = */ 0,

View File

@ -47,4 +47,21 @@ int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *erro
void manager_load_scheduled_shutdown(Manager *m); void manager_load_scheduled_shutdown(Manager *m);
int manager_create_session(
Manager *m,
uid_t uid,
PidRef *leader,
const char *service,
SessionType type,
SessionClass class,
const char *desktop,
Seat *seat,
unsigned vtnr,
const char *tty,
const char *display,
bool remote,
const char *remote_user,
const char *remote_host,
Session **ret_session);
extern const BusObjectImplementation manager_object; extern const BusObjectImplementation manager_object;

View File

@ -899,62 +899,40 @@ int session_send_lock_all(Manager *m, bool lock) {
return r; return r;
} }
static bool session_job_pending(Session *s) { int session_send_create_reply_bus(Session *s, const sd_bus_error *error) {
assert(s);
assert(s->user);
/* Check if we have some jobs enqueued and not finished yet. Each time we get JobRemoved signal about
* relevant units, session_send_create_reply and hence us is called (see match_job_removed).
* Note that we don't care about job result here. */
return s->scope_job ||
s->user->runtime_dir_job ||
(SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) && s->user->service_manager_job);
}
int session_send_create_reply(Session *s, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
_cleanup_close_ int fifo_fd = -EBADF;
_cleanup_free_ char *p = NULL;
assert(s); assert(s);
/* This is called after the session scope and the user service were successfully created, and finishes where /* This is called after the session scope and the user service were successfully created, and
* bus_manager_create_session() left off. */ * finishes where manager_create_session() left off. */
if (!s->create_message) _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = TAKE_PTR(s->create_message);
if (!c)
return 0; return 0;
/* If error occurred, return it immediately. Otherwise let's wait for all jobs to finish before if (sd_bus_error_is_set(error))
* continuing. */
if (!sd_bus_error_is_set(error) && session_job_pending(s))
return 0;
c = TAKE_PTR(s->create_message);
if (error)
return sd_bus_reply_method_error(c, error); return sd_bus_reply_method_error(c, error);
fifo_fd = session_create_fifo(s); _cleanup_close_ int fifo_fd = session_create_fifo(s);
if (fifo_fd < 0) if (fifo_fd < 0)
return fifo_fd; return fifo_fd;
/* Update the session state file before we notify the client about the result. */ /* Update the session state file before we notify the client about the result. */
session_save(s); session_save(s);
p = session_bus_path(s); _cleanup_free_ char *p = session_bus_path(s);
if (!p) if (!p)
return -ENOMEM; return -ENOMEM;
log_debug("Sending reply about created session: " log_debug("Sending D-Bus reply about created session: "
"id=%s object_path=%s uid=%u runtime_path=%s " "id=%s object_path=%s uid=" UID_FMT " runtime_path=%s "
"session_fd=%d seat=%s vtnr=%u", "session_fd=%d seat=%s vtnr=%u",
s->id, s->id,
p, p,
(uint32_t) s->user->user_record->uid, s->user->user_record->uid,
s->user->runtime_path, s->user->runtime_path,
fifo_fd, fifo_fd,
s->seat ? s->seat->id : "", s->seat ? s->seat->id : "",
(uint32_t) s->vtnr); s->vtnr);
return sd_bus_reply_method_return( return sd_bus_reply_method_return(
c, "soshusub", c, "soshusub",
@ -968,7 +946,7 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
false); false);
} }
int session_send_upgrade_reply(Session *s, sd_bus_error *error) { int session_send_upgrade_reply(Session *s, const sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
assert(s); assert(s);

View File

@ -15,8 +15,8 @@ int session_send_changed(Session *s, const char *properties, ...) _sentinel_;
int session_send_lock(Session *s, bool lock); int session_send_lock(Session *s, bool lock);
int session_send_lock_all(Manager *m, bool lock); int session_send_lock_all(Manager *m, bool lock);
int session_send_create_reply(Session *s, sd_bus_error *error); int session_send_create_reply_bus(Session *s, const sd_bus_error *error);
int session_send_upgrade_reply(Session *s, sd_bus_error *error); int session_send_upgrade_reply(Session *s, const sd_bus_error *error);
int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error);

View File

@ -29,6 +29,7 @@
#include "logind-session-dbus.h" #include "logind-session-dbus.h"
#include "logind-session.h" #include "logind-session.h"
#include "logind-user-dbus.h" #include "logind-user-dbus.h"
#include "logind-varlink.h"
#include "mkdir-label.h" #include "mkdir-label.h"
#include "parse-util.h" #include "parse-util.h"
#include "path-util.h" #include "path-util.h"
@ -192,6 +193,8 @@ Session* session_free(Session *s) {
sd_bus_message_unref(s->create_message); sd_bus_message_unref(s->create_message);
sd_bus_message_unref(s->upgrade_message); sd_bus_message_unref(s->upgrade_message);
sd_varlink_unref(s->create_link);
free(s->tty); free(s->tty);
free(s->display); free(s->display);
free(s->remote_host); free(s->remote_host);
@ -1648,6 +1651,35 @@ void session_drop_controller(Session *s) {
session_restore_vt(s); session_restore_vt(s);
} }
bool session_job_pending(Session *s) {
assert(s);
assert(s->user);
/* Check if we have some jobs enqueued and not finished yet. Each time we get JobRemoved signal about
* relevant units, session_send_create_reply and hence us is called (see match_job_removed).
* Note that we don't care about job result here. */
return s->scope_job ||
s->user->runtime_dir_job ||
(SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) && s->user->service_manager_job);
}
int session_send_create_reply(Session *s, const sd_bus_error *error) {
int r;
assert(s);
/* If error occurred, return it immediately. Otherwise let's wait for all jobs to finish before
* continuing. */
if (!sd_bus_error_is_set(error) && session_job_pending(s))
return 0;
r = 0;
RET_GATHER(r, session_send_create_reply_bus(s, error));
RET_GATHER(r, session_send_create_reply_varlink(s, error));
return r;
}
static const char* const session_state_table[_SESSION_STATE_MAX] = { static const char* const session_state_table[_SESSION_STATE_MAX] = {
[SESSION_OPENING] = "opening", [SESSION_OPENING] = "opening",
[SESSION_ONLINE] = "online", [SESSION_ONLINE] = "online",

View File

@ -150,6 +150,8 @@ struct Session {
sd_bus_message *create_message; /* The D-Bus message used to create the session, which we haven't responded to yet */ sd_bus_message *create_message; /* The D-Bus message used to create the session, which we haven't responded to yet */
sd_bus_message *upgrade_message; /* The D-Bus message used to upgrade the session class user-incomplete → user, which we haven't responded to yet */ sd_bus_message *upgrade_message; /* The D-Bus message used to upgrade the session class user-incomplete → user, which we haven't responded to yet */
sd_varlink *create_link; /* The Varlink connection used to create session, which we haven't responded to yet */
/* Set up when a client requested to release the session via the bus */ /* Set up when a client requested to release the session via the bus */
sd_event_source *timer_event_source; sd_event_source *timer_event_source;
@ -216,6 +218,10 @@ bool session_is_controller(Session *s, const char *sender);
int session_set_controller(Session *s, const char *sender, bool force, bool prepare); int session_set_controller(Session *s, const char *sender, bool force, bool prepare);
void session_drop_controller(Session *s); void session_drop_controller(Session *s);
bool session_job_pending(Session *s);
int session_send_create_reply(Session *s, const sd_bus_error *error);
static inline bool SESSION_IS_SELF(const char *name) { static inline bool SESSION_IS_SELF(const char *name) {
return isempty(name) || streq(name, "self"); return isempty(name) || streq(name, "self");
} }

383
src/login/logind-varlink.c Normal file
View File

@ -0,0 +1,383 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "cgroup-util.h"
#include "fd-util.h"
#include "json-util.h"
#include "logind.h"
#include "logind-dbus.h"
#include "logind-session-dbus.h"
#include "logind-varlink.h"
#include "terminal-util.h"
#include "user-util.h"
#include "varlink-io.systemd.Login.h"
#include "varlink-util.h"
static int manager_varlink_get_session_by_peer(
Manager *m,
sd_varlink *link,
bool consult_display,
Session **ret) {
int r;
assert(m);
assert(link);
assert(ret);
/* Determines the session of the peer. If the peer is not part of a session, but consult_display is
* true, then will return the display session of the peer's owning user */
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
r = varlink_get_peer_pidref(link, &pidref);
if (r < 0)
return log_error_errno(r, "Failed to acquire peer PID: %m");
Session *session = NULL;
_cleanup_free_ char *name = NULL;
r = cg_pidref_get_session(&pidref, &name);
if (r < 0) {
if (!consult_display)
log_debug_errno(r, "Failed to acquire session of peer, giving up: %m");
else {
log_debug_errno(r, "Failed to acquire session of peer, trying to find owner UID: %m");
uid_t uid;
r = cg_pidref_get_owner_uid(&pidref, &uid);
if (r < 0)
log_debug_errno(r, "Failed to acquire owning UID of peer, giving up: %m");
else {
User *user = hashmap_get(m->users, UID_TO_PTR(uid));
if (user)
session = user->display;
}
}
} else
session = hashmap_get(m->sessions, name);
if (!session)
return sd_varlink_error(link, "io.systemd.Login.NoSuchSession", /* parameters= */ NULL);
*ret = session;
return 0;
}
static int manager_varlink_get_session_by_name(
Manager *m,
sd_varlink *link,
const char *name,
Session **ret) {
assert(m);
assert(link);
assert(ret);
/* Resolves a session name to a session object. Supports resolving the special names "self" and "auto". */
if (SESSION_IS_SELF(name))
return manager_varlink_get_session_by_peer(m, link, /* consult_display= */ false, ret);
if (SESSION_IS_AUTO(name))
return manager_varlink_get_session_by_peer(m, link, /* consult_display= */ true, ret);
Session *session = hashmap_get(m->sessions, name);
if (!session)
return sd_varlink_error(link, "io.systemd.Login.NoSuchSession", /* parameters= */ NULL);
*ret = session;
return 0;
}
int session_send_create_reply_varlink(Session *s, const sd_bus_error *error) {
assert(s);
/* This is called after the session scope and the user service were successfully created, and
* finishes where manager_create_session() left off. */
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = TAKE_PTR(s->create_link);
if (!vl)
return 0;
if (sd_bus_error_is_set(error))
return sd_varlink_error(vl, "io.systemd.Login.UnitAllocationFailed", /* parameters= */ NULL);
_cleanup_close_ int fifo_fd = session_create_fifo(s);
if (fifo_fd < 0)
return fifo_fd;
/* Update the session state file before we notify the client about the result. */
session_save(s);
log_debug("Sending Varlink reply about created session: "
"id=%s uid=" UID_FMT " runtime_path=%s "
"session_fd=%d seat=%s vtnr=%u",
s->id,
s->user->user_record->uid,
s->user->runtime_path,
fifo_fd,
s->seat ? s->seat->id : "",
s->vtnr);
int fifo_fd_idx = sd_varlink_push_fd(vl, fifo_fd);
if (fifo_fd_idx < 0) {
log_error_errno(fifo_fd_idx, "Failed to push FIFO fd to Varlink: %m");
return sd_varlink_error_errno(vl, fifo_fd_idx);
}
TAKE_FD(fifo_fd);
return sd_varlink_replybo(
vl,
SD_JSON_BUILD_PAIR_STRING("Id", s->id),
SD_JSON_BUILD_PAIR_STRING("RuntimePath", s->user->runtime_path),
SD_JSON_BUILD_PAIR_UNSIGNED("SessionFileDescriptor", fifo_fd_idx),
SD_JSON_BUILD_PAIR_UNSIGNED("UID", s->user->user_record->uid),
SD_JSON_BUILD_PAIR_CONDITION(!!s->seat, "Seat", SD_JSON_BUILD_STRING(s->seat ? s->seat->id : NULL)),
SD_JSON_BUILD_PAIR_CONDITION(s->vtnr > 0, "VTNr", SD_JSON_BUILD_UNSIGNED(s->vtnr)));
}
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_session_class, SessionClass, session_class_from_string);
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_session_type, SessionType, session_type_from_string);
typedef struct CreateSessionParameters {
uid_t uid;
PidRef pid;
const char *service;
SessionType type;
SessionClass class;
const char *desktop;
const char *seat;
unsigned vtnr;
const char *tty;
const char *display;
int remote;
const char *remote_user;
const char *remote_host;
} CreateSessionParameters;
static void create_session_parameters_done(CreateSessionParameters *p) {
pidref_done(&p->pid);
}
static int vl_method_create_session(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
Manager *m = ASSERT_PTR(userdata);
int r;
static const sd_json_dispatch_field dispatch_table[] = {
{ "UID", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(CreateSessionParameters, uid), SD_JSON_MANDATORY },
{ "PID", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_pidref, offsetof(CreateSessionParameters, pid), SD_JSON_RELAX },
{ "Service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, service), 0 },
{ "Type", SD_JSON_VARIANT_STRING, json_dispatch_session_type, offsetof(CreateSessionParameters, type), SD_JSON_MANDATORY },
{ "Class", SD_JSON_VARIANT_STRING, json_dispatch_session_class, offsetof(CreateSessionParameters, class), SD_JSON_MANDATORY },
{ "Desktop", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, desktop), SD_JSON_STRICT },
{ "Seat", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, seat), 0 },
{ "VTNr", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, offsetof(CreateSessionParameters, vtnr), 0 },
{ "TTY", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, tty), 0 },
{ "Display", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, display), 0 },
{ "Remote", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(CreateSessionParameters, remote), 0 },
{ "RemoteUser", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, remote_user), 0 },
{ "RemoteHost", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(CreateSessionParameters, remote_host), 0 },
{}
};
_cleanup_(create_session_parameters_done) CreateSessionParameters p = {
.uid = UID_INVALID,
.pid = PIDREF_NULL,
.class = _SESSION_CLASS_INVALID,
.type = _SESSION_TYPE_INVALID,
.remote = -1,
};
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
if (r != 0)
return r;
Seat *seat = NULL;
if (p.seat) {
seat = hashmap_get(m->seats, p.seat);
if (!seat)
return sd_varlink_replyb(link, "io.systemd.Login.NoSuchSeat", /* parameters= */ NULL);
}
if (p.tty) {
if (tty_is_vc(p.tty)) {
if (!seat)
seat = m->seat0;
else if (seat != m->seat0)
return sd_varlink_error_invalid_parameter_name(link, "Seat");
int v = vtnr_from_tty(p.tty);
if (v <= 0)
return sd_varlink_error_invalid_parameter_name(link, "TTY");
if (p.vtnr == 0)
p.vtnr = v;
else if (p.vtnr != (unsigned) v)
return sd_varlink_error_invalid_parameter_name(link, "VTNr");
} else if (tty_is_console(p.tty)) {
if (!seat)
seat = m->seat0;
else if (seat != m->seat0)
return sd_varlink_error_invalid_parameter_name(link, "Seat");
if (p.vtnr != 0)
return sd_varlink_error_invalid_parameter_name(link, "VTNr");
}
}
if (seat) {
if (seat_has_vts(seat)) {
if (!vtnr_is_valid(p.vtnr))
return sd_varlink_error_invalid_parameter_name(link, "VTNr");
} else {
if (p.vtnr != 0)
return sd_varlink_error_invalid_parameter_name(link, "VTNr");
}
}
if (p.remote < 0)
p.remote = p.remote_user || p.remote_host;
/* Before we continue processing this, let's ensure the peer is privileged */
uid_t peer_uid;
r = sd_varlink_get_peer_uid(link, &peer_uid);
if (r < 0)
return log_debug_errno(r, "Failed to get peer UID: %m");
if (peer_uid != 0)
return sd_varlink_error(link, SD_VARLINK_ERROR_PERMISSION_DENIED, /* parameters= */ NULL);
if (!pidref_is_set(&p.pid)) {
r = varlink_get_peer_pidref(link, &p.pid);
if (r < 0)
return log_debug_errno(r, "Failed to get peer pidref: %m");
}
Session *session;
r = manager_create_session(
m,
p.uid,
&p.pid,
p.service,
p.type,
p.class,
p.desktop,
seat,
p.vtnr,
p.tty,
p.display,
p.remote,
p.remote_user,
p.remote_host,
&session);
if (r == -EBUSY)
return sd_varlink_error(link, "io.systemd.Login.AlreadySessionMember", /* parameters= */ NULL);
if (r == -EADDRNOTAVAIL)
return sd_varlink_error(link, "io.systemd.Login.VirtualTerminalAlreadyTaken", /* parameters= */ NULL);
if (r == -EUSERS)
return sd_varlink_error(link, "io.systemd.Login.TooManySessions", /* parameters= */ NULL);
if (r < 0)
return r;
r = session_start(session, /* properties= */ NULL, /* error= */ NULL);
if (r < 0)
goto fail;
session->create_link = sd_varlink_ref(link);
/* Let's check if this is complete now */
r = session_send_create_reply(session, /* error= */ NULL);
if (r < 0)
goto fail;
return 1;
fail:
if (session)
session_add_to_gc_queue(session);
return r;
}
static int vl_method_release_session(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
Manager *m = ASSERT_PTR(userdata);
int r;
struct {
const char *id;
} p;
static const sd_json_dispatch_field dispatch_table[] = {
{ "Id", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, id), SD_JSON_MANDATORY },
{}
};
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
if (r != 0)
return r;
Session *session;
r = manager_varlink_get_session_by_name(m, link, p.id, &session);
if (r != 0)
return r;
Session *peer_session;
r = manager_varlink_get_session_by_peer(m, link, /* consult_display= */ false, &peer_session);
if (r != 0)
return r;
if (session != peer_session)
return sd_varlink_error(link, SD_VARLINK_ERROR_PERMISSION_DENIED, /* parameters= */ NULL);
r = session_release(session);
if (r < 0)
return r;
return sd_varlink_replyb(link, SD_JSON_BUILD_EMPTY_OBJECT);
}
int manager_varlink_init(Manager *m) {
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
int r;
assert(m);
if (m->varlink_server)
return 0;
r = sd_varlink_server_new(
&s,
SD_VARLINK_SERVER_ACCOUNT_UID|
SD_VARLINK_SERVER_INHERIT_USERDATA|
SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT);
if (r < 0)
return log_error_errno(r, "Failed to allocate varlink server object: %m");
sd_varlink_server_set_userdata(s, m);
r = sd_varlink_server_add_interface(s, &vl_interface_io_systemd_Login);
if (r < 0)
return log_error_errno(r, "Failed to add Login interface to varlink server: %m");
r = sd_varlink_server_bind_method_many(
s,
"io.systemd.Login.CreateSession", vl_method_create_session,
"io.systemd.Login.ReleaseSession", vl_method_release_session);
if (r < 0)
return log_error_errno(r, "Failed to register varlink methods: %m");
r = sd_varlink_server_listen_address(s, "/run/systemd/io.systemd.Login", 0666);
if (r < 0)
return log_error_errno(r, "Failed to bind to varlink socket: %m");
r = sd_varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
if (r < 0)
return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
m->varlink_server = TAKE_PTR(s);
return 0;
}
void manager_varlink_done(Manager *m) {
assert(m);
m->varlink_server = sd_varlink_server_unref(m->varlink_server);
}

View File

@ -0,0 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-bus.h"
#include "logind.h"
#include "logind-session.h"
int manager_varlink_init(Manager *m);
void manager_varlink_done(Manager *m);
int session_send_create_reply_varlink(Session *s, const sd_bus_error *error);

View File

@ -24,11 +24,12 @@
#include "fd-util.h" #include "fd-util.h"
#include "format-util.h" #include "format-util.h"
#include "fs-util.h" #include "fs-util.h"
#include "logind.h"
#include "logind-dbus.h" #include "logind-dbus.h"
#include "logind-seat-dbus.h" #include "logind-seat-dbus.h"
#include "logind-session-dbus.h" #include "logind-session-dbus.h"
#include "logind-user-dbus.h" #include "logind-user-dbus.h"
#include "logind.h" #include "logind-varlink.h"
#include "main-func.h" #include "main-func.h"
#include "mkdir-label.h" #include "mkdir-label.h"
#include "parse-util.h" #include "parse-util.h"
@ -153,6 +154,8 @@ static Manager* manager_free(Manager *m) {
hashmap_free(m->polkit_registry); hashmap_free(m->polkit_registry);
manager_varlink_done(m);
sd_bus_flush_close_unref(m->bus); sd_bus_flush_close_unref(m->bus);
sd_event_unref(m->event); sd_event_unref(m->event);
@ -1114,6 +1117,10 @@ static int manager_startup(Manager *m) {
if (r < 0) if (r < 0)
return r; return r;
r = manager_varlink_init(m);
if (r < 0)
return r;
/* Instantiate magic seat 0 */ /* Instantiate magic seat 0 */
r = manager_add_seat(m, "seat0", &m->seat0); r = manager_add_seat(m, "seat0", &m->seat0);
if (r < 0) if (r < 0)

View File

@ -7,6 +7,7 @@
#include "sd-bus.h" #include "sd-bus.h"
#include "sd-device.h" #include "sd-device.h"
#include "sd-event.h" #include "sd-event.h"
#include "sd-varlink.h"
#include "calendarspec.h" #include "calendarspec.h"
#include "conf-parser.h" #include "conf-parser.h"
@ -145,6 +146,8 @@ struct Manager {
struct stat efi_loader_entry_one_shot_stat; struct stat efi_loader_entry_one_shot_stat;
CalendarSpec *maintenance_time; CalendarSpec *maintenance_time;
sd_varlink_server *varlink_server;
}; };
void manager_reset_config(Manager *m); void manager_reset_config(Manager *m);

View File

@ -26,6 +26,7 @@ liblogind_core_sources = files(
'logind-session.c', 'logind-session.c',
'logind-user-dbus.c', 'logind-user-dbus.c',
'logind-user.c', 'logind-user.c',
'logind-varlink.c',
'logind-wall.c', 'logind-wall.c',
) )

View File

@ -18,6 +18,9 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include "sd-bus.h"
#include "sd-varlink.h"
#include "alloc-util.h" #include "alloc-util.h"
#include "audit-util.h" #include "audit-util.h"
#include "bus-common-errors.h" #include "bus-common-errors.h"
@ -35,6 +38,7 @@
#include "format-util.h" #include "format-util.h"
#include "fs-util.h" #include "fs-util.h"
#include "hostname-util.h" #include "hostname-util.h"
#include "json-util.h"
#include "locale-util.h" #include "locale-util.h"
#include "login-util.h" #include "login-util.h"
#include "macro.h" #include "macro.h"
@ -390,116 +394,108 @@ static int export_legacy_dbus_address(
} }
static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) { static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
uint64_t val;
int r; int r;
assert(handle);
assert(m);
if (isempty(limit)) if (isempty(limit))
return PAM_SUCCESS; return 0;
if (streq(limit, "infinity")) { if (streq(limit, "infinity"))
r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", UINT64_MAX); return sd_bus_message_append(m, "(sv)", "MemoryMax", "t", UINT64_MAX);
if (r < 0)
return pam_bus_log_create_error(handle, r);
return PAM_SUCCESS;
}
r = parse_permyriad(limit); r = parse_permyriad(limit);
if (r >= 0) { if (r < 0) {
r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", UINT32_SCALE_FROM_PERMYRIAD(r)); uint64_t val;
if (r < 0)
return pam_bus_log_create_error(handle, r);
return PAM_SUCCESS;
}
r = parse_size(limit, 1024, &val); r = parse_size(limit, 1024, &val);
if (r >= 0) { if (r < 0) {
r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", val);
if (r < 0)
return pam_bus_log_create_error(handle, r);
return PAM_SUCCESS;
}
pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.memory_max, ignoring: %s", limit); pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.memory_max, ignoring: %s", limit);
return PAM_SUCCESS; return PAM_SUCCESS;
}
return sd_bus_message_append(m, "(sv)", "MemoryMax", "t", val);
}
return sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", UINT32_SCALE_FROM_PERMYRIAD(r));
} }
static int append_session_runtime_max_sec(pam_handle_t *handle, sd_bus_message *m, const char *limit) { static int append_session_runtime_max_sec(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
usec_t val;
int r; int r;
assert(handle);
assert(m);
/* No need to parse "infinity" here, it will be set by default later in scope_init() */ /* No need to parse "infinity" here, it will be set by default later in scope_init() */
if (isempty(limit) || streq(limit, "infinity")) if (isempty(limit) || streq(limit, "infinity"))
return PAM_SUCCESS; return 0;
usec_t val;
r = parse_sec(limit, &val); r = parse_sec(limit, &val);
if (r >= 0) { if (r < 0) {
r = sd_bus_message_append(m, "(sv)", "RuntimeMaxUSec", "t", (uint64_t) val);
if (r < 0)
return pam_bus_log_create_error(handle, r);
} else
pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.runtime_max_sec: %s, ignoring.", limit); pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.runtime_max_sec: %s, ignoring.", limit);
return 0;
}
return PAM_SUCCESS; return sd_bus_message_append(m, "(sv)", "RuntimeMaxUSec", "t", (uint64_t) val);
} }
static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) { static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
uint64_t val;
int r; int r;
assert(handle);
assert(m);
/* No need to parse "infinity" here, it will be set unconditionally later in manager_start_scope() */ /* No need to parse "infinity" here, it will be set unconditionally later in manager_start_scope() */
if (isempty(limit) || streq(limit, "infinity")) if (isempty(limit) || streq(limit, "infinity"))
return PAM_SUCCESS; return 0;
uint64_t val;
r = safe_atou64(limit, &val); r = safe_atou64(limit, &val);
if (r >= 0) { if (r < 0) {
r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", val);
if (r < 0)
return pam_bus_log_create_error(handle, r);
} else
pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.tasks_max, ignoring: %s", limit); pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.tasks_max, ignoring: %s", limit);
return 0;
}
return PAM_SUCCESS; return sd_bus_message_append(m, "(sv)", "TasksMax", "t", val);
} }
static int append_session_cpu_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit) { static int append_session_cpu_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
uint64_t val;
int r; int r;
if (isempty(limit)) assert(handle);
return PAM_SUCCESS; assert(m);
if (isempty(limit))
return 0;
uint64_t val;
r = cg_cpu_weight_parse(limit, &val); r = cg_cpu_weight_parse(limit, &val);
if (r < 0) if (r < 0) {
pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight, ignoring: %s", limit); pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.cpu_weight, ignoring: %s", limit);
else { return 0;
r = sd_bus_message_append(m, "(sv)", "CPUWeight", "t", val);
if (r < 0)
return pam_bus_log_create_error(handle, r);
} }
return PAM_SUCCESS; return sd_bus_message_append(m, "(sv)", "CPUWeight", "t", val);
} }
static int append_session_io_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit) { static int append_session_io_weight(pam_handle_t *handle, sd_bus_message *m, const char *limit) {
uint64_t val;
int r; int r;
if (isempty(limit)) assert(handle);
return PAM_SUCCESS; assert(m);
if (isempty(limit))
return 0;
uint64_t val;
r = cg_weight_parse(limit, &val); r = cg_weight_parse(limit, &val);
if (r < 0) if (r < 0) {
pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight, ignoring: %s", limit); pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.io_weight, ignoring: %s", limit);
else { return 0;
r = sd_bus_message_append(m, "(sv)", "IOWeight", "t", val);
if (r < 0)
return pam_bus_log_create_error(handle, r);
} }
return PAM_SUCCESS; return sd_bus_message_append(m, "(sv)", "IOWeight", "t", val);
} }
static const char* getenv_harder(pam_handle_t *handle, const char *key, const char *fallback) { static const char* getenv_harder(pam_handle_t *handle, const char *key, const char *fallback) {
@ -549,6 +545,26 @@ static bool getenv_harder_bool(pam_handle_t *handle, const char *key, bool fallb
return r; return r;
} }
static uint32_t getenv_harder_uint32(pam_handle_t *handle, const char *key, uint32_t fallback) {
int r;
assert(handle);
assert(key);
const char *v = getenv_harder(handle, key, NULL);
if (isempty(v))
return fallback;
uint32_t u;
r = safe_atou32(v, &u);
if (r < 0) {
pam_syslog(handle, LOG_ERR, "Unsigned integer environment variable value of '%s' is not valid: %s", key, v);
return fallback;
}
return u;
}
static int update_environment(pam_handle_t *handle, const char *key, const char *value) { static int update_environment(pam_handle_t *handle, const char *key, const char *value) {
int r; int r;
@ -826,17 +842,15 @@ static uint64_t pick_default_capability_ambient_set(
} }
typedef struct SessionContext { typedef struct SessionContext {
const uid_t uid;
const pid_t pid;
const char *service; const char *service;
const char *type; const char *type;
const char *class; const char *class;
const char *desktop; const char *desktop;
const char *seat; const char *seat;
const uint32_t vtnr; uint32_t vtnr;
const char *tty; const char *tty;
const char *display; const char *display;
const bool remote; bool remote;
const char *remote_user; const char *remote_user;
const char *remote_host; const char *remote_host;
const char *memory_max; const char *memory_max;
@ -844,11 +858,13 @@ typedef struct SessionContext {
const char *cpu_weight; const char *cpu_weight;
const char *io_weight; const char *io_weight;
const char *runtime_max_sec; const char *runtime_max_sec;
bool incomplete;
} SessionContext; } SessionContext;
static int create_session_message( static int create_session_message(
sd_bus *bus, sd_bus *bus,
pam_handle_t *handle, pam_handle_t *handle,
UserRecord *ur,
const SessionContext *context, const SessionContext *context,
bool avoid_pidfd, bool avoid_pidfd,
sd_bus_message **ret) { sd_bus_message **ret) {
@ -859,6 +875,7 @@ static int create_session_message(
assert(bus); assert(bus);
assert(handle); assert(handle);
assert(ur);
assert(context); assert(context);
assert(ret); assert(ret);
@ -872,10 +889,11 @@ static int create_session_message(
if (r < 0) if (r < 0)
return r; return r;
r = sd_bus_message_append(m, r = sd_bus_message_append(
m,
pidfd >= 0 ? "uhsssssussbss" : "uusssssussbss", pidfd >= 0 ? "uhsssssussbss" : "uusssssussbss",
(uint32_t) context->uid, (uint32_t) ur->uid,
pidfd >= 0 ? pidfd : context->pid, pidfd >= 0 ? pidfd : 0,
context->service, context->service,
context->type, context->type,
context->class, context->class,
@ -901,23 +919,23 @@ static int create_session_message(
return r; return r;
r = append_session_memory_max(handle, m, context->memory_max); r = append_session_memory_max(handle, m, context->memory_max);
if (r != PAM_SUCCESS) if (r < 0)
return r; return r;
r = append_session_runtime_max_sec(handle, m, context->runtime_max_sec); r = append_session_runtime_max_sec(handle, m, context->runtime_max_sec);
if (r != PAM_SUCCESS) if (r < 0)
return r; return r;
r = append_session_tasks_max(handle, m, context->tasks_max); r = append_session_tasks_max(handle, m, context->tasks_max);
if (r != PAM_SUCCESS) if (r < 0)
return r; return r;
r = append_session_cpu_weight(handle, m, context->cpu_weight); r = append_session_cpu_weight(handle, m, context->cpu_weight);
if (r != PAM_SUCCESS) if (r < 0)
return r; return r;
r = append_session_io_weight(handle, m, context->io_weight); r = append_session_io_weight(handle, m, context->io_weight);
if (r != PAM_SUCCESS) if (r < 0)
return r; return r;
r = sd_bus_message_close_container(m); r = sd_bus_message_close_container(m);
@ -928,211 +946,260 @@ static int create_session_message(
return 0; return 0;
} }
_public_ PAM_EXTERN int pam_sm_open_session( static void session_context_mangle(
pam_handle_t *handle, pam_handle_t *handle,
int flags, SessionContext *c,
int argc, const char **argv) { UserRecord *ur,
bool debug) {
/* Let's release the D-Bus connection once this function exits, after all the session might live
* quite a long time, and we are not going to process the bus connection in that time, so let's
* better close before the daemon kicks us off because we are not processing anything. */
_cleanup_(pam_bus_data_disconnectp) PamBusData *d = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
const char
*id, *object_path, *runtime_path,
*service = NULL,
*tty = NULL, *display = NULL,
*remote_user = NULL, *remote_host = NULL,
*seat = NULL,
*type = NULL, *class = NULL,
*class_pam = NULL, *type_pam = NULL, *cvtnr = NULL, *desktop = NULL, *desktop_pam = NULL,
*memory_max = NULL, *tasks_max = NULL, *cpu_weight = NULL, *io_weight = NULL, *runtime_max_sec = NULL;
uint64_t default_capability_bounding_set = UINT64_MAX, default_capability_ambient_set = UINT64_MAX;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
int session_fd = -EBADF, existing, r;
bool debug = false, remote, incomplete;
uint32_t vtnr = 0;
uid_t original_uid;
assert(handle); assert(handle);
assert(c);
assert(ur);
pam_log_setup(); if (streq_ptr(c->service, "systemd-user")) {
if (parse_argv(handle,
argc, argv,
&class_pam,
&type_pam,
&desktop_pam,
&debug,
&default_capability_bounding_set,
&default_capability_ambient_set) < 0)
return PAM_SESSION_ERR;
pam_debug_syslog(handle, debug, "pam-systemd initializing");
r = acquire_user_record(handle, &ur);
if (r != PAM_SUCCESS)
return r;
/* Make most of this a NOP on non-logind systems */
if (!logind_running())
goto success;
r = pam_get_item_many(
handle,
PAM_SERVICE, &service,
PAM_XDISPLAY, &display,
PAM_TTY, &tty,
PAM_RUSER, &remote_user,
PAM_RHOST, &remote_host);
if (r != PAM_SUCCESS)
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM items: @PAMERR@");
seat = getenv_harder(handle, "XDG_SEAT", NULL);
cvtnr = getenv_harder(handle, "XDG_VTNR", NULL);
type = getenv_harder(handle, "XDG_SESSION_TYPE", type_pam);
class = getenv_harder(handle, "XDG_SESSION_CLASS", class_pam);
desktop = getenv_harder(handle, "XDG_SESSION_DESKTOP", desktop_pam);
incomplete = getenv_harder_bool(handle, "XDG_SESSION_INCOMPLETE", false);
if (streq_ptr(service, "systemd-user")) {
/* If we detect that we are running in the "systemd-user" PAM stack, then let's patch the class to /* If we detect that we are running in the "systemd-user" PAM stack, then let's patch the class to
* 'manager' if not set, simply for robustness reasons. */ * 'manager' if not set, simply for robustness reasons. */
type = "unspecified"; c->type = "unspecified";
class = IN_SET(user_record_disposition(ur), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC) ? c->class = IN_SET(user_record_disposition(ur), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC) ?
"manager-early" : "manager"; "manager-early" : "manager";
tty = NULL; c->tty = NULL;
} else if (tty && strchr(tty, ':')) { } else if (c->tty && strchr(c->tty, ':')) {
/* A tty with a colon is usually an X11 display, placed there to show up in utmp. We rearrange things /* A tty with a colon is usually an X11 display, placed there to show up in utmp. We rearrange things
* and don't pretend that an X display was a tty. */ * and don't pretend that an X display was a tty. */
if (isempty(display)) if (isempty(c->display))
display = tty; c->display = c->tty;
tty = NULL; c->tty = NULL;
} else if (streq_ptr(tty, "cron")) { } else if (streq_ptr(c->tty, "cron")) {
/* cron is setting PAM_TTY to "cron" for some reason (the commit carries no information why, but /* cron is setting PAM_TTY to "cron" for some reason (the commit carries no information why, but
* probably because it wants to set it to something as pam_time/pam_access/ require PAM_TTY to be set * probably because it wants to set it to something as pam_time/pam_access/ require PAM_TTY to be set
* (as they otherwise even try to update it!) but cron doesn't actually allocate a TTY for its forked * (as they otherwise even try to update it!) but cron doesn't actually allocate a TTY for its forked
* off processes.) */ * off processes.) */
type = "unspecified"; c->type = "unspecified";
class = "background"; c->class = "background";
tty = NULL; c->tty = NULL;
} else if (streq_ptr(tty, "ssh")) { } else if (streq_ptr(c->tty, "ssh")) {
/* ssh has been setting PAM_TTY to "ssh" (for the same reason as cron does this, see above. For further /* ssh has been setting PAM_TTY to "ssh" (for the same reason as cron does this, see above. For further
* details look for "PAM_TTY_KLUDGE" in the openssh sources). */ * details look for "PAM_TTY_KLUDGE" in the openssh sources). */
type = "tty"; c->type = "tty";
class = "user"; c->class = "user";
tty = NULL; /* This one is particularly sad, as this means that ssh sessions — even though usually c->tty = NULL; /* This one is particularly sad, as this means that ssh sessions — even though
* associated with a pty won't be tracked by their tty in logind. This is because ssh * usually associated with a pty won't be tracked by their tty in
* does the PAM session registration early for new connections, and registers a pty only * logind. This is because ssh does the PAM session registration early for new
* much later (this is because it doesn't know yet if it needs one at all, as whether to * connections, and registers a pty only much later (this is because it doesn't
* register a pty or not is negotiated much later in the protocol). */ * know yet if it needs one at all, as whether to register a pty or not is
* negotiated much later in the protocol). */
} else if (tty) } else if (c->tty)
/* Chop off leading /dev prefix that some clients specify, but others do not. */ /* Chop off leading /dev prefix that some clients specify, but others do not. */
tty = skip_dev_prefix(tty); c->tty = skip_dev_prefix(c->tty);
/* If this fails vtnr will be 0, that's intended */ if (!isempty(c->display) && !c->vtnr) {
if (!isempty(cvtnr)) if (isempty(c->seat))
(void) safe_atou32(cvtnr, &vtnr); (void) get_seat_from_display(c->display, &c->seat, &c->vtnr);
else if (streq(c->seat, "seat0"))
if (!isempty(display) && !vtnr) { (void) get_seat_from_display(c->display, /* seat= */ NULL, &c->vtnr);
if (isempty(seat))
(void) get_seat_from_display(display, &seat, &vtnr);
else if (streq(seat, "seat0"))
(void) get_seat_from_display(display, NULL, &vtnr);
} }
if (seat && !streq(seat, "seat0") && vtnr != 0) { if (c->seat && !streq(c->seat, "seat0") && c->vtnr != 0) {
pam_debug_syslog(handle, debug, "Ignoring vtnr %"PRIu32" for %s which is not seat0", vtnr, seat); pam_debug_syslog(handle, debug, "Ignoring vtnr %"PRIu32" for %s which is not seat0", c->vtnr, c->seat);
vtnr = 0; c->vtnr = 0;
} }
if (isempty(type)) if (isempty(c->type))
type = !isempty(display) ? "x11" : c->type = !isempty(c->display) ? "x11" :
!isempty(tty) ? "tty" : "unspecified"; !isempty(c->tty) ? "tty" : "unspecified";
if (isempty(class)) if (isempty(c->class))
class = streq(type, "unspecified") ? "background" : c->class = streq(c->type, "unspecified") ? "background" :
((IN_SET(user_record_disposition(ur), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC) && ((IN_SET(user_record_disposition(ur), USER_INTRINSIC, USER_SYSTEM, USER_DYNAMIC) &&
streq(type, "tty")) ? "user-early" : "user"); streq(c->type, "tty")) ? "user-early" : "user");
if (incomplete) { if (c->incomplete) {
if (streq(class, "user")) if (streq(c->class, "user"))
class = "user-incomplete"; c->class = "user-incomplete";
else else
pam_syslog_pam_error(handle, LOG_WARNING, 0, "PAM session of class '%s' is incomplete, which is not supported, ignoring.", class); pam_syslog_pam_error(handle, LOG_WARNING, 0, "PAM session of class '%s' is incomplete, which is not supported, ignoring.", c->class);
} }
remote = !isempty(remote_host) && !is_localhost(remote_host); c->remote = !isempty(c->remote_host) && !is_localhost(c->remote_host);
}
r = pam_get_data(handle, "systemd.memory_max", (const void **)&memory_max); static bool can_use_varlink(const SessionContext *c) {
if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) /* Since PID 1 currently doesn't do Varlink right now, we cannot directly set properties for the
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM systemd.memory_max data: @PAMERR@"); * scope, for now. */
r = pam_get_data(handle, "systemd.tasks_max", (const void **)&tasks_max); return !c->memory_max &&
if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) !c->runtime_max_sec &&
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM systemd.tasks_max data: @PAMERR@"); !c->tasks_max &&
r = pam_get_data(handle, "systemd.cpu_weight", (const void **)&cpu_weight); !c->cpu_weight &&
if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) !c->io_weight;
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM systemd.cpu_weight data: @PAMERR@"); }
r = pam_get_data(handle, "systemd.io_weight", (const void **)&io_weight);
if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) static int register_session(
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM systemd.io_weight data: @PAMERR@"); pam_handle_t *handle,
r = pam_get_data(handle, "systemd.runtime_max_sec", (const void **)&runtime_max_sec); SessionContext *c,
if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) UserRecord *ur,
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM systemd.runtime_max_sec data: @PAMERR@"); bool debug,
char **ret_seat) {
int r;
assert(handle);
assert(c);
assert(ur);
assert(ret_seat);
/* Make most of this a NOP on non-logind systems */
if (!logind_running()) {
*ret_seat = NULL;
return PAM_SUCCESS;
}
pam_debug_syslog(handle, debug,
"Asking logind to create session: "
"uid="UID_FMT" pid="PID_FMT" service=%s type=%s class=%s desktop=%s seat=%s vtnr=%"PRIu32" tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
ur->uid, getpid_cached(),
strempty(c->service),
c->type, c->class, strempty(c->desktop),
strempty(c->seat), c->vtnr, strempty(c->tty), strempty(c->display),
yes_no(c->remote), strempty(c->remote_user), strempty(c->remote_host));
pam_debug_syslog(handle, debug,
"Session limits: "
"memory_max=%s tasks_max=%s cpu_weight=%s io_weight=%s runtime_max_sec=%s",
strna(c->memory_max), strna(c->tasks_max), strna(c->cpu_weight), strna(c->io_weight), strna(c->runtime_max_sec));
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; /* the following variables point into this message, hence pin it for longer */
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL; /* similar */
const char *id = NULL, *object_path = NULL, *runtime_path = NULL, *real_seat = NULL;
int session_fd = -EBADF, existing = false;
uint32_t original_uid = UID_INVALID, real_vtnr = 0;
bool done = false;
if (can_use_varlink(c)) {
r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.Login");
if (r < 0)
log_debug_errno(r, "Failed to connect to logind via Varlink, falling back to D-Bus: %m");
else {
r = sd_varlink_set_allow_fd_passing_input(vl, true);
if (r < 0)
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to enable input fd passing on Varlink socket: %m");
r = sd_varlink_set_allow_fd_passing_output(vl, true);
if (r < 0)
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to enable output fd passing on Varlink socket: %m");
r = sd_varlink_set_relative_timeout(vl, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC);
if (r < 0)
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to set relative timeout on Varlink socket: %m");
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
r = pidref_set_self(&pidref);
if (r < 0)
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to acquire PID reference on ourselves: %m");
sd_json_variant *vreply = NULL;
const char *error_id = NULL;
r = sd_varlink_callbo(
vl,
"io.systemd.Login.CreateSession",
&vreply,
&error_id,
SD_JSON_BUILD_PAIR_UNSIGNED("UID", ur->uid),
JSON_BUILD_PAIR_PIDREF("PID", &pidref),
JSON_BUILD_PAIR_STRING_NON_EMPTY("Service", c->service),
SD_JSON_BUILD_PAIR("Type", JSON_BUILD_STRING_UNDERSCORIFY(c->type)),
SD_JSON_BUILD_PAIR("Class", JSON_BUILD_STRING_UNDERSCORIFY(c->class)),
JSON_BUILD_PAIR_STRING_NON_EMPTY("Desktop", c->desktop),
JSON_BUILD_PAIR_STRING_NON_EMPTY("Seat", c->seat),
SD_JSON_BUILD_PAIR_CONDITION(c->vtnr > 0, "VTNr", SD_JSON_BUILD_UNSIGNED(c->vtnr)),
JSON_BUILD_PAIR_STRING_NON_EMPTY("TTY", c->tty),
JSON_BUILD_PAIR_STRING_NON_EMPTY("Display", c->display),
SD_JSON_BUILD_PAIR_BOOLEAN("Remote", c->remote),
JSON_BUILD_PAIR_STRING_NON_EMPTY("RemoteUser", c->remote_user),
JSON_BUILD_PAIR_STRING_NON_EMPTY("RemoteHost", c->remote_host));
if (r < 0)
return pam_syslog_errno(handle, LOG_ERR, r,
"Failed to register session: %s", error_id);
if (streq_ptr(error_id, "io.systemd.Login.AlreadySessionMember")) {
/* We are already in a session, don't do anything */
pam_debug_syslog(handle, debug, "Not creating session: %s", error_id);
*ret_seat = NULL;
return PAM_SUCCESS;
}
if (error_id)
return pam_syslog_errno(handle, LOG_ERR, sd_varlink_error_to_errno(error_id, vreply),
"Failed to issue CreateSession() varlink call: %s", error_id);
struct {
const char *id;
const char *runtime_path;
unsigned session_fd_idx;
uid_t uid;
const char *seat;
unsigned vtnr;
bool existing;
} p = {
.session_fd_idx = UINT_MAX,
.uid = UID_INVALID,
};
static const sd_json_dispatch_field dispatch_table[] = {
{ "Id", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, id), SD_JSON_MANDATORY },
{ "RuntimePath", SD_JSON_VARIANT_STRING, json_dispatch_const_path, voffsetof(p, runtime_path), SD_JSON_MANDATORY },
{ "SessionFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(p, session_fd_idx), SD_JSON_MANDATORY },
{ "UID", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, voffsetof(p, uid), SD_JSON_MANDATORY },
{ "Seat", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, seat), 0 },
{ "VTNr", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, voffsetof(p, vtnr), 0 },
{}
};
r = sd_json_dispatch(vreply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
if (r < 0)
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to parse CreateSession() reply: %m");
session_fd = sd_varlink_peek_fd(vl, p.session_fd_idx);
if (session_fd < 0)
return pam_syslog_errno(handle, LOG_ERR, session_fd, "Failed to extract session fd from CreateSession() reply: %m");
id = p.id;
runtime_path = p.runtime_path;
original_uid = p.uid;
real_seat = p.seat;
real_vtnr = p.vtnr;
existing = false; /* Even on D-Bus logind only returns false these days */
done = true;
}
}
if (!done) {
/* Let's release the D-Bus connection once we are done here, after all the session might live
* quite a long time, and we are not going to process the bus connection in that time, so
* let's better close before the daemon kicks us off because we are not processing
* anything. */
_cleanup_(pam_bus_data_disconnectp) PamBusData *d = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
/* Talk to logind over the message bus */ /* Talk to logind over the message bus */
r = pam_acquire_bus_connection(handle, "pam-systemd", debug, &bus, &d); r = pam_acquire_bus_connection(handle, "pam-systemd", debug, &bus, &d);
if (r != PAM_SUCCESS) if (r != PAM_SUCCESS)
return r; return r;
pam_debug_syslog(handle, debug, _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
"Asking logind to create session: " r = create_session_message(
"uid="UID_FMT" pid="PID_FMT" service=%s type=%s class=%s desktop=%s seat=%s vtnr=%"PRIu32" tty=%s display=%s remote=%s remote_user=%s remote_host=%s", bus,
ur->uid, getpid_cached(),
strempty(service),
type, class, strempty(desktop),
strempty(seat), vtnr, strempty(tty), strempty(display),
yes_no(remote), strempty(remote_user), strempty(remote_host));
pam_debug_syslog(handle, debug,
"Session limits: "
"memory_max=%s tasks_max=%s cpu_weight=%s io_weight=%s runtime_max_sec=%s",
strna(memory_max), strna(tasks_max), strna(cpu_weight), strna(io_weight), strna(runtime_max_sec));
const SessionContext context = {
.uid = ur->uid,
.pid = 0,
.service = service,
.type = type,
.class = class,
.desktop = desktop,
.seat = seat,
.vtnr = vtnr,
.tty = tty,
.display = display,
.remote = remote,
.remote_user = remote_user,
.remote_host = remote_host,
.memory_max = memory_max,
.tasks_max = tasks_max,
.cpu_weight = cpu_weight,
.io_weight = io_weight,
.runtime_max_sec = runtime_max_sec,
};
r = create_session_message(bus,
handle, handle,
&context, ur,
c,
/* avoid_pidfd = */ false, /* avoid_pidfd = */ false,
&m); &m);
if (r < 0) if (r < 0)
return pam_bus_log_create_error(handle, r); return pam_bus_log_create_error(handle, r);
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
r = sd_bus_call(bus, m, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply); r = sd_bus_call(bus, m, LOGIN_SLOW_BUS_CALL_TIMEOUT_USEC, &error, &reply);
if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) { if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
sd_bus_error_free(&error); sd_bus_error_free(&error);
@ -1142,7 +1209,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
m = sd_bus_message_unref(m); m = sd_bus_message_unref(m);
r = create_session_message(bus, r = create_session_message(bus,
handle, handle,
&context, ur,
c,
/* avoid_pidfd = */ true, /* avoid_pidfd = */ true,
&m); &m);
if (r < 0) if (r < 0)
@ -1155,7 +1223,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
/* We are already in a session, don't do anything */ /* We are already in a session, don't do anything */
pam_debug_syslog(handle, debug, pam_debug_syslog(handle, debug,
"Not creating session: %s", bus_error_message(&error, r)); "Not creating session: %s", bus_error_message(&error, r));
goto success; *ret_seat = NULL;
return PAM_SUCCESS;
} }
pam_syslog(handle, LOG_ERR, pam_syslog(handle, LOG_ERR,
@ -1163,23 +1232,25 @@ _public_ PAM_EXTERN int pam_sm_open_session(
return PAM_SESSION_ERR; return PAM_SESSION_ERR;
} }
r = sd_bus_message_read(reply, r = sd_bus_message_read(
reply,
"soshusub", "soshusub",
&id, &id,
&object_path, &object_path,
&runtime_path, &runtime_path,
&session_fd, &session_fd,
&original_uid, &original_uid,
&seat, &real_seat,
&vtnr, &real_vtnr,
&existing); &existing);
if (r < 0) if (r < 0)
return pam_bus_log_parse_error(handle, r); return pam_bus_log_parse_error(handle, r);
}
pam_debug_syslog(handle, debug, pam_debug_syslog(handle, debug,
"Reply from logind: " "Reply from logind: "
"id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u", "id=%s object_path=%s runtime_path=%s session_fd=%d seat=%s vtnr=%u original_uid=%u",
id, object_path, runtime_path, session_fd, seat, vtnr, original_uid); id, strna(object_path), runtime_path, session_fd, real_seat, real_vtnr, original_uid);
/* Please update manager_default_environment() in core/manager.c accordingly if more session envvars /* Please update manager_default_environment() in core/manager.c accordingly if more session envvars
* shall be added. */ * shall be added. */
@ -1202,38 +1273,25 @@ _public_ PAM_EXTERN int pam_sm_open_session(
* somewhere else (for example PAM module parameters). Let's now update the environment variables, so that this * somewhere else (for example PAM module parameters). Let's now update the environment variables, so that this
* data is inherited into the session processes, and programs can rely on them to be initialized. */ * data is inherited into the session processes, and programs can rely on them to be initialized. */
r = update_environment(handle, "XDG_SESSION_TYPE", type); r = update_environment(handle, "XDG_SESSION_TYPE", c->type);
if (r != PAM_SUCCESS) if (r != PAM_SUCCESS)
return r; return r;
r = update_environment(handle, "XDG_SESSION_CLASS", class); r = update_environment(handle, "XDG_SESSION_CLASS", c->class);
if (r != PAM_SUCCESS) if (r != PAM_SUCCESS)
return r; return r;
r = update_environment(handle, "XDG_SESSION_DESKTOP", desktop); r = update_environment(handle, "XDG_SESSION_DESKTOP", c->desktop);
if (r != PAM_SUCCESS) if (r != PAM_SUCCESS)
return r; return r;
r = update_environment(handle, "XDG_SEAT", seat); r = update_environment(handle, "XDG_SEAT", real_seat);
if (r != PAM_SUCCESS) if (r != PAM_SUCCESS)
return r; return r;
static const char *const propagate[] = { if (real_vtnr > 0) {
"shell.prompt.prefix", "SHELL_PROMPT_PREFIX", char buf[DECIMAL_STR_MAX(real_vtnr)];
"shell.prompt.suffix", "SHELL_PROMPT_SUFFIX", sprintf(buf, "%u", real_vtnr);
"shell.welcome", "SHELL_WELCOME",
NULL
};
STRV_FOREACH_PAIR(k, v, propagate) {
r = propagate_credential_to_environment(handle, *k, *v);
if (r != PAM_SUCCESS)
return r;
}
if (vtnr > 0) {
char buf[DECIMAL_STR_MAX(vtnr)];
sprintf(buf, "%u", vtnr);
r = update_environment(handle, "XDG_VTNR", buf); r = update_environment(handle, "XDG_VTNR", buf);
if (r != PAM_SUCCESS) if (r != PAM_SUCCESS)
@ -1255,9 +1313,114 @@ _public_ PAM_EXTERN int pam_sm_open_session(
TAKE_FD(fd); TAKE_FD(fd);
} }
success: /* Everything worked, hence let's patch in the data we learned. Since 'real_set' points into the
* D-Bus message, let's copy it and return it as a buffer */
char *rs = NULL;
if (real_seat) {
rs = strdup(real_seat);
if (!rs)
return pam_log_oom(handle);
}
c->seat = *ret_seat = rs;
c->vtnr = real_vtnr;
return PAM_SUCCESS;
}
static int import_shell_credentials(pam_handle_t *handle) {
static const char *const propagate[] = {
"shell.prompt.prefix", "SHELL_PROMPT_PREFIX",
"shell.prompt.suffix", "SHELL_PROMPT_SUFFIX",
"shell.welcome", "SHELL_WELCOME",
NULL
};
int r;
assert(handle);
STRV_FOREACH_PAIR(k, v, propagate) {
r = propagate_credential_to_environment(handle, *k, *v);
if (r != PAM_SUCCESS)
return r;
}
return PAM_SUCCESS;
}
_public_ PAM_EXTERN int pam_sm_open_session(
pam_handle_t *handle,
int flags,
int argc, const char **argv) {
int r;
assert(handle);
pam_log_setup();
uint64_t default_capability_bounding_set = UINT64_MAX, default_capability_ambient_set = UINT64_MAX;
const char *class_pam = NULL, *type_pam = NULL, *desktop_pam = NULL;
bool debug = false;
if (parse_argv(handle,
argc, argv,
&class_pam,
&type_pam,
&desktop_pam,
&debug,
&default_capability_bounding_set,
&default_capability_ambient_set) < 0)
return PAM_SESSION_ERR;
pam_debug_syslog(handle, debug, "pam-systemd initializing");
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
r = acquire_user_record(handle, &ur);
if (r != PAM_SUCCESS)
return r;
SessionContext c = {};
r = pam_get_item_many(
handle,
PAM_SERVICE, &c.service,
PAM_XDISPLAY, &c.display,
PAM_TTY, &c.tty,
PAM_RUSER, &c.remote_user,
PAM_RHOST, &c.remote_host);
if (r != PAM_SUCCESS)
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM items: @PAMERR@");
c.seat = getenv_harder(handle, "XDG_SEAT", NULL);
c.vtnr = getenv_harder_uint32(handle, "XDG_VTNR", 0);
c.type = getenv_harder(handle, "XDG_SESSION_TYPE", type_pam);
c.class = getenv_harder(handle, "XDG_SESSION_CLASS", class_pam);
c.desktop = getenv_harder(handle, "XDG_SESSION_DESKTOP", desktop_pam);
c.incomplete = getenv_harder_bool(handle, "XDG_SESSION_INCOMPLETE", false);
r = pam_get_data_many(
handle,
"systemd.memory_max", &c.memory_max,
"systemd.tasks_max", &c.tasks_max,
"systemd.cpu_weight", &c.cpu_weight,
"systemd.io_weight", &c.io_weight,
"systemd.runtime_max_sec", &c.runtime_max_sec);
if (r != PAM_SUCCESS)
return pam_syslog_pam_error(handle, LOG_ERR, r, "Failed to get PAM data: @PAMERR@");
session_context_mangle(handle, &c, ur, debug);
_cleanup_free_ char *seat_buffer = NULL;
r = register_session(handle, &c, ur, debug, &seat_buffer);
if (r != PAM_SUCCESS)
return r;
r = import_shell_credentials(handle);
if (r != PAM_SUCCESS)
return r;
if (default_capability_ambient_set == UINT64_MAX) if (default_capability_ambient_set == UINT64_MAX)
default_capability_ambient_set = pick_default_capability_ambient_set(ur, service, seat); default_capability_ambient_set = pick_default_capability_ambient_set(ur, c.service, c.seat);
return apply_user_record_settings(handle, ur, debug, default_capability_bounding_set, default_capability_ambient_set); return apply_user_record_settings(handle, ur, debug, default_capability_bounding_set, default_capability_ambient_set);
} }
@ -1297,13 +1460,39 @@ _public_ PAM_EXTERN int pam_sm_close_session(
id = pam_getenv(handle, "XDG_SESSION_ID"); id = pam_getenv(handle, "XDG_SESSION_ID");
if (id && !existing) { if (id && !existing) {
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
bool done = false;
r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.Login");
if (r < 0)
log_debug_errno(r, "Failed to connect to logind via Varlink, falling back to D-Bus: %m");
else {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *vreply = NULL;
const char *error_id = NULL;
r = sd_varlink_callbo(
vl,
"io.systemd.Login.ReleaseSession",
/* ret_reply= */ NULL,
&error_id,
SD_JSON_BUILD_PAIR_STRING("Id", id));
if (r < 0)
return pam_syslog_errno(handle, LOG_ERR, r, "Failed to register session: %s", error_id);
if (error_id)
return pam_syslog_errno(handle, LOG_ERR, sd_varlink_error_to_errno(error_id, vreply),
"Failed to issue ReleaseSession() varlink call: %s", error_id);
done = true;
}
if (!done) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(pam_bus_data_disconnectp) PamBusData *d = NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
/* Before we go and close the FIFO we need to tell logind that this is a clean session /* Before we go and close the FIFO we need to tell logind that this is a clean session
* shutdown, so that it doesn't just go and slaughter us immediately after closing the fd */ * shutdown, so that it doesn't just go and slaughter us immediately after closing the fd */
r = pam_acquire_bus_connection(handle, "pam-systemd", debug, &bus, NULL); r = pam_acquire_bus_connection(handle, "pam-systemd", debug, &bus, &d);
if (r != PAM_SUCCESS) if (r != PAM_SUCCESS)
return r; return r;
@ -1312,6 +1501,7 @@ _public_ PAM_EXTERN int pam_sm_close_session(
return pam_syslog_pam_error(handle, LOG_ERR, PAM_SESSION_ERR, return pam_syslog_pam_error(handle, LOG_ERR, PAM_SESSION_ERR,
"Failed to release session: %s", bus_error_message(&error, r)); "Failed to release session: %s", bus_error_message(&error, r));
} }
}
/* Note that we are knowingly leaking the FIFO fd here. This way, logind can watch us die. If we /* Note that we are knowingly leaking the FIFO fd here. This way, logind can watch us die. If we
* closed it here it would not have any clue when that is completed. Given that one cannot really * closed it here it would not have any clue when that is completed. Given that one cannot really

View File

@ -182,6 +182,7 @@ shared_sources = files(
'varlink-io.systemd.Hostname.c', 'varlink-io.systemd.Hostname.c',
'varlink-io.systemd.Import.c', 'varlink-io.systemd.Import.c',
'varlink-io.systemd.Journal.c', 'varlink-io.systemd.Journal.c',
'varlink-io.systemd.Login.c',
'varlink-io.systemd.Machine.c', 'varlink-io.systemd.Machine.c',
'varlink-io.systemd.MachineImage.c', 'varlink-io.systemd.MachineImage.c',
'varlink-io.systemd.ManagedOOM.c', 'varlink-io.systemd.ManagedOOM.c',

View File

@ -253,17 +253,17 @@ int pam_get_item_many_internal(pam_handle_t *handle, ...) {
va_list ap; va_list ap;
int r; int r;
assert(handle);
va_start(ap, handle); va_start(ap, handle);
for (;;) { for (;;) {
int item_type = va_arg(ap, int); int item_type = va_arg(ap, int);
if (item_type <= 0) { if (item_type <= 0) {
r = PAM_SUCCESS; r = PAM_SUCCESS;
break; break;
} }
const void **value = ASSERT_PTR(va_arg(ap, const void **)); const void **value = ASSERT_PTR(va_arg(ap, const void **));
r = pam_get_item(handle, item_type, value); r = pam_get_item(handle, item_type, value);
if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS)) if (!IN_SET(r, PAM_BAD_ITEM, PAM_SUCCESS))
break; break;
@ -273,6 +273,30 @@ int pam_get_item_many_internal(pam_handle_t *handle, ...) {
return r; return r;
} }
int pam_get_data_many_internal(pam_handle_t *handle, ...) {
va_list ap;
int r;
assert(handle);
va_start(ap, handle);
for (;;) {
const char *data_name = va_arg(ap, const char *);
if (!data_name) {
r = PAM_SUCCESS;
break;
}
const void **value = ASSERT_PTR(va_arg(ap, const void **));
r = pam_get_data(handle, data_name, value);
if (!IN_SET(r, PAM_NO_MODULE_DATA, PAM_SUCCESS))
break;
}
va_end(ap);
return r;
}
int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, const char *fmt, ...) { int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, const char *fmt, ...) {
va_list args; va_list args;
int r; int r;

View File

@ -44,7 +44,9 @@ int pam_get_bus_data(pam_handle_t *handle, const char *module_name, PamBusData *
void pam_cleanup_free(pam_handle_t *handle, void *data, int error_status); void pam_cleanup_free(pam_handle_t *handle, void *data, int error_status);
int pam_get_item_many_internal(pam_handle_t *handle, ...); int pam_get_item_many_internal(pam_handle_t *handle, ...);
#define pam_get_item_many(handle, ...) pam_get_item_many_internal(handle, __VA_ARGS__, -1) #define pam_get_item_many(handle, ...) pam_get_item_many_internal(handle, __VA_ARGS__, -1)
int pam_get_data_many_internal(pam_handle_t *handle, ...);
#define pam_get_data_many(handle, ...) pam_get_data_many_internal(handle, __VA_ARGS__, NULL)
int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, const char *fmt, ...) _printf_(4,5); int pam_prompt_graceful(pam_handle_t *handle, int style, char **ret_response, const char *fmt, ...) _printf_(4,5);

View File

@ -0,0 +1,115 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "bus-polkit.h"
#include "varlink-idl-common.h"
#include "varlink-io.systemd.Login.h"
static SD_VARLINK_DEFINE_ENUM_TYPE(
SessionType,
SD_VARLINK_DEFINE_ENUM_VALUE(unspecified),
SD_VARLINK_DEFINE_ENUM_VALUE(tty),
SD_VARLINK_DEFINE_ENUM_VALUE(x11),
SD_VARLINK_DEFINE_ENUM_VALUE(wayland),
SD_VARLINK_DEFINE_ENUM_VALUE(mir),
SD_VARLINK_DEFINE_ENUM_VALUE(web));
static SD_VARLINK_DEFINE_ENUM_TYPE(
SessionClass,
SD_VARLINK_FIELD_COMMENT("Regular user sessions"),
SD_VARLINK_DEFINE_ENUM_VALUE(user),
SD_VARLINK_FIELD_COMMENT("Session of the root user that shall be open for login from earliest moment on, and not be delayed for /run/nologin"),
SD_VARLINK_DEFINE_ENUM_VALUE(user_early),
SD_VARLINK_FIELD_COMMENT("Regular user session whose home directory is not available right now, but will be later, at which point the session class can be upgraded to 'user'"),
SD_VARLINK_DEFINE_ENUM_VALUE(user_incomplete),
SD_VARLINK_FIELD_COMMENT("Display manager greeter screen used for login"),
SD_VARLINK_DEFINE_ENUM_VALUE(greeter),
SD_VARLINK_FIELD_COMMENT("Similar, but a a lock screen"),
SD_VARLINK_DEFINE_ENUM_VALUE(lock_screen),
SD_VARLINK_FIELD_COMMENT("Background session (that has no TTY, VT, Seat)"),
SD_VARLINK_DEFINE_ENUM_VALUE(background),
SD_VARLINK_FIELD_COMMENT("Similar, but for which no service manager is invoked"),
SD_VARLINK_DEFINE_ENUM_VALUE(background_light),
SD_VARLINK_FIELD_COMMENT("The special session of the service manager"),
SD_VARLINK_DEFINE_ENUM_VALUE(manager),
SD_VARLINK_FIELD_COMMENT("The special session of the service manager for the root user"),
SD_VARLINK_DEFINE_ENUM_VALUE(manager_early));
static SD_VARLINK_DEFINE_METHOD(
CreateSession,
SD_VARLINK_FIELD_COMMENT("Numeric UNIX UID of the user this session shall be owned by"),
SD_VARLINK_DEFINE_INPUT(UID, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("Process that shall become the leader of the session. If null defaults to the IPC client."),
SD_VARLINK_DEFINE_INPUT_BY_TYPE(PID, ProcessId, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("PAM service name of the program requesting the session"),
SD_VARLINK_DEFINE_INPUT(Service, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The type of the session"),
SD_VARLINK_DEFINE_INPUT_BY_TYPE(Type, SessionType, 0),
SD_VARLINK_FIELD_COMMENT("The class of the session"),
SD_VARLINK_DEFINE_INPUT_BY_TYPE(Class, SessionClass, 0),
SD_VARLINK_FIELD_COMMENT("An identifier for the chosen desktop"),
SD_VARLINK_DEFINE_INPUT(Desktop, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The name of the seat to assign this session to"),
SD_VARLINK_DEFINE_INPUT(Seat, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The virtual terminal number to assign this session to"),
SD_VARLINK_DEFINE_INPUT(VTNr, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The TTY device to assign this session to, if applicable"),
SD_VARLINK_DEFINE_INPUT(TTY, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The X11 display for this session"),
SD_VARLINK_DEFINE_INPUT(Display, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("If true this is a remote session"),
SD_VARLINK_DEFINE_INPUT(Remote, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("User name on the remote site, if known"),
SD_VARLINK_DEFINE_INPUT(RemoteUser, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("Host name of the remote host"),
SD_VARLINK_DEFINE_INPUT(RemoteHost, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The identifier string of the session of the user."),
SD_VARLINK_DEFINE_OUTPUT(Id, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("The runtime path ($XDG_RUNTIME_DIR) of the user."),
SD_VARLINK_DEFINE_OUTPUT(RuntimePath, SD_VARLINK_STRING, 0),
SD_VARLINK_FIELD_COMMENT("Index into the file descriptor table of this reply with the session tracking fd for this session."),
SD_VARLINK_DEFINE_OUTPUT(SessionFileDescriptor, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("The original UID of this session."),
SD_VARLINK_DEFINE_OUTPUT(UID, SD_VARLINK_INT, 0),
SD_VARLINK_FIELD_COMMENT("The seat this session has been assigned to"),
SD_VARLINK_DEFINE_OUTPUT(Seat, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
SD_VARLINK_FIELD_COMMENT("The virtual terminal number the session has been assigned to"),
SD_VARLINK_DEFINE_OUTPUT(VTNr, SD_VARLINK_INT, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
ReleaseSession,
SD_VARLINK_FIELD_COMMENT("The identifier string of the session to release. If unspecified or 'self', will return the callers session."),
SD_VARLINK_DEFINE_INPUT(Id, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_ERROR(NoSuchSession);
static SD_VARLINK_DEFINE_ERROR(NoSuchSeat);
static SD_VARLINK_DEFINE_ERROR(AlreadySessionMember);
static SD_VARLINK_DEFINE_ERROR(VirtualTerminalAlreadyTaken);
static SD_VARLINK_DEFINE_ERROR(TooManySessions);
static SD_VARLINK_DEFINE_ERROR(UnitAllocationFailed);
SD_VARLINK_DEFINE_INTERFACE(
io_systemd_Login,
"io.systemd.Login",
SD_VARLINK_INTERFACE_COMMENT("APIs for managing login sessions."),
SD_VARLINK_SYMBOL_COMMENT("Process identifier"),
&vl_type_ProcessId,
SD_VARLINK_SYMBOL_COMMENT("Various types of sessions"),
&vl_type_SessionType,
SD_VARLINK_SYMBOL_COMMENT("Various classes of sessions"),
&vl_type_SessionClass,
SD_VARLINK_SYMBOL_COMMENT("Allocates a new session."),
&vl_method_CreateSession,
SD_VARLINK_SYMBOL_COMMENT("Releases an existing session. Currently, will be refuses unless originating from the session to release itself."),
&vl_method_ReleaseSession,
SD_VARLINK_SYMBOL_COMMENT("No session by this name found"),
&vl_error_NoSuchSession,
SD_VARLINK_SYMBOL_COMMENT("No seat by this name found"),
&vl_error_NoSuchSeat,
SD_VARLINK_SYMBOL_COMMENT("Process already member of a session"),
&vl_error_AlreadySessionMember,
SD_VARLINK_SYMBOL_COMMENT("The specified virtual terminal (VT) is already taken by another session"),
&vl_error_VirtualTerminalAlreadyTaken,
SD_VARLINK_SYMBOL_COMMENT("Maximum number of sessions reached"),
&vl_error_TooManySessions,
SD_VARLINK_SYMBOL_COMMENT("Failed to allocate a unit for the session"),
&vl_error_UnitAllocationFailed);

View File

@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-varlink-idl.h"
extern const sd_varlink_interface vl_interface_io_systemd_Login;

View File

@ -70,6 +70,8 @@ __extension__ typedef enum _SD_ENUM_TYPE_S64(sd_varlink_server_flags_t) {
SD_VARLINK_SERVER_ACCOUNT_UID = 1 << 2, /* Do per user accounting */ SD_VARLINK_SERVER_ACCOUNT_UID = 1 << 2, /* Do per user accounting */
SD_VARLINK_SERVER_INHERIT_USERDATA = 1 << 3, /* Initialize Varlink connection userdata from sd_varlink_server userdata */ SD_VARLINK_SERVER_INHERIT_USERDATA = 1 << 3, /* Initialize Varlink connection userdata from sd_varlink_server userdata */
SD_VARLINK_SERVER_INPUT_SENSITIVE = 1 << 4, /* Automatically mark all connection input as sensitive */ SD_VARLINK_SERVER_INPUT_SENSITIVE = 1 << 4, /* Automatically mark all connection input as sensitive */
SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT = 1 << 5, /* Allow receiving fds over all connections */
SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT = 1 << 6, /* Allow sending fds over all connections */
_SD_ENUM_FORCE_S64(SD_VARLINK_SERVER) _SD_ENUM_FORCE_S64(SD_VARLINK_SERVER)
} sd_varlink_server_flags_t; } sd_varlink_server_flags_t;

View File

@ -212,8 +212,8 @@ TEST(proc) {
cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid.pid, &path); cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid.pid, &path);
cg_pid_get_path_shifted(pid.pid, NULL, &path_shifted); cg_pid_get_path_shifted(pid.pid, NULL, &path_shifted);
cg_pid_get_owner_uid(pid.pid, &uid); cg_pidref_get_owner_uid(&pid, &uid);
cg_pid_get_session(pid.pid, &session); cg_pidref_get_session(&pid, &session);
cg_pid_get_unit(pid.pid, &unit); cg_pid_get_unit(pid.pid, &unit);
cg_pid_get_user_unit(pid.pid, &user_unit); cg_pid_get_user_unit(pid.pid, &user_unit);
cg_pid_get_machine_name(pid.pid, &machine); cg_pid_get_machine_name(pid.pid, &machine);

View File

@ -213,9 +213,23 @@ TEST(idmapping_supported) {
assert_se(is_idmapping_supported("/etc") >= 0); assert_se(is_idmapping_supported("/etc") >= 0);
} }
TEST(namespace_is_init) {
int r;
for (NamespaceType t = 0; t < _NAMESPACE_TYPE_MAX; t++) {
r = namespace_is_init(t);
if (r == -EBADR)
log_info_errno(r, "In root namespace of type '%s': don't know", namespace_info[t].proc_name);
else {
ASSERT_OK(r);
log_info("In root namespace of type '%s': %s", namespace_info[t].proc_name, yes_no(r));
}
}
}
static int intro(void) { static int intro(void) {
if (!have_namespaces()) if (!have_namespaces())
return log_tests_skipped("Don't have namespace support"); return log_tests_skipped("Don't have namespace support or lacking privileges");
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -14,6 +14,7 @@
#include "varlink-io.systemd.Credentials.h" #include "varlink-io.systemd.Credentials.h"
#include "varlink-io.systemd.Import.h" #include "varlink-io.systemd.Import.h"
#include "varlink-io.systemd.Journal.h" #include "varlink-io.systemd.Journal.h"
#include "varlink-io.systemd.Login.h"
#include "varlink-io.systemd.Machine.h" #include "varlink-io.systemd.Machine.h"
#include "varlink-io.systemd.MachineImage.h" #include "varlink-io.systemd.MachineImage.h"
#include "varlink-io.systemd.ManagedOOM.h" #include "varlink-io.systemd.ManagedOOM.h"
@ -193,6 +194,8 @@ TEST(parse_format) {
print_separator(); print_separator();
test_parse_format_one(&vl_interface_io_systemd_MachineImage); test_parse_format_one(&vl_interface_io_systemd_MachineImage);
print_separator(); print_separator();
test_parse_format_one(&vl_interface_io_systemd_Login);
print_separator();
test_parse_format_one(&vl_interface_xyz_test); test_parse_format_one(&vl_interface_xyz_test);
} }

View File

@ -52,14 +52,14 @@ directory (`OutputDirectory=`) to point to the other directory using `mkosi.loca
After the image has been built, the integration tests can be run with: After the image has been built, the integration tests can be run with:
```shell ```shell
$ SYSTEMD_INTEGRATION_TESTS=1 meson test -C build --no-rebuild --suite integration-tests --num-processes "$(($(nproc) / 4))" $ env SYSTEMD_INTEGRATION_TESTS=1 meson test -C build --no-rebuild --suite integration-tests --num-processes "$(($(nproc) / 4))"
``` ```
As usual, specific tests can be run in meson by appending the name of the test As usual, specific tests can be run in meson by appending the name of the test
which is usually the name of the directory e.g. which is usually the name of the directory e.g.
```shell ```shell
$ SYSTEMD_INTEGRATION_TESTS=1 meson test -C build --no-rebuild -v TEST-01-BASIC $ env SYSTEMD_INTEGRATION_TESTS=1 meson test -C build --no-rebuild -v TEST-01-BASIC
``` ```
See `meson introspect build --tests` for a list of tests. See `meson introspect build --tests` for a list of tests.
@ -69,7 +69,7 @@ To interactively debug a failing integration test, the `--interactive` option
newer: newer:
```shell ```shell
$ SYSTEMD_INTEGRATION_TESTS=1 meson test -C build --no-rebuild -i TEST-01-BASIC $ env SYSTEMD_INTEGRATION_TESTS=1 meson test -C build --no-rebuild -i TEST-01-BASIC
``` ```
Due to limitations in meson, the integration tests do not yet depend on the Due to limitations in meson, the integration tests do not yet depend on the
@ -78,7 +78,7 @@ running the integration tests. To rebuild the image and rerun a test, the
following command can be used: following command can be used:
```shell ```shell
$ meson compile -C build mkosi && SYSTEMD_INTEGRATION_TESTS=1 meson test -C build --no-rebuild -v TEST-01-BASIC $ meson compile -C build mkosi && env SYSTEMD_INTEGRATION_TESTS=1 meson test -C build --no-rebuild -v TEST-01-BASIC
``` ```
The integration tests use the same mkosi configuration that's used when you run The integration tests use the same mkosi configuration that's used when you run
@ -92,7 +92,7 @@ To iterate on an integration test, let's first get a shell in the integration te
the following: the following:
```shell ```shell
$ meson compile -C build mkosi && SYSTEMD_INTEGRATION_TESTS=1 TEST_SHELL=1 meson test -C build --no-rebuild -i TEST-01-BASIC $ meson compile -C build mkosi && env SYSTEMD_INTEGRATION_TESTS=1 TEST_SHELL=1 meson test -C build --no-rebuild -i TEST-01-BASIC
``` ```
This will get us a shell in the integration test environment after booting the machine without running the This will get us a shell in the integration test environment after booting the machine without running the

View File

@ -3,6 +3,7 @@
integration_tests += [ integration_tests += [
integration_test_template + { integration_test_template + {
'name' : fs.name(meson.current_source_dir()), 'name' : fs.name(meson.current_source_dir()),
'coredump-exclude-regex' : '/(bash|python3.[0-9]+|systemd-executor)$',
'cmdline' : integration_test_template['cmdline'] + [ 'cmdline' : integration_test_template['cmdline'] + [
''' '''

View File

@ -4,6 +4,7 @@ integration_tests += [
integration_test_template + { integration_test_template + {
'name' : fs.name(meson.current_source_dir()), 'name' : fs.name(meson.current_source_dir()),
'unit' : files('TEST-16-EXTEND-TIMEOUT.service'), 'unit' : files('TEST-16-EXTEND-TIMEOUT.service'),
'coredump-exclude-regex' : '/(bash|sleep)$',
}, },
] ]

View File

@ -4,5 +4,6 @@ integration_tests += [
integration_test_template + { integration_test_template + {
'name' : fs.name(meson.current_source_dir()), 'name' : fs.name(meson.current_source_dir()),
'vm' : true, 'vm' : true,
'coredump-exclude-regex' : '/(sleep|udevadm)$',
}, },
] ]

View File

@ -3,5 +3,6 @@
integration_tests += [ integration_tests += [
integration_test_template + { integration_test_template + {
'name' : fs.name(meson.current_source_dir()), 'name' : fs.name(meson.current_source_dir()),
'coredump-exclude-regex' : '/(sleep|bash|systemd-notify)$',
}, },
] ]

View File

@ -4,5 +4,7 @@ integration_tests += [
integration_test_template + { integration_test_template + {
'name' : fs.name(meson.current_source_dir()), 'name' : fs.name(meson.current_source_dir()),
'priority' : 10, 'priority' : 10,
# TODO: Remove when https://github.com/systemd/systemd/issues/35335 is fixed.
'coredump-exclude-regex' : '/systemd-localed',
}, },
] ]

View File

@ -5,6 +5,7 @@ integration_tests += [
'name' : fs.name(meson.current_source_dir()), 'name' : fs.name(meson.current_source_dir()),
'storage': 'persistent', 'storage': 'persistent',
'vm' : true, 'vm' : true,
'coredump-exclude-regex' : '/(test-usr-dump|test-dump|bash)$',
}, },
] ]

View File

@ -6,6 +6,7 @@
import argparse import argparse
import json import json
import os import os
import re
import shlex import shlex
import subprocess import subprocess
import sys import sys
@ -32,6 +33,59 @@ ExecStart=false
""" """
def process_coredumps(args: argparse.Namespace, journal_file: Path) -> bool:
# Collect executable paths of all coredumps and filter out the expected ones.
if args.coredump_exclude_regex:
exclude_regex = re.compile(args.coredump_exclude_regex)
else:
exclude_regex = None
result = subprocess.run(
[
args.mkosi,
'--directory', os.fspath(args.meson_source_dir),
'--extra-search-path', os.fspath(args.meson_build_dir),
'sandbox',
'coredumpctl',
'--file', journal_file,
'--json=short',
],
stdout=subprocess.PIPE,
text=True,
) # fmt: skip
# coredumpctl returns a non-zero exit status if there are no coredumps.
if result.returncode != 0:
return False
coredumps = json.loads(result.stdout)
coredumps = [
coredump for coredump in coredumps if not exclude_regex or not exclude_regex.search(coredump['exe'])
]
if not coredumps:
return False
subprocess.run(
[
args.mkosi,
'--directory', os.fspath(args.meson_source_dir),
'--extra-search-path', os.fspath(args.meson_build_dir),
'sandbox',
'coredumpctl',
'--file', journal_file,
'--no-pager',
'info',
*(coredump['exe'] for coredump in coredumps),
],
check=True,
) # fmt: skip
return True
def main() -> None: def main() -> None:
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--mkosi', required=True) parser.add_argument('--mkosi', required=True)
@ -44,6 +98,7 @@ def main() -> None:
parser.add_argument('--slow', action=argparse.BooleanOptionalAction) parser.add_argument('--slow', action=argparse.BooleanOptionalAction)
parser.add_argument('--vm', action=argparse.BooleanOptionalAction) parser.add_argument('--vm', action=argparse.BooleanOptionalAction)
parser.add_argument('--exit-code', required=True, type=int) parser.add_argument('--exit-code', required=True, type=int)
parser.add_argument('--coredump-exclude-regex', required=True)
parser.add_argument('mkosi_args', nargs='*') parser.add_argument('mkosi_args', nargs='*')
args = parser.parse_args() args = parser.parse_args()
@ -114,7 +169,9 @@ def main() -> None:
""" """
) )
journal_file = None journal_file = (args.meson_build_dir / (f'test/journal/{name}.journal')).absolute()
journal_file.unlink(missing_ok=True)
if not sys.stderr.isatty(): if not sys.stderr.isatty():
dropin += textwrap.dedent( dropin += textwrap.dedent(
""" """
@ -122,9 +179,6 @@ def main() -> None:
FailureAction=exit FailureAction=exit
""" """
) )
journal_file = (args.meson_build_dir / (f'test/journal/{name}.journal')).absolute()
journal_file.unlink(missing_ok=True)
elif not shell: elif not shell:
dropin += textwrap.dedent( dropin += textwrap.dedent(
""" """
@ -194,15 +248,16 @@ def main() -> None:
) )
exit(77) exit(77)
if journal_file and ( coredumps = process_coredumps(args, journal_file)
keep_journal == '0' or (result.returncode in (args.exit_code, 77) and keep_journal == 'fail')
if keep_journal == '0' or (
keep_journal == 'fail' and result.returncode in (args.exit_code, 77) and not coredumps
): ):
journal_file.unlink(missing_ok=True) journal_file.unlink(missing_ok=True)
if shell or result.returncode in (args.exit_code, 77): if shell or (result.returncode in (args.exit_code, 77) and not coredumps):
exit(0 if shell or result.returncode == args.exit_code else 77) exit(0 if shell or result.returncode == args.exit_code else 77)
if journal_file:
ops = [] ops = []
if os.getenv('GITHUB_ACTIONS'): if os.getenv('GITHUB_ACTIONS'):
@ -228,10 +283,7 @@ def main() -> None:
ops += [f'journalctl --file {journal_file} --no-hostname -o short-monotonic -u {args.unit} -p info'] ops += [f'journalctl --file {journal_file} --no-hostname -o short-monotonic -u {args.unit} -p info']
print( print("Test failed, relevant logs can be viewed with: \n\n" f"{(' && '.join(ops))}\n", file=sys.stderr)
"Test failed, relevant logs can be viewed with: \n\n" f"{(' && '.join(ops))}\n",
file=sys.stderr,
)
# 0 also means we failed so translate that to a non-zero exit code to mark the test as failed. # 0 also means we failed so translate that to a non-zero exit code to mark the test as failed.
exit(result.returncode or 1) exit(result.returncode or 1)

View File

@ -297,6 +297,7 @@ integration_test_template = {
'qemu-args' : [], 'qemu-args' : [],
'exit-code' : 123, 'exit-code' : 123,
'vm' : false, 'vm' : false,
'coredump-exclude-regex' : '',
} }
testdata_subdirs = [ testdata_subdirs = [
'auxv', 'auxv',
@ -391,6 +392,7 @@ foreach integration_test : integration_tests
'--storage', integration_test['storage'], '--storage', integration_test['storage'],
'--firmware', integration_test['firmware'], '--firmware', integration_test['firmware'],
'--exit-code', integration_test['exit-code'].to_string(), '--exit-code', integration_test['exit-code'].to_string(),
'--coredump-exclude-regex', integration_test['coredump-exclude-regex'],
] ]
if 'unit' in integration_test if 'unit' in integration_test

View File

@ -248,6 +248,7 @@ Bridge=mybridge
[Match] [Match]
Name=mybridge Name=mybridge
[Network] [Network]
IPv6AcceptRA=no
DNS=192.168.250.1 DNS=192.168.250.1
Address=192.168.250.33/24 Address=192.168.250.33/24
Gateway=192.168.250.1 Gateway=192.168.250.1
@ -540,6 +541,7 @@ MACAddress=12:34:56:78:9a:bc
[Match] [Match]
Name=dummy0 Name=dummy0
[Network] [Network]
IPv6AcceptRA=no
Address=192.168.42.100/24 Address=192.168.42.100/24
DNS=192.168.42.1 DNS=192.168.42.1
Domains= ~company Domains= ~company
@ -573,6 +575,7 @@ MACAddress=12:34:56:78:9a:bc
self.write_network('50-myvpn.network', '''[Match] self.write_network('50-myvpn.network', '''[Match]
Name=dummy0 Name=dummy0
[Network] [Network]
IPv6AcceptRA=no
Address=192.168.42.100/24 Address=192.168.42.100/24
DNS=192.168.42.1 DNS=192.168.42.1
Domains= ~company ~. Domains= ~company ~.
@ -927,6 +930,7 @@ cat <<EOF >/run/systemd/network/50-test.network
Name={ifr} Name={ifr}
[Network] [Network]
IPv6AcceptRA=no
Address=192.168.5.1/24 Address=192.168.5.1/24
{addr6} {addr6}
DHCPServer=yes DHCPServer=yes
@ -1006,6 +1010,7 @@ MACAddress=12:34:56:78:9a:bc
[Match] [Match]
Name=dummy0 Name=dummy0
[Network] [Network]
IPv6AcceptRA=no
Address=192.168.42.100/24 Address=192.168.42.100/24
DNS=192.168.42.1 DNS=192.168.42.1
Domains= one two three four five six seven eight nine ten Domains= one two three four five six seven eight nine ten
@ -1035,6 +1040,7 @@ MACAddress=12:34:56:78:9a:bc
[Match] [Match]
Name=dummy0 Name=dummy0
[Network] [Network]
IPv6AcceptRA=no
Address=192.168.42.100/24 Address=192.168.42.100/24
DNS=192.168.42.1 DNS=192.168.42.1
''') ''')
@ -1107,7 +1113,12 @@ class MatchClientTest(unittest.TestCase, NetworkdTestingUtilities):
def test_basic_matching(self): def test_basic_matching(self):
"""Verify the Name= line works throughout this class.""" """Verify the Name= line works throughout this class."""
self.add_veth_pair('test_if1', 'fake_if2') self.add_veth_pair('test_if1', 'fake_if2')
self.write_network('50-test.network', "[Match]\nName=test_*\n[Network]") self.write_network('50-test.network', '''\
[Match]
Name=test_*
[Network]
IPv6AcceptRA=no
''')
subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
self.assert_link_states(test_if1='managed', fake_if2='unmanaged') self.assert_link_states(test_if1='managed', fake_if2='unmanaged')
@ -1118,11 +1129,13 @@ class MatchClientTest(unittest.TestCase, NetworkdTestingUtilities):
mac = '00:01:02:03:98:99' mac = '00:01:02:03:98:99'
self.add_veth_pair('test_veth', 'test_peer', self.add_veth_pair('test_veth', 'test_peer',
['addr', mac], ['addr', mac]) ['addr', mac], ['addr', mac])
self.write_network('50-no-veth.network', """\ self.write_network('50-no-veth.network', '''\
[Match] [Match]
MACAddress={} MACAddress={}
Name=!nonexistent *peer* Name=!nonexistent *peer*
[Network]""".format(mac)) [Network]
IPv6AcceptRA=no
'''.format(mac))
subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) subprocess.check_call(['systemctl', 'start', 'systemd-networkd'])
self.assert_link_states(test_veth='managed', test_peer='unmanaged') self.assert_link_states(test_veth='managed', test_peer='unmanaged')

View File

@ -0,0 +1,71 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
# shellcheck disable=SC2317
set -ex
set -o pipefail
# This is a reproducer of issue #35329,
# which is a regression caused by 405be62f05d76f1845f347737b5972158c79dd3e.
IFNAME=udevtestnetif
at_exit() {
set +e
systemctl stop testsleep.service
rm -f /run/udev/udev.conf.d/timeout.conf
rm -f /run/udev/rules.d/99-testsuite.rules
# Forcibly kills sleep command invoked by the udev rule before restarting,
# otherwise systemctl restart below will takes longer.
killall -KILL sleep
systemctl restart systemd-udevd.service
ip link del "$IFNAME"
}
trap at_exit EXIT
udevadm settle
mkdir -p /run/udev/udev.conf.d/
cat >/run/udev/udev.conf.d/timeout.conf <<EOF
event_timeout=1h
EOF
mkdir -p /run/udev/rules.d/
cat >/run/udev/rules.d/99-testsuite.rules <<EOF
SUBSYSTEM=="net", ACTION=="change", KERNEL=="${IFNAME}", OPTIONS="log_level=debug", RUN+="/usr/bin/sleep 1000"
EOF
systemctl restart systemd-udevd.service
ip link add "$IFNAME" type dummy
IFINDEX=$(ip -json link show "$IFNAME" | jq '.[].ifindex')
udevadm wait --timeout 10 "/sys/class/net/${IFNAME}"
# Check if the database file is created.
[[ -e "/run/udev/data/n${IFINDEX}" ]]
systemd-run \
-p After="sys-subsystem-net-devices-${IFNAME}.device" \
-p BindsTo="sys-subsystem-net-devices-${IFNAME}.device" \
-u testsleep.service \
sleep 1h
timeout 10 bash -c 'until systemctl is-active testsleep.service; do sleep .5; done'
udevadm trigger "/sys/class/net/${IFNAME}"
timeout 30 bash -c "until grep -F 'ID_PROCESSING=1' /run/udev/data/n${IFINDEX}; do sleep .5; done"
for _ in {1..3}; do
systemctl daemon-reexec
systemctl is-active testsleep.service
done
for _ in {1..3}; do
systemctl daemon-reload
systemctl is-active testsleep.service
done
# Check if the reexec and reload have finished during processing the event.
grep -F 'ID_PROCESSING=1' "/run/udev/data/n${IFINDEX}"
exit 0

View File

@ -739,6 +739,10 @@ EOF
systemctl stop user@"$uid".service systemctl stop user@"$uid".service
} }
testcase_varlink() {
varlinkctl introspect /run/systemd/io.systemd.Login
}
setup_test_user setup_test_user
test_write_dropin test_write_dropin
run_testcases run_testcases

View File

@ -5,3 +5,7 @@ set -o pipefail
SYSTEMD_IN_CHROOT=1 systemd-detect-virt --chroot SYSTEMD_IN_CHROOT=1 systemd-detect-virt --chroot
(! SYSTEMD_IN_CHROOT=0 systemd-detect-virt --chroot) (! SYSTEMD_IN_CHROOT=0 systemd-detect-virt --chroot)
if ! systemd-detect-virt -c; then
unshare --mount-proc --fork --user --pid systemd-detect-virt --container
fi