mirror of
https://github.com/systemd/systemd.git
synced 2024-12-21 13:34:21 +03:00
Compare commits
74 Commits
7ea0d0cb2a
...
8cbcdc78db
Author | SHA1 | Date | |
---|---|---|---|
|
8cbcdc78db | ||
|
4103bf9f2f | ||
|
026dfd60d4 | ||
|
1af989e8de | ||
|
8506a9955c | ||
|
d2cd189324 | ||
|
b226b7fb6d | ||
|
060e2512cd | ||
|
81082f2dc2 | ||
|
616586b910 | ||
|
cf7d0a2d2e | ||
|
06ffa66a5b | ||
|
33bfa69b2e | ||
|
f108996319 | ||
|
dec47e58a6 | ||
|
cdcb1eeeb8 | ||
|
77d4a263c1 | ||
|
5f29c86ace | ||
|
182ffb5819 | ||
|
e9f781a5a4 | ||
|
cb3801a4c9 | ||
|
8a135111ca | ||
|
5e837858e7 | ||
|
a3fecea5e2 | ||
|
f01132aacf | ||
|
ad920b4cb3 | ||
|
a7396f8364 | ||
|
71ec342d13 | ||
|
3b9010b170 | ||
|
cf89e48028 | ||
|
3cbf00a30c | ||
|
0f67cb3606 | ||
|
5ceb38cb1e | ||
|
312cf91005 | ||
|
b1b128d0e2 | ||
|
91cdc8ab0f | ||
|
009a02b263 | ||
|
b83358b87f | ||
|
bf1ef54d30 | ||
|
8f9ea89ce4 | ||
|
5229cd839a | ||
|
595ca10f37 | ||
|
6e3f32cc56 | ||
|
1462736c7e | ||
|
f441831c9e | ||
|
4bc06da775 | ||
|
ced0ef3b35 | ||
|
e95861d909 | ||
|
0f72af536f | ||
|
b5ea69f5ac | ||
|
04fa5c1580 | ||
|
394a678aec | ||
|
32c3e1379d | ||
|
0d1ebcf67d | ||
|
0e5a83f510 | ||
|
8f114904fc | ||
|
cffae6e113 | ||
|
a48803fd84 | ||
|
22583a002e | ||
|
be1bcb85ce | ||
|
85d040dabd | ||
|
67e5622bfe | ||
|
0da73fab56 | ||
|
64b504bde3 | ||
|
12807b5a49 | ||
|
4a4e7ec0e9 | ||
|
d517427dff | ||
|
5e6a48bf99 | ||
|
781c8c3f72 | ||
|
25b1a73f71 | ||
|
a5370d35d6 | ||
|
1184626a26 | ||
|
9311c28b34 | ||
|
19491cc90f |
4
.github/workflows/coverage.yml
vendored
4
.github/workflows/coverage.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
- uses: systemd/mkosi@07ef37c4c0dad5dfc6cec86c967a7600df1cd88c
|
||||
- uses: systemd/mkosi@7d45366395f29fdb2b534a850c09d23d29b78fa9
|
||||
|
||||
# Freeing up disk space with rm -rf can take multiple minutes. Since we don't need the extra free space
|
||||
# immediately, we remove the files in the background. However, we first move them to a different location
|
||||
@ -64,7 +64,7 @@ jobs:
|
||||
MESON_OPTIONS=--werror
|
||||
COVERAGE=1
|
||||
|
||||
[Host]
|
||||
[Runtime]
|
||||
QemuMem=4G
|
||||
EOF
|
||||
|
||||
|
4
.github/workflows/mkosi.yml
vendored
4
.github/workflows/mkosi.yml
vendored
@ -113,7 +113,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
- uses: systemd/mkosi@c4bbf3b71a3e2cf947995caedf10f69da3c4957a
|
||||
- uses: systemd/mkosi@7d45366395f29fdb2b534a850c09d23d29b78fa9
|
||||
|
||||
# Freeing up disk space with rm -rf can take multiple minutes. Since we don't need the extra free space
|
||||
# immediately, we remove the files in the background. However, we first move them to a different location
|
||||
@ -171,7 +171,7 @@ jobs:
|
||||
[Content]
|
||||
SELinuxRelabel=${{ matrix.relabel }}
|
||||
|
||||
[Host]
|
||||
[Runtime]
|
||||
QemuMem=4G
|
||||
EOF
|
||||
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,6 +7,7 @@
|
||||
.config.args
|
||||
.gdb_history
|
||||
.deps/
|
||||
.venv/
|
||||
.mypy_cache/
|
||||
__pycache__/
|
||||
/*.gcda
|
||||
|
6
TODO
6
TODO
@ -446,10 +446,6 @@ Features:
|
||||
* credentials: add a flag to the scoped credentials that if set require PK
|
||||
reauthentication when unlocking a secret.
|
||||
|
||||
* teach systemd --user to properly load credentials off disk, with
|
||||
/etc/credstore equivalent and similar. Make sure that $CREDENTIALS_DIRECTORY=
|
||||
actually works too when run with user privs.
|
||||
|
||||
* extend the smbios11 logic for passing credentials so that instead of passing
|
||||
the credential data literally it can also just reference an AF_VSOCK CID/port
|
||||
to read them from. This way the data doesn't remain in the SMBIOS blob during
|
||||
@ -795,7 +791,7 @@ Features:
|
||||
* udevd: extend memory pressure logic: also kill any idle worker processes
|
||||
|
||||
* udevadm: to make symlink querying with udevadm nicer:
|
||||
- do not enable the pager for queries like 'udevadm info -q -r symlink'
|
||||
- do not enable the pager for queries like 'udevadm info -q symlink -r'
|
||||
- add mode with newlines instead of spaces (for grep)?
|
||||
|
||||
* SIGRTMIN+18 and memory pressure handling should still be added to: hostnamed,
|
||||
|
@ -3,3 +3,7 @@
|
||||
# Dell iDRAC Virtual USB NIC
|
||||
usb:v413CpA102*
|
||||
ID_NET_NAME_FROM_DATABASE=idrac
|
||||
|
||||
# Disable inclusion of PCI domain in interface names on Azure MANA
|
||||
pci:v00001414d000000BA*
|
||||
ID_NET_NAME_INCLUDE_DOMAIN=0
|
||||
|
@ -10,7 +10,7 @@ sudo systemd-cryptsetup attach mytest /dev/sdXn - fido2-device=auto
|
||||
# If that worked, let's now add the same line persistently to /etc/crypttab,
|
||||
# for the future. We don't want to use the (unstable) /dev/sdX name, so let's
|
||||
# figure out a stable link:
|
||||
udevadm info -q -r symlink /dev/sdXn
|
||||
udevadm info -q symlink -r /dev/sdXn
|
||||
|
||||
# Now add the line using the by-uuid symlink to /etc/crypttab:
|
||||
sudo bash -c 'echo "mytest /dev/disk/by-uuid/... - fido2-device=auto" >>/etc/crypttab'
|
||||
|
@ -97,6 +97,18 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.break=</varname></term>
|
||||
<term><varname>rd.systemd.break=</varname></term>
|
||||
<listitem>
|
||||
<para>Parameters understood by
|
||||
<citerefentry><refentrytitle>systemd-debug-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
to pause the boot process at a certain point and spawn a debug shell.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.run=</varname></term>
|
||||
<term><varname>systemd.run_success_action=</varname></term>
|
||||
|
@ -205,6 +205,11 @@
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">smbios11</arg>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-analyze</command>
|
||||
<arg choice="opt" rep="repeat">OPTIONS</arg>
|
||||
<arg choice="plain">chid</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
@ -1084,6 +1089,37 @@ io.systemd.credential:vmm.notify_socket=vsock-stream:2:254570042
|
||||
<xi:include href="version-info.xml" xpointer="v257"/>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title><command>systemd-analyze chid</command></title>
|
||||
|
||||
<para>Shows a list of Computer Hardware IDs (CHIDs) of the local system. These IDs identify the
|
||||
system's computer hardware, based on SMBIOS data. See <ulink
|
||||
url="https://learn.microsoft.com/en-us/windows-hardware/drivers/dashboard/using-chids">Using Computer
|
||||
Hardware IDs (CHIDs)</ulink> for details about CHIDs.</para>
|
||||
|
||||
<example>
|
||||
<title>Example output</title>
|
||||
<programlisting>$ systemd-analyze chid
|
||||
TYPE INPUT CHID
|
||||
3 MFPSmp 520537c0-3b59-504f-b062-9682ea236b21
|
||||
4 MFPS-- edf05dc8-a53d-5b2c-8023-630bca2a2463
|
||||
5 MFP--- ebc6a4d9-ec48-537a-916b-c69fa4fdd814
|
||||
6 M--Smp 5ebe4bba-f598-5e90-9ff2-9fd0d3211465
|
||||
7 M--S-- 1a3fb835-b42a-5f9c-a38c-eff5bfd5c41d
|
||||
8 M-P-mp 2a831dce-8163-5bad-8406-435b8c752dd8
|
||||
9 M-P--- 7c21c878-4a75-50f7-9816-21e811588da0
|
||||
10 MF--mp 9a003537-bcc5-500e-b10a-8d8892e4fc64
|
||||
11 MF---- bb9122bb-8a5c-50d2-a742-a85beb719909
|
||||
13 M---mp bfc36935-5032-5987-a0a3-6311f01de33a
|
||||
|
||||
LEGEND: M → sys_vendor (LENOVO) ┄ F → product_family (ThinkPad X1 Carbon Gen 9) ┄ P → product_name (20XW0055GE)
|
||||
S → product_sku (LENOVO_MT_20XW_BU_Think_FM_ThinkPad X1 Carbon Gen 9) ┄ m → board_vendor (LENOVO)
|
||||
p → board_name (20XW0055GE)</programlisting>
|
||||
</example>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/>
|
||||
</refsect2>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
@ -31,45 +31,131 @@
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><filename>systemd-debug-generator</filename> is a generator
|
||||
that reads the kernel command line and understands three
|
||||
options:</para>
|
||||
<para><command>systemd-debug-generator</command> is a generator that provides some debugging
|
||||
functionality.</para>
|
||||
|
||||
<para>If the <option>systemd.mask=</option> or <option>rd.systemd.mask=</option>
|
||||
option is specified and followed by a unit name, this unit is
|
||||
masked for the runtime (i.e. for this session — from boot to shutdown), similarly to the effect of
|
||||
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||
<command>mask</command> command. This is useful to boot with
|
||||
certain units removed from the initial boot transaction for
|
||||
debugging system startup. May be specified more than once.
|
||||
<option>rd.systemd.mask=</option> is honored only by initial
|
||||
RAM disk (initrd) while <option>systemd.mask=</option> is
|
||||
honored only in the main system.</para>
|
||||
|
||||
<para>If the <option>systemd.wants=</option> or
|
||||
<option>rd.systemd.wants=</option> option is specified
|
||||
and followed by a unit name, a start job for this unit is added to
|
||||
the initial transaction. This is useful to start one or more
|
||||
additional units at boot. May be specified more than once.
|
||||
<option>rd.systemd.wants=</option> is honored only by initial
|
||||
RAM disk (initrd) while <option>systemd.wants=</option> is
|
||||
honored only in the main system.</para>
|
||||
|
||||
<para>If the <option>systemd.debug_shell</option> or <option>rd.systemd.debug_shell</option> option is
|
||||
specified, the debug shell service <literal>debug-shell.service</literal> is pulled into the boot
|
||||
transaction and a debug shell will be spawned during early boot. By default,
|
||||
<filename>&DEBUGTTY;</filename> is used, but a specific tty can also be specified, either with or without
|
||||
the <filename>/dev/</filename> prefix. To set the tty to use without enabling the debug shell, the
|
||||
<option>systemd.default_debug_tty=</option> option can be used which also takes a tty with or without the
|
||||
<filename>/dev/</filename> prefix. Note that the shell may also be turned on persistently by enabling it
|
||||
with <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||
<command>enable</command> command. <option>rd.systemd.debug_shell</option> is honored only by initial
|
||||
RAM disk (initrd) while <option>systemd.debug_shell</option> is honored only in the main system.</para>
|
||||
|
||||
<para><filename>systemd-debug-generator</filename> implements
|
||||
<para><command>systemd-debug-generator</command> implements
|
||||
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Kernel Command Line</title>
|
||||
|
||||
<para><command>systemd-debug-generator</command> understands the following kernel command line
|
||||
parameters:</para>
|
||||
|
||||
<variablelist class='kernel-commandline-options'>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.mask=</varname></term>
|
||||
<term><varname>rd.systemd.mask=</varname></term>
|
||||
|
||||
<listitem><para>These options take a unit name as argument. The unit specified is masked for the
|
||||
runtime (i.e. for this session — from boot to shutdown), similarly to the effect of
|
||||
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||
<command>mask</command> command. This is useful to boot with certain units removed from the initial
|
||||
boot transaction for debugging system startup. May be specified more than once. The option prefixed
|
||||
with <literal>rd.</literal> is honored only in the initrd, while the one without prefix is only
|
||||
honored in the main system.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v215"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.wants=</varname></term>
|
||||
<term><varname>rd.systemd.wants=</varname></term>
|
||||
|
||||
<listitem><para>These options take a unit name as argument. A start job for this unit is added to the
|
||||
initial transaction. This is useful to start one or more additional units at boot. May be specified
|
||||
more than once. The option prefixed with <literal>rd.</literal> is honored only in the initrd, while
|
||||
the one that is not prefixed only in the main system.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v215"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.debug_shell</varname></term>
|
||||
<term><varname>rd.systemd.debug_shell</varname></term>
|
||||
<term><varname>systemd.default_debug_tty=</varname></term>
|
||||
<term><varname>rd.systemd.default_debug_tty=</varname></term>
|
||||
|
||||
<listitem><para>If the <option>systemd.debug_shell</option> or
|
||||
<option>rd.systemd.debug_shell</option> option is specified, the debug shell service
|
||||
<literal>debug-shell.service</literal> is pulled into the boot transaction and a debug shell will be
|
||||
spawned during early boot. By default, <filename>&DEBUGTTY;</filename> is used, but a specific tty
|
||||
can also be specified, either with or without the <filename>/dev/</filename> prefix. To set the tty
|
||||
to use without enabling the debug shell, the <option>systemd.default_debug_tty=</option> option can
|
||||
be used which also takes a tty with or without the <filename>/dev/</filename> prefix. Note that the
|
||||
shell may also be turned on persistently by enabling it with
|
||||
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||
<command>enable</command> command. The options prefixed with <literal>rd.</literal> are honored only
|
||||
in the initrd, while the ones without prefix are only honored in the main system.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v215"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.break=</varname></term>
|
||||
<term><varname>rd.systemd.break=</varname></term>
|
||||
|
||||
<listitem><para>Takes one of <option>pre-udev</option>, <option>pre-basic</option>,
|
||||
<option>pre-mount</option>, or <option>pre-switch-root</option> (the default for the
|
||||
<literal>rd.</literal> option). It also accepts multiple values separated by comma
|
||||
(<literal>,</literal>). These options allow to pause the boot process at a certain point and spawn a
|
||||
debug shell. After exiting this shell, the system will resume booting. The option prefixed with
|
||||
<literal>rd.</literal> is honored only in the initrd, while the one without prefix is only honored in
|
||||
the main system.</para>
|
||||
|
||||
<table>
|
||||
<title>Available breakpoints</title>
|
||||
|
||||
<tgroup cols='4'>
|
||||
<colspec colname='breakpoint' />
|
||||
<colspec colname='description' />
|
||||
<colspec colname='initrd' />
|
||||
<colspec colname='main' />
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Breakpoints</entry>
|
||||
<entry>Description</entry>
|
||||
<entry>Can be used in the initrd</entry>
|
||||
<entry>Can be used in the main system</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry><option>pre-udev</option></entry>
|
||||
<entry>Before starting to process kernel uevents, i.e., before <filename>systemd-udevd.service</filename> starts.</entry>
|
||||
<entry>✓</entry>
|
||||
<entry>✓</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><option>pre-basic</option></entry>
|
||||
<entry>Before leaving early boot and regular services start, i.e., before <filename>basic.target</filename> is reached.</entry>
|
||||
<entry>✓</entry>
|
||||
<entry>✓</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><option>pre-mount</option></entry>
|
||||
<entry>Before the root filesystem is mounted, i.e., before <filename>sysroot.mount</filename> starts.</entry>
|
||||
<entry>✓</entry>
|
||||
<entry>✗</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><option>pre-switch-root</option></entry>
|
||||
<entry>Before switching from the initrd to the real root.</entry>
|
||||
<entry>✓</entry>
|
||||
<entry>✗</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>System Credentials</title>
|
||||
|
||||
@ -108,6 +194,8 @@
|
||||
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd.system-credentials</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
</simplelist></para>
|
||||
</refsect1>
|
||||
|
||||
|
@ -3468,37 +3468,43 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
|
||||
<term><varname>LoadCredentialEncrypted=</varname><replaceable>ID</replaceable><optional>:<replaceable>PATH</replaceable></optional></term>
|
||||
|
||||
<listitem><para>Pass a credential to the unit. Credentials are limited-size binary or textual objects
|
||||
that may be passed to unit processes. They are primarily used for passing cryptographic keys (both
|
||||
public and private) or certificates, user account information or identity information from host to
|
||||
services. The data is accessible from the unit's processes via the file system, at a read-only
|
||||
location that (if possible and permitted) is backed by non-swappable memory. The data is only
|
||||
accessible to the user associated with the unit, via the
|
||||
<varname>User=</varname>/<varname>DynamicUser=</varname> settings (as well as the superuser). When
|
||||
available, the location of credentials is exported as the <varname>$CREDENTIALS_DIRECTORY</varname>
|
||||
environment variable to the unit's processes.</para>
|
||||
that may be passed to unit processes. They are primarily intended for passing cryptographic keys
|
||||
(both public and private) or certificates, user account information or identity information from host
|
||||
to services, but can be freely used to pass any kind of limited-size information to a service. The
|
||||
data is accessible from the unit's processes via the file system, at a read-only location that (if
|
||||
possible and permitted) is backed by non-swappable memory. The data is only accessible to the user
|
||||
associated with the unit, via the <varname>User=</varname>/<varname>DynamicUser=</varname> settings
|
||||
(as well as the superuser). When available, the location of credentials is exported as the
|
||||
<varname>$CREDENTIALS_DIRECTORY</varname> environment variable to the unit's processes.</para>
|
||||
|
||||
<para>The <varname>LoadCredential=</varname> setting takes a textual ID to use as name for a
|
||||
credential plus a file system path, separated by a colon. The ID must be a short ASCII string
|
||||
suitable as filename in the filesystem, and may be chosen freely by the user. If the specified path
|
||||
is absolute it is opened as regular file and the credential data is read from it. If the absolute
|
||||
path refers to an <constant>AF_UNIX</constant> stream socket in the file system a connection is made
|
||||
to it (only once at unit start-up) and the credential data read from the connection, providing an
|
||||
to it (once at process invocation) and the credential data read from the connection, providing an
|
||||
easy IPC integration point for dynamically transferring credentials from other services.</para>
|
||||
|
||||
<para>If the specified path is not absolute and itself qualifies as valid credential identifier it is
|
||||
attempted to find a credential that the service manager itself received under the specified name —
|
||||
which may be used to propagate credentials from an invoking environment (e.g. a container manager
|
||||
that invoked the service manager) into a service. If no matching system credential is found, the
|
||||
directories <filename>/etc/credstore/</filename>, <filename>/run/credstore/</filename> and
|
||||
<filename>/usr/lib/credstore/</filename> are searched for files under the credential's name — which
|
||||
hence are recommended locations for credential data on disk. If
|
||||
that invoked the service manager) into a service. If no matching passed credential is found, the
|
||||
system service manager will search the directories <filename>/etc/credstore/</filename>,
|
||||
<filename>/run/credstore/</filename> and <filename>/usr/lib/credstore/</filename> for files under the
|
||||
credential's name — which hence are recommended locations for credential data on disk. If
|
||||
<varname>LoadCredentialEncrypted=</varname> is used <filename>/run/credstore.encrypted/</filename>,
|
||||
<filename>/etc/credstore.encrypted/</filename>, and
|
||||
<filename>/usr/lib/credstore.encrypted/</filename> are searched as well.</para>
|
||||
<filename>/usr/lib/credstore.encrypted/</filename> are searched as well. The per-user service manager
|
||||
will search <filename>$XDG_CONFIG_HOME/credstore/</filename>,
|
||||
<filename>$XDG_RUNTIME_DIR/credstore/</filename>, <filename>$HOME/.local/lib/credstore/</filename>
|
||||
(and the counterparts ending with <filename>…/credstore.encrypted/</filename>) instead. The
|
||||
<citerefentry><refentrytitle>systemd-path</refentrytitle><manvolnum>1</manvolnum></citerefentry> tool
|
||||
may be used to query the precise credential store search path.</para>
|
||||
|
||||
<para>If the file system path is omitted it is chosen identical to the credential name, i.e. this is
|
||||
a terse way to declare credentials to inherit from the service manager into a service. This option
|
||||
may be used multiple times, each time defining an additional credential to pass to the unit.</para>
|
||||
a terse way to declare credentials to inherit from the service manager or credstore directories into
|
||||
a service. This option may be used multiple times, each time defining an additional credential to
|
||||
pass to the unit.</para>
|
||||
|
||||
<para>Note that if the path is not specified or a valid credential identifier is given, i.e.
|
||||
in the above two cases, a missing credential is not considered fatal.</para>
|
||||
|
@ -64,6 +64,10 @@
|
||||
override existing definitions. For tests, generators may be called with just one argument; the generator
|
||||
should assume that all three paths are the same in that case.</para>
|
||||
|
||||
<para>Generators executed by the system manager are invoked in a sandbox with a private writable
|
||||
<filename>/tmp/</filename> directory and where most of the file system is read-only except for the
|
||||
generator output directories.</para>
|
||||
|
||||
<para>Directory paths for generator output differ by priority: <filename>…/generator.early</filename> has
|
||||
priority higher than the admin configuration in <filename>/etc/</filename>, while
|
||||
<filename>…/generator</filename> has lower priority than <filename>/etc/</filename> but higher than
|
||||
|
@ -672,7 +672,7 @@
|
||||
|
||||
<listitem><para>Specifies what anchor point <varname>Path=</varname> should be relative to. Takes one
|
||||
of <constant>root</constant>, <constant>esp</constant>, <constant>xbootldr</constant>,
|
||||
<constant>boot</constant> or <constant>directory</constant>. If unspecified, defaults to
|
||||
<constant>boot</constant> or <constant>explicit</constant>. If unspecified, defaults to
|
||||
<constant>root</constant>.</para>
|
||||
|
||||
<para>If set to <constant>root</constant>, <constant>esp</constant>, <constant>xbootldr</constant>,
|
||||
|
@ -93,7 +93,7 @@
|
||||
XML file.
|
||||
This may be used by software centers (such as GNOME Software or KDE Discover) to present rich
|
||||
metadata about this feature.
|
||||
This includes display names, chagnelogs, icons, and more.
|
||||
This includes display names, changelogs, icons, and more.
|
||||
This setting supports specifier expansion; see below for details on supported specifiers.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
@ -177,7 +177,7 @@ Enabled=false
|
||||
</programlisting></para>
|
||||
|
||||
<para>The above defines the <literal>devel</literal> feature, and disables it by default.
|
||||
Now let's a define a transfer that's associated with this feature:</para>
|
||||
Now let's define a transfer that's associated with this feature:</para>
|
||||
|
||||
<para><programlisting># /usr/lib/sysupdate.d/50-devel.transfer
|
||||
[Transfer]
|
||||
|
@ -10,7 +10,7 @@ sudo systemd-cryptsetup attach mytest /dev/sdXn - tpm2-device=auto
|
||||
# If that worked, let's now add the same line persistently to /etc/crypttab,
|
||||
# for the future. We don't want to use the (unstable) /dev/sdX name, so let's
|
||||
# figure out a stable link:
|
||||
udevadm info -q -r symlink /dev/sdXn
|
||||
udevadm info -q symlink -r /dev/sdXn
|
||||
|
||||
# Now add the line using the by-uuid symlink to /etc/crypttab:
|
||||
sudo bash -c 'echo "mytest /dev/disk/by-uuid/... - tpm2-device=auto" >>/etc/crypttab'
|
||||
|
@ -26,7 +26,7 @@ sudo systemd-cryptsetup attach mytest /dev/sdXn - pkcs11-uri=auto
|
||||
# If that worked, let's now add the same line persistently to /etc/crypttab,
|
||||
# for the future. We don't want to use the (unstable) /dev/sdX name, so let's
|
||||
# figure out a stable link:
|
||||
udevadm info -q -r symlink /dev/sdXn
|
||||
udevadm info -q symlink -r /dev/sdXn
|
||||
|
||||
# Now add the line using the by-uuid symlink to /etc/crypttab:
|
||||
sudo bash -c 'echo "mytest /dev/disk/by-uuid/... - pkcs11-uri=auto" >>/etc/crypttab'
|
||||
|
@ -130,7 +130,7 @@ Packages=
|
||||
zsh
|
||||
zstd
|
||||
|
||||
[Host]
|
||||
[Runtime]
|
||||
Credentials=
|
||||
journal.storage=persistent
|
||||
tty.serial.hvc0.agetty.autologin=root
|
||||
|
9
po/fi.po
9
po/fi.po
@ -8,8 +8,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-11-28 18:16+0900\n"
|
||||
"PO-Revision-Date: 2024-11-20 19:13+0000\n"
|
||||
"Last-Translator: Jiri Grönroos <jiri.gronroos@iki.fi>\n"
|
||||
"PO-Revision-Date: 2024-12-20 15:38+0000\n"
|
||||
"Last-Translator: Ricky Tigg <ricky.tigg@gmail.com>\n"
|
||||
"Language-Team: Finnish <https://translate.fedoraproject.org/projects/systemd/"
|
||||
"main/fi/>\n"
|
||||
"Language: fi\n"
|
||||
@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.8.2\n"
|
||||
"X-Generator: Weblate 5.9.1\n"
|
||||
|
||||
#: src/core/org.freedesktop.systemd1.policy.in:22
|
||||
msgid "Send passphrase back to system"
|
||||
@ -1176,9 +1176,8 @@ msgid "Manage optional features"
|
||||
msgstr "Hallitse valinnaisia ominaisuuksia"
|
||||
|
||||
#: src/sysupdate/org.freedesktop.sysupdate1.policy:76
|
||||
#, fuzzy
|
||||
msgid "Authentication is required to manage optional features."
|
||||
msgstr "Todennus vaaditaan valinnaisten ominaisuuksien hallintaan"
|
||||
msgstr "Todennus vaaditaan valinnaisten ominaisuuksien hallintaan."
|
||||
|
||||
#: src/timedate/org.freedesktop.timedate1.policy:22
|
||||
msgid "Set system time"
|
||||
|
@ -67,7 +67,7 @@ _systemd_analyze() {
|
||||
)
|
||||
|
||||
local -A VERBS=(
|
||||
[STANDALONE]='time blame unit-files unit-paths exit-status compare-versions calendar timestamp timespan pcrs srk has-tpm2 smbios11'
|
||||
[STANDALONE]='time blame unit-files unit-paths exit-status compare-versions calendar timestamp timespan pcrs srk has-tpm2 smbios11 chid'
|
||||
[CRITICAL_CHAIN]='critical-chain'
|
||||
[DOT]='dot'
|
||||
[DUMP]='dump'
|
||||
|
224
src/analyze/analyze-chid.c
Normal file
224
src/analyze/analyze-chid.c
Normal file
@ -0,0 +1,224 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "analyze.h"
|
||||
#include "analyze-chid.h"
|
||||
#include "chid-fundamental.h"
|
||||
#include "efi-api.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "format-table.h"
|
||||
#include "parse-util.h"
|
||||
#include "strv.h"
|
||||
#include "utf8.h"
|
||||
#include "virt.h"
|
||||
|
||||
static int parse_chid_type(const char *s, size_t *ret) {
|
||||
unsigned u;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
r = safe_atou(s, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (u >= CHID_TYPES_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
if (ret)
|
||||
*ret = u;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char chid_smbios_fields_char[_CHID_SMBIOS_FIELDS_MAX] = {
|
||||
[CHID_SMBIOS_MANUFACTURER] = 'M',
|
||||
[CHID_SMBIOS_FAMILY] = 'F',
|
||||
[CHID_SMBIOS_PRODUCT_NAME] = 'P',
|
||||
[CHID_SMBIOS_PRODUCT_SKU] = 'S',
|
||||
[CHID_SMBIOS_BASEBOARD_MANUFACTURER] = 'm',
|
||||
[CHID_SMBIOS_BASEBOARD_PRODUCT] = 'p',
|
||||
};
|
||||
|
||||
static char *chid_smbios_fields_string(uint32_t combination) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
|
||||
for (ChidSmbiosFields f = 0; f < _CHID_SMBIOS_FIELDS_MAX; f++) {
|
||||
char c;
|
||||
|
||||
c = (combination & (UINT32_C(1) << f)) ? chid_smbios_fields_char[f] : '-';
|
||||
|
||||
if (!strextend(&s, CHAR_TO_STR(c)))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return TAKE_PTR(s);
|
||||
}
|
||||
|
||||
static int add_chid(Table *table, const EFI_GUID guids[static CHID_TYPES_MAX], size_t t) {
|
||||
int r;
|
||||
|
||||
assert(table);
|
||||
assert(guids);
|
||||
assert(t < CHID_TYPES_MAX);
|
||||
|
||||
sd_id128_t id = efi_guid_to_id128(guids + t);
|
||||
|
||||
if (sd_id128_is_null(id))
|
||||
return 0;
|
||||
|
||||
_cleanup_free_ char *flags = chid_smbios_fields_string(chid_smbios_table[t]);
|
||||
if (!flags)
|
||||
return log_oom();
|
||||
|
||||
r = table_add_many(table,
|
||||
TABLE_UINT, (unsigned) t,
|
||||
TABLE_STRING, flags,
|
||||
TABLE_UUID, id);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void smbios_fields_free(char16_t *(*fields)[_CHID_SMBIOS_FIELDS_MAX]) {
|
||||
assert(fields);
|
||||
|
||||
FOREACH_ARRAY(i, *fields, _CHID_SMBIOS_FIELDS_MAX)
|
||||
free(*i);
|
||||
}
|
||||
|
||||
int verb_chid(int argc, char *argv[], void *userdata) {
|
||||
|
||||
static const char *const smbios_files[_CHID_SMBIOS_FIELDS_MAX] = {
|
||||
[CHID_SMBIOS_MANUFACTURER] = "sys_vendor",
|
||||
[CHID_SMBIOS_FAMILY] = "product_family",
|
||||
[CHID_SMBIOS_PRODUCT_NAME] = "product_name",
|
||||
[CHID_SMBIOS_PRODUCT_SKU] = "product_sku",
|
||||
[CHID_SMBIOS_BASEBOARD_MANUFACTURER] = "board_vendor",
|
||||
[CHID_SMBIOS_BASEBOARD_PRODUCT] = "board_name",
|
||||
};
|
||||
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
int r;
|
||||
|
||||
if (detect_container() > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Container environments do not have SMBIOS.");
|
||||
|
||||
table = table_new("type", "input", "chid");
|
||||
if (!table)
|
||||
return log_oom();
|
||||
|
||||
(void) table_set_align_percent(table, table_get_cell(table, 0, 0), 100);
|
||||
(void) table_set_align_percent(table, table_get_cell(table, 0, 1), 50);
|
||||
|
||||
_cleanup_close_ int smbios_fd = open("/sys/class/dmi/id", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (smbios_fd < 0)
|
||||
return log_error_errno(errno, "Failed to open SMBIOS sysfs object: %m");
|
||||
|
||||
_cleanup_(smbios_fields_free) char16_t* smbios_fields[_CHID_SMBIOS_FIELDS_MAX] = {};
|
||||
for (ChidSmbiosFields f = 0; f < _CHID_SMBIOS_FIELDS_MAX; f++) {
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
size_t size;
|
||||
|
||||
r = read_virtual_file_at(smbios_fd, smbios_files[f], SIZE_MAX, &buf, &size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read SMBIOS field '%s': %m", smbios_files[f]);
|
||||
|
||||
if (size < 1 || buf[size-1] != '\n')
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected SMBIOS field '%s' to end in newline, but it doesn't, refusing.", smbios_files[f]);
|
||||
|
||||
size--;
|
||||
|
||||
smbios_fields[f] = utf8_to_utf16(buf, size);
|
||||
if (!smbios_fields[f])
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
EFI_GUID chids[CHID_TYPES_MAX] = {};
|
||||
chid_calculate((const char16_t* const*) smbios_fields, chids);
|
||||
|
||||
if (strv_isempty(strv_skip(argv, 1)))
|
||||
for (size_t t = 0; t < CHID_TYPES_MAX; t++) {
|
||||
r = add_chid(table, chids, t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
else {
|
||||
STRV_FOREACH(as, strv_skip(argv, 1)) {
|
||||
size_t t;
|
||||
r = parse_chid_type(*as, &t);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to pare CHID type: %s", *as);
|
||||
|
||||
r = add_chid(table, chids, t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
(void) table_set_sort(table, (size_t) 0);
|
||||
}
|
||||
|
||||
r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to output table: %m");
|
||||
|
||||
if (!sd_json_format_enabled(arg_json_format_flags)) {
|
||||
_cleanup_free_ char *legend = NULL;
|
||||
bool separator = false;
|
||||
size_t w = 0;
|
||||
|
||||
legend = strjoin(ansi_grey(), "LEGEND: ", ansi_normal());
|
||||
if (!legend)
|
||||
return log_oom();
|
||||
|
||||
for (ChidSmbiosFields f = 0; f < _CHID_SMBIOS_FIELDS_MAX; f++) {
|
||||
_cleanup_free_ char *c = utf16_to_utf8(smbios_fields[f], SIZE_MAX);
|
||||
if (!c)
|
||||
return log_oom();
|
||||
|
||||
if (!strextend(&legend,
|
||||
ansi_grey(),
|
||||
separator ? " " : "",
|
||||
separator ? special_glyph(SPECIAL_GLYPH_HORIZONTAL_DOTTED) : "",
|
||||
separator ? " " : "",
|
||||
ansi_normal(),
|
||||
CHAR_TO_STR(chid_smbios_fields_char[f]),
|
||||
ansi_grey(),
|
||||
" ",
|
||||
special_glyph(SPECIAL_GLYPH_ARROW_RIGHT),
|
||||
" ",
|
||||
ansi_normal(),
|
||||
smbios_files[f],
|
||||
ansi_grey(),
|
||||
" (",
|
||||
ansi_highlight(),
|
||||
c,
|
||||
ansi_grey(),
|
||||
")",
|
||||
ansi_normal()))
|
||||
return log_oom();
|
||||
|
||||
w += separator * 3 +
|
||||
4 +
|
||||
utf8_console_width(smbios_files[f]) +
|
||||
2 +
|
||||
utf8_console_width(c) +
|
||||
1;
|
||||
|
||||
if (w > 79) {
|
||||
if (!strextend(&legend, "\n "))
|
||||
return log_oom();
|
||||
|
||||
separator = false;
|
||||
w = 8;
|
||||
} else
|
||||
separator = true;
|
||||
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
puts(legend);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
4
src/analyze/analyze-chid.h
Normal file
4
src/analyze/analyze-chid.h
Normal file
@ -0,0 +1,4 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
int verb_chid(int argc, char *argv[], void *userdata);
|
@ -18,6 +18,7 @@
|
||||
#include "analyze-calendar.h"
|
||||
#include "analyze-capability.h"
|
||||
#include "analyze-cat-config.h"
|
||||
#include "analyze-chid.h"
|
||||
#include "analyze-compare-versions.h"
|
||||
#include "analyze-condition.h"
|
||||
#include "analyze-critical-chain.h"
|
||||
@ -219,6 +220,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" filesystems [NAME...] List known filesystems\n"
|
||||
" architectures [NAME...] List known architectures\n"
|
||||
" smbios11 List strings passed via SMBIOS Type #11\n"
|
||||
" chid List local CHIDs\n"
|
||||
"\n%3$sExpression Evaluation:%4$s\n"
|
||||
" condition CONDITION... Evaluate conditions and asserts\n"
|
||||
" compare-versions VERSION1 [OP] VERSION2\n"
|
||||
@ -593,10 +595,6 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Option --offline= requires one or more units to perform a security review.");
|
||||
|
||||
if (sd_json_format_enabled(arg_json_format_flags) && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf", "plot", "fdstore", "pcrs", "architectures", "capability", "exit-status"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Option --json= is only supported for security, inspect-elf, plot, fdstore, pcrs, architectures, capability, exit-status right now.");
|
||||
|
||||
if (arg_threshold != 100 && !streq_ptr(argv[optind], "security"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Option --threshold= is only supported for security right now.");
|
||||
@ -631,10 +629,6 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (streq_ptr(argv[optind], "condition") && arg_unit && optind < argc - 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No conditions can be passed if --unit= is used.");
|
||||
|
||||
if ((!arg_legend && !STRPTR_IN_SET(argv[optind], "plot", "architectures")) ||
|
||||
(streq_ptr(argv[optind], "plot") && !arg_legend && !arg_table && !sd_json_format_enabled(arg_json_format_flags)))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --no-legend is only supported for plot with either --table or --json=.");
|
||||
|
||||
if (arg_table && !streq_ptr(argv[optind], "plot"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --table is only supported for plot right now.");
|
||||
|
||||
@ -691,6 +685,7 @@ static int run(int argc, char *argv[]) {
|
||||
{ "srk", VERB_ANY, 1, 0, verb_srk },
|
||||
{ "architectures", VERB_ANY, VERB_ANY, 0, verb_architectures },
|
||||
{ "smbios11", VERB_ANY, 1, 0, verb_smbios11 },
|
||||
{ "chid", VERB_ANY, VERB_ANY, 0, verb_chid },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -6,6 +6,7 @@ systemd_analyze_sources = files(
|
||||
'analyze-calendar.c',
|
||||
'analyze-capability.c',
|
||||
'analyze-cat-config.c',
|
||||
'analyze-chid.c',
|
||||
'analyze-compare-versions.c',
|
||||
'analyze-condition.c',
|
||||
'analyze-critical-chain.c',
|
||||
|
@ -9,8 +9,8 @@
|
||||
|
||||
#define AUDIT_SESSION_INVALID UINT32_MAX
|
||||
|
||||
int audit_session_from_pid(const PidRef *pid, uint32_t *id);
|
||||
int audit_loginuid_from_pid(const PidRef *pid, uid_t *uid);
|
||||
int audit_session_from_pid(const PidRef *pid, uint32_t *ret_id);
|
||||
int audit_loginuid_from_pid(const PidRef *pid, uid_t *ret_uid);
|
||||
|
||||
bool use_audit(void);
|
||||
|
||||
|
@ -8,8 +8,9 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "capability-util.h"
|
||||
#include "cap-list.h"
|
||||
#include "capability-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "log.h"
|
||||
#include "logarithm.h"
|
||||
@ -17,6 +18,8 @@
|
||||
#include "missing_prctl.h"
|
||||
#include "missing_threads.h"
|
||||
#include "parse-util.h"
|
||||
#include "pidref.h"
|
||||
#include "stat-util.h"
|
||||
#include "user-util.h"
|
||||
|
||||
int have_effective_cap(int value) {
|
||||
@ -607,3 +610,78 @@ int capability_get_ambient(uint64_t *ret) {
|
||||
*ret = a;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int pidref_get_capability(const PidRef *pidref, CapabilityQuintet *ret) {
|
||||
int r;
|
||||
|
||||
if (!pidref_is_set(pidref))
|
||||
return -ESRCH;
|
||||
if (pidref_is_remote(pidref))
|
||||
return -EREMOTE;
|
||||
|
||||
const char *path = procfs_file_alloca(pidref->pid, "status");
|
||||
_cleanup_fclose_ FILE *f = fopen(path, "re");
|
||||
if (!f) {
|
||||
if (errno == ENOENT && proc_mounted() == 0)
|
||||
return -ENOSYS;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
CapabilityQuintet q = CAPABILITY_QUINTET_NULL;
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
|
||||
r = read_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
static const struct {
|
||||
const char *field;
|
||||
size_t offset;
|
||||
} fields[] = {
|
||||
{ "CapBnd:", offsetof(CapabilityQuintet, bounding) },
|
||||
{ "CapInh:", offsetof(CapabilityQuintet, inheritable) },
|
||||
{ "CapPrm:", offsetof(CapabilityQuintet, permitted) },
|
||||
{ "CapEff:", offsetof(CapabilityQuintet, effective) },
|
||||
{ "CapAmb:", offsetof(CapabilityQuintet, ambient) },
|
||||
};
|
||||
|
||||
FOREACH_ELEMENT(i, fields) {
|
||||
|
||||
const char *p = first_word(line, i->field);
|
||||
if (!p)
|
||||
continue;
|
||||
|
||||
uint64_t *v = (uint64_t*) ((uint8_t*) &q + i->offset);
|
||||
|
||||
if (*v != CAP_MASK_UNSET)
|
||||
return -EBADMSG;
|
||||
|
||||
r = safe_atoux64(p, v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (*v == CAP_MASK_UNSET)
|
||||
return -EBADMSG;
|
||||
}
|
||||
}
|
||||
|
||||
if (q.effective == CAP_MASK_UNSET ||
|
||||
q.inheritable == CAP_MASK_UNSET ||
|
||||
q.permitted == CAP_MASK_UNSET ||
|
||||
q.effective == CAP_MASK_UNSET ||
|
||||
q.ambient == CAP_MASK_UNSET)
|
||||
return -EBADMSG;
|
||||
|
||||
r = pidref_verify(pidref);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = q;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "macro.h"
|
||||
#include "missing_capability.h"
|
||||
#include "pidref.h"
|
||||
|
||||
/* Special marker used when storing a capabilities mask as "unset" */
|
||||
#define CAP_MASK_UNSET UINT64_MAX
|
||||
@ -66,14 +67,18 @@ typedef struct CapabilityQuintet {
|
||||
|
||||
assert_cc(CAP_LAST_CAP < 64);
|
||||
|
||||
#define CAPABILITY_QUINTET_NULL { CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET }
|
||||
#define CAPABILITY_QUINTET_NULL (CapabilityQuintet) { CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET }
|
||||
|
||||
static inline bool capability_is_set(uint64_t v) {
|
||||
return v != CAP_MASK_UNSET;
|
||||
}
|
||||
|
||||
static inline bool capability_quintet_is_set(const CapabilityQuintet *q) {
|
||||
return q->effective != CAP_MASK_UNSET ||
|
||||
q->bounding != CAP_MASK_UNSET ||
|
||||
q->inheritable != CAP_MASK_UNSET ||
|
||||
q->permitted != CAP_MASK_UNSET ||
|
||||
q->ambient != CAP_MASK_UNSET;
|
||||
return capability_is_set(q->effective) ||
|
||||
capability_is_set(q->bounding) ||
|
||||
capability_is_set(q->inheritable) ||
|
||||
capability_is_set(q->permitted) ||
|
||||
capability_is_set(q->ambient);
|
||||
}
|
||||
|
||||
/* Mangles the specified caps quintet taking the current bounding set into account:
|
||||
@ -84,3 +89,5 @@ bool capability_quintet_mangle(CapabilityQuintet *q);
|
||||
int capability_quintet_enforce(const CapabilityQuintet *q);
|
||||
|
||||
int capability_get_ambient(uint64_t *ret);
|
||||
|
||||
int pidref_get_capability(const PidRef *pidref, CapabilityQuintet *ret);
|
||||
|
@ -500,22 +500,6 @@ int pidref_is_kernel_thread(const PidRef *pid) {
|
||||
return result;
|
||||
}
|
||||
|
||||
int get_process_capeff(pid_t pid, char **ret) {
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(pid >= 0);
|
||||
assert(ret);
|
||||
|
||||
p = procfs_file_alloca(pid, "status");
|
||||
|
||||
r = get_proc_field(p, "CapEff", WHITESPACE, ret);
|
||||
if (r == -ENOENT)
|
||||
return -ESRCH;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int get_process_link_contents(pid_t pid, const char *proc_file, char **ret) {
|
||||
const char *p;
|
||||
int r;
|
||||
|
@ -50,7 +50,6 @@ int get_process_exe(pid_t pid, char **ret);
|
||||
int pid_get_uid(pid_t pid, uid_t *ret);
|
||||
int pidref_get_uid(const PidRef *pid, uid_t *ret);
|
||||
int get_process_gid(pid_t pid, gid_t *ret);
|
||||
int get_process_capeff(pid_t pid, char **ret);
|
||||
int get_process_cwd(pid_t pid, char **ret);
|
||||
int get_process_root(pid_t pid, char **ret);
|
||||
int get_process_environ(pid_t pid, char **ret);
|
||||
|
@ -85,7 +85,9 @@ static int device_set_sysfs(Device *d, const char *sysfs) {
|
||||
Unit *u = UNIT(ASSERT_PTR(d));
|
||||
int r;
|
||||
|
||||
if (streq_ptr(d->sysfs, sysfs))
|
||||
assert(sysfs);
|
||||
|
||||
if (path_equal(d->sysfs, sysfs))
|
||||
return 0;
|
||||
|
||||
Hashmap **devices = &u->manager->devices_by_sysfs;
|
||||
@ -332,6 +334,20 @@ static void device_catchup(Unit *u) {
|
||||
Device *d = ASSERT_PTR(DEVICE(u));
|
||||
|
||||
/* Second, let's update the state with the enumerated state */
|
||||
|
||||
/* If Device.found (set from Device.deserialized_found) does not have DEVICE_FOUND_UDEV, and the
|
||||
* device has not been processed by udevd while enumeration, it indicates the unit was never active
|
||||
* before reexecution, hence we can safely drop the flag from Device.enumerated_found. The device
|
||||
* will be set up later when udev finishes processing (see also comment in
|
||||
* device_setup_devlink_unit_one()).
|
||||
*
|
||||
* NB: 💣💣💣 If Device.found already contains udev, i.e. the unit was fully ready before
|
||||
* reexecution, do not unset the flag. Otherwise, e.g. if systemd-udev-trigger.service is started
|
||||
* just before reexec, reload, and so on, devices being reprocessed (carrying ID_PROCESSING=1
|
||||
* property) on enumeration and will enter dead state. See issue #35329. */
|
||||
if (!FLAGS_SET(d->found, DEVICE_FOUND_UDEV) && !d->processed)
|
||||
d->enumerated_found &= ~DEVICE_FOUND_UDEV;
|
||||
|
||||
device_update_found_one(d, d->enumerated_found, _DEVICE_FOUND_MASK);
|
||||
}
|
||||
|
||||
@ -777,8 +793,16 @@ static int device_setup_devlink_unit_one(Manager *m, const char *devlink, Set **
|
||||
assert(ready_units);
|
||||
assert(not_ready_units);
|
||||
|
||||
if (sd_device_new_from_devname(&dev, devlink) >= 0 && device_is_ready(dev))
|
||||
if (sd_device_new_from_devname(&dev, devlink) >= 0 && device_is_ready(dev)) {
|
||||
if (MANAGER_IS_RUNNING(m) && device_is_processed(dev) <= 0)
|
||||
/* The device is being processed by udevd. We will receive relevant uevent for the
|
||||
* device later when completed. Let's ignore the device now. */
|
||||
return 0;
|
||||
|
||||
/* Note, even if the device is being processed by udevd, setup the unit on enumerate.
|
||||
* See also the comments in device_catchup(). */
|
||||
return device_setup_unit(m, dev, devlink, /* main = */ false, ready_units);
|
||||
}
|
||||
|
||||
/* the devlink is already removed or not ready */
|
||||
if (device_by_path(m, devlink, &u) < 0)
|
||||
@ -874,14 +898,15 @@ static int device_setup_extra_units(Manager *m, sd_device *dev, Set **ready_unit
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_setup_units(Manager *m, sd_device *dev, Set **ready_units, Set **not_ready_units) {
|
||||
static int device_setup_units(Manager *m, sd_device *dev, Set **ret_ready_units, Set **ret_not_ready_units) {
|
||||
_cleanup_set_free_ Set *ready_units = NULL, *not_ready_units = NULL;
|
||||
const char *syspath, *devname = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(dev);
|
||||
assert(ready_units);
|
||||
assert(not_ready_units);
|
||||
assert(ret_ready_units);
|
||||
assert(ret_not_ready_units);
|
||||
|
||||
r = sd_device_get_syspath(dev, &syspath);
|
||||
if (r < 0)
|
||||
@ -901,13 +926,13 @@ static int device_setup_units(Manager *m, sd_device *dev, Set **ready_units, Set
|
||||
/* Add the main unit named after the syspath. If this one fails, don't bother with the rest,
|
||||
* as this one shall be the main device unit the others just follow. (Compare with how
|
||||
* device_following() is implemented, see below, which looks for the sysfs device.) */
|
||||
r = device_setup_unit(m, dev, syspath, /* main = */ true, ready_units);
|
||||
r = device_setup_unit(m, dev, syspath, /* main = */ true, &ready_units);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Add an additional unit for the device node */
|
||||
if (sd_device_get_devname(dev, &devname) >= 0)
|
||||
(void) device_setup_unit(m, dev, devname, /* main = */ false, ready_units);
|
||||
(void) device_setup_unit(m, dev, devname, /* main = */ false, &ready_units);
|
||||
|
||||
} else {
|
||||
Unit *u;
|
||||
@ -915,28 +940,30 @@ static int device_setup_units(Manager *m, sd_device *dev, Set **ready_units, Set
|
||||
/* If the device exists but not ready, then save the units and unset udev bits later. */
|
||||
|
||||
if (device_by_path(m, syspath, &u) >= 0) {
|
||||
r = set_ensure_put(not_ready_units, NULL, DEVICE(u));
|
||||
r = set_ensure_put(¬_ready_units, NULL, DEVICE(u));
|
||||
if (r < 0)
|
||||
log_unit_debug_errno(u, r, "Failed to store unit, ignoring: %m");
|
||||
}
|
||||
|
||||
if (sd_device_get_devname(dev, &devname) >= 0 &&
|
||||
device_by_path(m, devname, &u) >= 0) {
|
||||
r = set_ensure_put(not_ready_units, NULL, DEVICE(u));
|
||||
r = set_ensure_put(¬_ready_units, NULL, DEVICE(u));
|
||||
if (r < 0)
|
||||
log_unit_debug_errno(u, r, "Failed to store unit, ignoring: %m");
|
||||
}
|
||||
}
|
||||
|
||||
/* Next, add/update additional .device units point to aliases and symlinks. */
|
||||
(void) device_setup_extra_units(m, dev, ready_units, not_ready_units);
|
||||
(void) device_setup_extra_units(m, dev, &ready_units, ¬_ready_units);
|
||||
|
||||
/* Safety check: no unit should be in ready_units and not_ready_units simultaneously. */
|
||||
Unit *u;
|
||||
SET_FOREACH(u, *not_ready_units)
|
||||
if (set_remove(*ready_units, u))
|
||||
SET_FOREACH(u, not_ready_units)
|
||||
if (set_remove(ready_units, u))
|
||||
log_unit_error(u, "Cannot activate and deactivate the unit simultaneously. Deactivating.");
|
||||
|
||||
*ret_ready_units = TAKE_PTR(ready_units);
|
||||
*ret_not_ready_units = TAKE_PTR(not_ready_units);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1046,13 +1073,32 @@ static void device_enumerate(Manager *m) {
|
||||
|
||||
FOREACH_DEVICE(e, dev) {
|
||||
_cleanup_set_free_ Set *ready_units = NULL, *not_ready_units = NULL;
|
||||
const char *syspath;
|
||||
bool processed;
|
||||
Device *d;
|
||||
|
||||
r = sd_device_get_syspath(dev, &syspath);
|
||||
if (r < 0) {
|
||||
log_device_debug_errno(dev, r, "Failed to get syspath of enumerated device, ignoring: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
r = device_is_processed(dev);
|
||||
if (r < 0)
|
||||
log_device_debug_errno(dev, r, "Failed to check if device is processed by udevd, assuming not: %m");
|
||||
processed = r > 0;
|
||||
|
||||
if (device_setup_units(m, dev, &ready_units, ¬_ready_units) < 0)
|
||||
continue;
|
||||
|
||||
SET_FOREACH(d, ready_units)
|
||||
SET_FOREACH(d, ready_units) {
|
||||
device_update_found_one(d, DEVICE_FOUND_UDEV, DEVICE_FOUND_UDEV);
|
||||
|
||||
/* Why we need to check the syspath here? Because the device unit may be generated by
|
||||
* a devlink, and the syspath may be different from the one of the original device. */
|
||||
if (path_equal(d->sysfs, syspath))
|
||||
d->processed = processed;
|
||||
}
|
||||
SET_FOREACH(d, not_ready_units)
|
||||
device_update_found_one(d, DEVICE_NOT_FOUND, DEVICE_FOUND_UDEV);
|
||||
}
|
||||
@ -1097,7 +1143,6 @@ static void device_remove_old_on_move(Manager *m, sd_device *dev) {
|
||||
}
|
||||
|
||||
static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *userdata) {
|
||||
_cleanup_set_free_ Set *ready_units = NULL, *not_ready_units = NULL;
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
sd_device_action_t action;
|
||||
const char *sysfs;
|
||||
@ -1150,6 +1195,7 @@ static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *
|
||||
* change events */
|
||||
ready = device_is_ready(dev);
|
||||
|
||||
_cleanup_set_free_ Set *ready_units = NULL, *not_ready_units = NULL;
|
||||
(void) device_setup_units(m, dev, &ready_units, ¬_ready_units);
|
||||
|
||||
if (action == SD_DEVICE_REMOVE) {
|
||||
|
@ -29,7 +29,9 @@ struct Device {
|
||||
|
||||
DeviceState state, deserialized_state;
|
||||
DeviceFound found, deserialized_found, enumerated_found;
|
||||
|
||||
bool processed; /* Whether udevd has done processing the device, i.e. the device has database and
|
||||
* ID_PROCESSING=1 udev property is not set. This is used only by enumeration and
|
||||
* subsequent catchup process. */
|
||||
bool bind_mounts;
|
||||
|
||||
/* The SYSTEMD_WANTS udev property for this device the last time we saw it */
|
||||
|
@ -117,10 +117,9 @@ int exec_context_put_load_credential(ExecContext *c, const char *id, const char
|
||||
return -ENOMEM;
|
||||
|
||||
r = hashmap_ensure_put(&c->load_credentials, &exec_load_credential_hash_ops, lc->id, lc);
|
||||
if (r < 0) {
|
||||
assert(r != -EEXIST);
|
||||
assert(r != -EEXIST);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
TAKE_PTR(lc);
|
||||
}
|
||||
@ -167,10 +166,9 @@ int exec_context_put_set_credential(
|
||||
return -ENOMEM;
|
||||
|
||||
r = hashmap_ensure_put(&c->set_credentials, &exec_set_credential_hash_ops, sc->id, sc);
|
||||
if (r < 0) {
|
||||
assert(r != -EEXIST);
|
||||
assert(r != -EEXIST);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
TAKE_PTR(sc);
|
||||
}
|
||||
@ -193,19 +191,22 @@ int exec_context_put_import_credential(ExecContext *c, const char *glob, const c
|
||||
|
||||
*ic = (ExecImportCredential) {
|
||||
.glob = strdup(glob),
|
||||
.rename = rename ? strdup(rename) : NULL,
|
||||
};
|
||||
if (!ic->glob || (rename && !ic->rename))
|
||||
if (!ic->glob)
|
||||
return -ENOMEM;
|
||||
if (rename) {
|
||||
ic->rename = strdup(rename);
|
||||
if (!ic->rename)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (ordered_set_contains(c->import_credentials, ic))
|
||||
return 0;
|
||||
|
||||
r = ordered_set_ensure_put(&c->import_credentials, &exec_import_credential_hash_ops, ic);
|
||||
if (r < 0) {
|
||||
assert(r != -EEXIST);
|
||||
assert(r != -EEXIST);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
TAKE_PTR(ic);
|
||||
|
||||
@ -383,30 +384,46 @@ typedef enum CredentialSearchPath {
|
||||
_CREDENTIAL_SEARCH_PATH_INVALID = -EINVAL,
|
||||
} CredentialSearchPath;
|
||||
|
||||
static char** credential_search_path(const ExecParameters *params, CredentialSearchPath path) {
|
||||
static int credential_search_path(const ExecParameters *params, CredentialSearchPath path, char ***ret) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
int r;
|
||||
|
||||
assert(params);
|
||||
assert(path >= 0 && path < _CREDENTIAL_SEARCH_PATH_MAX);
|
||||
assert(ret);
|
||||
|
||||
/* Assemble a search path to find credentials in. For non-encrypted credentials, We'll look in
|
||||
* /etc/credstore/ (and similar directories in /usr/lib/ + /run/). If we're looking for encrypted
|
||||
* credentials, we'll look in /etc/credstore.encrypted/ (and similar dirs). */
|
||||
|
||||
if (IN_SET(path, CREDENTIAL_SEARCH_PATH_ENCRYPTED, CREDENTIAL_SEARCH_PATH_ALL)) {
|
||||
if (strv_extend(&l, params->received_encrypted_credentials_directory) < 0)
|
||||
return NULL;
|
||||
r = strv_extend(&l, params->received_encrypted_credentials_directory);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore.encrypted"), /* filter_duplicates= */ true) < 0)
|
||||
return NULL;
|
||||
_cleanup_strv_free_ char **add = NULL;
|
||||
r = credential_store_path_encrypted(params->runtime_scope, &add);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_extend_strv_consume(&l, TAKE_PTR(add), /* filter_duplicates= */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (IN_SET(path, CREDENTIAL_SEARCH_PATH_TRUSTED, CREDENTIAL_SEARCH_PATH_ALL)) {
|
||||
if (strv_extend(&l, params->received_credentials_directory) < 0)
|
||||
return NULL;
|
||||
r = strv_extend(&l, params->received_credentials_directory);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore"), /* filter_duplicates= */ true) < 0)
|
||||
return NULL;
|
||||
_cleanup_strv_free_ char **add = NULL;
|
||||
r = credential_store_path(params->runtime_scope, &add);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_extend_strv_consume(&l, TAKE_PTR(add), /* filter_duplicates= */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
@ -414,7 +431,8 @@ static char** credential_search_path(const ExecParameters *params, CredentialSea
|
||||
log_debug("Credential search path is: %s", strempty(t));
|
||||
}
|
||||
|
||||
return TAKE_PTR(l);
|
||||
*ret = TAKE_PTR(l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct load_cred_args {
|
||||
@ -445,15 +463,38 @@ static int maybe_decrypt_and_write_credential(
|
||||
assert(data || size == 0);
|
||||
|
||||
if (args->encrypted) {
|
||||
r = decrypt_credential_and_warn(
|
||||
id,
|
||||
now(CLOCK_REALTIME),
|
||||
/* tpm2_device= */ NULL,
|
||||
/* tpm2_signature_path= */ NULL,
|
||||
getuid(),
|
||||
&IOVEC_MAKE(data, size),
|
||||
CREDENTIAL_ANY_SCOPE,
|
||||
&plaintext);
|
||||
switch (args->params->runtime_scope) {
|
||||
|
||||
case RUNTIME_SCOPE_SYSTEM:
|
||||
/* In system mode talk directly to the TPM */
|
||||
r = decrypt_credential_and_warn(
|
||||
id,
|
||||
now(CLOCK_REALTIME),
|
||||
/* tpm2_device= */ NULL,
|
||||
/* tpm2_signature_path= */ NULL,
|
||||
getuid(),
|
||||
&IOVEC_MAKE(data, size),
|
||||
CREDENTIAL_ANY_SCOPE,
|
||||
&plaintext);
|
||||
break;
|
||||
|
||||
case RUNTIME_SCOPE_USER:
|
||||
/* In per user mode we'll not have access to the machine secret, nor to the TPM (most
|
||||
* likely), hence go via the IPC service instead. Do this if we are run in root's
|
||||
* per-user invocation too, to minimize differences and because isolating this logic
|
||||
* into a separate process is generally a good thing anyway. */
|
||||
r = ipc_decrypt_credential(
|
||||
id,
|
||||
now(CLOCK_REALTIME),
|
||||
getuid(),
|
||||
&IOVEC_MAKE(data, size),
|
||||
/* flags= */ 0, /* only allow user creds in user scope */
|
||||
&plaintext);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -611,9 +652,9 @@ static int load_credential(
|
||||
* directory we received ourselves. We don't support the AF_UNIX stuff in this mode, since we
|
||||
* are operating on a credential store, i.e. this is guaranteed to be regular files. */
|
||||
|
||||
search_path = credential_search_path(args->params, CREDENTIAL_SEARCH_PATH_ALL);
|
||||
if (!search_path)
|
||||
return -ENOMEM;
|
||||
r = credential_search_path(args->params, CREDENTIAL_SEARCH_PATH_ALL, &search_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
missing_ok = true;
|
||||
} else
|
||||
@ -797,9 +838,9 @@ static int acquire_credentials(
|
||||
ORDERED_SET_FOREACH(ic, context->import_credentials) {
|
||||
_cleanup_free_ char **search_path = NULL;
|
||||
|
||||
search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_TRUSTED);
|
||||
if (!search_path)
|
||||
return -ENOMEM;
|
||||
r = credential_search_path(params, CREDENTIAL_SEARCH_PATH_TRUSTED, &search_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
args.encrypted = false;
|
||||
|
||||
@ -811,9 +852,10 @@ static int acquire_credentials(
|
||||
return r;
|
||||
|
||||
search_path = strv_free(search_path);
|
||||
search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ENCRYPTED);
|
||||
if (!search_path)
|
||||
return -ENOMEM;
|
||||
|
||||
r = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ENCRYPTED, &search_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
args.encrypted = true;
|
||||
|
||||
|
@ -3,9 +3,11 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bitfield.h"
|
||||
#include "creds-util.h"
|
||||
#include "dropin.h"
|
||||
#include "errno-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "generator.h"
|
||||
@ -27,6 +29,7 @@ static char **arg_wants = NULL;
|
||||
static bool arg_debug_shell = false;
|
||||
static char *arg_debug_tty = NULL;
|
||||
static char *arg_default_debug_tty = NULL;
|
||||
static uint32_t arg_breakpoints = 0;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_default_unit, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_mask, strv_freep);
|
||||
@ -34,6 +37,91 @@ STATIC_DESTRUCTOR_REGISTER(arg_wants, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_debug_tty, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_default_debug_tty, freep);
|
||||
|
||||
typedef enum BreakpointType {
|
||||
BREAKPOINT_PRE_UDEV,
|
||||
BREAKPOINT_PRE_BASIC,
|
||||
BREAKPOINT_PRE_SYSROOT_MOUNT,
|
||||
BREAKPOINT_PRE_SWITCH_ROOT,
|
||||
_BREAKPOINT_TYPE_MAX,
|
||||
_BREAKPOINT_TYPE_INVALID = -EINVAL,
|
||||
} BreakpointType;
|
||||
|
||||
typedef enum BreakpointValidity {
|
||||
BREAKPOINT_DEFAULT = 1 << 0,
|
||||
BREAKPOINT_IN_INITRD = 1 << 1,
|
||||
BREAKPOINT_ON_HOST = 1 << 2,
|
||||
} BreakpointValidity;
|
||||
|
||||
typedef struct BreakpointInfo {
|
||||
BreakpointType type;
|
||||
const char *name;
|
||||
const char *unit;
|
||||
BreakpointValidity validity;
|
||||
} BreakpointInfo;
|
||||
|
||||
static const struct BreakpointInfo breakpoint_info_table[_BREAKPOINT_TYPE_MAX] = {
|
||||
{ BREAKPOINT_PRE_UDEV, "pre-udev", "breakpoint-pre-udev.service", BREAKPOINT_IN_INITRD | BREAKPOINT_ON_HOST },
|
||||
{ BREAKPOINT_PRE_BASIC, "pre-basic", "breakpoint-pre-basic.service", BREAKPOINT_IN_INITRD | BREAKPOINT_ON_HOST },
|
||||
{ BREAKPOINT_PRE_SYSROOT_MOUNT, "pre-mount", "breakpoint-pre-mount.service", BREAKPOINT_IN_INITRD },
|
||||
{ BREAKPOINT_PRE_SWITCH_ROOT, "pre-switch-root", "breakpoint-pre-switch-root.service", BREAKPOINT_IN_INITRD | BREAKPOINT_DEFAULT },
|
||||
};
|
||||
|
||||
static BreakpointType parse_breakpoint_from_string_one(const char *s) {
|
||||
assert(s);
|
||||
|
||||
FOREACH_ARRAY(i, breakpoint_info_table, ELEMENTSOF(breakpoint_info_table))
|
||||
if (streq(i->name, s))
|
||||
return i->type;
|
||||
|
||||
return _BREAKPOINT_TYPE_INVALID;
|
||||
}
|
||||
|
||||
static int parse_breakpoint_from_string(const char *s, uint32_t *ret_breakpoints) {
|
||||
uint32_t breakpoints = 0;
|
||||
int r;
|
||||
|
||||
assert(ret_breakpoints);
|
||||
|
||||
/* Empty value? set default breakpoint */
|
||||
if (isempty(s)) {
|
||||
if (in_initrd()) {
|
||||
FOREACH_ARRAY(i, breakpoint_info_table, ELEMENTSOF(breakpoint_info_table))
|
||||
if (i->validity & BREAKPOINT_DEFAULT) {
|
||||
breakpoints |= 1 << i->type;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
log_warning("No default breakpoint defined on the host, ignoring breakpoint request from kernel command line.");
|
||||
} else
|
||||
for (;;) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
BreakpointType tt;
|
||||
|
||||
r = extract_first_word(&s, &t, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
tt = parse_breakpoint_from_string_one(t);
|
||||
if (tt < 0) {
|
||||
log_warning("Invalid breakpoint value '%s', ignoring.", t);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_initrd() && !FLAGS_SET(breakpoint_info_table[tt].validity, BREAKPOINT_IN_INITRD))
|
||||
log_warning("Breakpoint '%s' not valid in the initrd, ignoring.", t);
|
||||
else if (!in_initrd() && !FLAGS_SET(breakpoint_info_table[tt].validity, BREAKPOINT_ON_HOST))
|
||||
log_warning("Breakpoint '%s' not valid on the host, ignoring.", t);
|
||||
else
|
||||
breakpoints |= 1 << tt;
|
||||
}
|
||||
|
||||
*ret_breakpoints = breakpoints;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
|
||||
int r;
|
||||
|
||||
@ -88,6 +176,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
|
||||
|
||||
return free_and_strdup_warn(&arg_default_unit, value);
|
||||
|
||||
} else if (streq(key, "systemd.break")) {
|
||||
uint32_t breakpoints = 0;
|
||||
|
||||
r = parse_breakpoint_from_string(value, &breakpoints);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to parse breakpoint value '%s': %m", value);
|
||||
|
||||
arg_breakpoints |= breakpoints;
|
||||
|
||||
} else if (!value) {
|
||||
const char *target;
|
||||
|
||||
@ -269,6 +366,10 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
|
||||
RET_GATHER(r, install_debug_shell_dropin());
|
||||
}
|
||||
|
||||
BIT_FOREACH(i, arg_breakpoints)
|
||||
if (strv_extend(&arg_wants, breakpoint_info_table[i].unit) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (get_credentials_dir(&credentials_dir) >= 0)
|
||||
RET_GATHER(r, process_unit_credentials(credentials_dir));
|
||||
|
||||
|
@ -61,7 +61,7 @@ static void get_chid(const char16_t *const smbios_fields[static _CHID_SMBIOS_FIE
|
||||
ret_chid->Data4[0] = (ret_chid->Data4[0] & UINT8_C(0x3f)) | UINT8_C(0x80);
|
||||
}
|
||||
|
||||
static const uint32_t chid_smbios_table[CHID_TYPES_MAX] = {
|
||||
const uint32_t chid_smbios_table[CHID_TYPES_MAX] = {
|
||||
[3] = (UINT32_C(1) << CHID_SMBIOS_MANUFACTURER) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_FAMILY) |
|
||||
(UINT32_C(1) << CHID_SMBIOS_PRODUCT_NAME) |
|
||||
|
@ -23,5 +23,7 @@ typedef enum ChidSmbiosFields {
|
||||
_CHID_SMBIOS_FIELDS_MAX,
|
||||
} ChidSmbiosFields;
|
||||
|
||||
extern const uint32_t chid_smbios_table[CHID_TYPES_MAX];
|
||||
|
||||
/* CHID (also called HWID by fwupd) is described at https://github.com/fwupd/fwupd/blob/main/docs/hwids.md */
|
||||
void chid_calculate(const char16_t *const smbios_fields[static _CHID_SMBIOS_FIELDS_MAX], EFI_GUID ret_chids[static CHID_TYPES_MAX]);
|
||||
|
@ -132,6 +132,7 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
|
||||
.log_level_max = -1,
|
||||
.log_ratelimit_interval = s->ratelimit_interval,
|
||||
.log_ratelimit_burst = s->ratelimit_burst,
|
||||
.capability_quintet = CAPABILITY_QUINTET_NULL,
|
||||
};
|
||||
|
||||
r = hashmap_ensure_put(&s->client_contexts, NULL, PID_TO_PTR(pid), c);
|
||||
@ -154,7 +155,6 @@ static void client_context_reset(Server *s, ClientContext *c) {
|
||||
c->comm = mfree(c->comm);
|
||||
c->exe = mfree(c->exe);
|
||||
c->cmdline = mfree(c->cmdline);
|
||||
c->capeff = mfree(c->capeff);
|
||||
|
||||
c->auditid = AUDIT_SESSION_INVALID;
|
||||
c->loginuid = UID_INVALID;
|
||||
@ -184,6 +184,8 @@ static void client_context_reset(Server *s, ClientContext *c) {
|
||||
|
||||
c->log_filter_allowed_patterns = set_free_free(c->log_filter_allowed_patterns);
|
||||
c->log_filter_denied_patterns = set_free_free(c->log_filter_denied_patterns);
|
||||
|
||||
c->capability_quintet = CAPABILITY_QUINTET_NULL;
|
||||
}
|
||||
|
||||
static ClientContext* client_context_free(Server *s, ClientContext *c) {
|
||||
@ -233,8 +235,7 @@ static void client_context_read_basic(ClientContext *c) {
|
||||
if (pid_get_cmdline(c->pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &t) >= 0)
|
||||
free_and_replace(c->cmdline, t);
|
||||
|
||||
if (get_process_capeff(c->pid, &t) >= 0)
|
||||
free_and_replace(c->capeff, t);
|
||||
(void) pidref_get_capability(&PIDREF_MAKE_FROM_PID(c->pid), &c->capability_quintet);
|
||||
}
|
||||
|
||||
static int client_context_read_label(
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "sd-id128.h"
|
||||
|
||||
#include "capability-util.h"
|
||||
#include "set.h"
|
||||
#include "time-util.h"
|
||||
|
||||
@ -27,7 +28,7 @@ struct ClientContext {
|
||||
char *comm;
|
||||
char *exe;
|
||||
char *cmdline;
|
||||
char *capeff;
|
||||
CapabilityQuintet capability_quintet;
|
||||
|
||||
uint32_t auditid;
|
||||
uid_t loginuid;
|
||||
|
@ -1109,7 +1109,7 @@ static void server_dispatch_message_real(
|
||||
* Let's use a heap allocation for this one. */
|
||||
cmdline1 = set_iovec_string_field(iovec, &n, "_CMDLINE=", c->cmdline);
|
||||
|
||||
IOVEC_ADD_STRING_FIELD(iovec, n, c->capeff, "_CAP_EFFECTIVE"); /* Read from /proc/.../status */
|
||||
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->capability_quintet.effective, uint64_t, capability_is_set, "%" PRIx64, "_CAP_EFFECTIVE");
|
||||
IOVEC_ADD_SIZED_FIELD(iovec, n, c->label, c->label_size, "_SELINUX_CONTEXT");
|
||||
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "_AUDIT_SESSION");
|
||||
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->loginuid, uid_t, uid_is_valid, UID_FMT, "_AUDIT_LOGINUID");
|
||||
@ -1144,7 +1144,7 @@ static void server_dispatch_message_real(
|
||||
if (o->cmdline)
|
||||
cmdline2 = set_iovec_string_field(iovec, &n, "OBJECT_CMDLINE=", o->cmdline);
|
||||
|
||||
IOVEC_ADD_STRING_FIELD(iovec, n, o->capeff, "OBJECT_CAP_EFFECTIVE");
|
||||
IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->capability_quintet.effective, uint64_t, capability_is_set, "%" PRIx64, "OBJECT_CAP_EFFECTIVE");
|
||||
IOVEC_ADD_SIZED_FIELD(iovec, n, o->label, o->label_size, "OBJECT_SELINUX_CONTEXT");
|
||||
IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "OBJECT_AUDIT_SESSION");
|
||||
IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->loginuid, uid_t, uid_is_valid, UID_FMT, "OBJECT_AUDIT_LOGINUID");
|
||||
|
@ -84,3 +84,19 @@ static inline char** generator_binary_paths(RuntimeScope runtime_scope) {
|
||||
static inline char** env_generator_binary_paths(RuntimeScope runtime_scope) {
|
||||
return generator_binary_paths_internal(runtime_scope, true);
|
||||
}
|
||||
|
||||
static inline int credential_store_path(RuntimeScope runtime_scope, char ***ret) {
|
||||
return sd_path_lookup_strv(
|
||||
runtime_scope == RUNTIME_SCOPE_SYSTEM ?
|
||||
SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE : SD_PATH_USER_SEARCH_CREDENTIAL_STORE,
|
||||
/* suffix= */ NULL,
|
||||
ret);
|
||||
}
|
||||
|
||||
static inline int credential_store_path_encrypted(RuntimeScope runtime_scope, char ***ret) {
|
||||
return sd_path_lookup_strv(
|
||||
runtime_scope == RUNTIME_SCOPE_SYSTEM ?
|
||||
SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE_ENCRYPTED : SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED,
|
||||
/* suffix= */ NULL,
|
||||
ret);
|
||||
}
|
||||
|
@ -36,7 +36,12 @@ static int from_environment(const char *envname, const char *fallback, const cha
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int from_home_dir(const char *envname, const char *suffix, char **buffer, const char **ret) {
|
||||
static int from_home_dir(
|
||||
const char *envname,
|
||||
const char *suffix,
|
||||
char **buffer,
|
||||
const char **ret) {
|
||||
|
||||
_cleanup_free_ char *h = NULL;
|
||||
int r;
|
||||
|
||||
@ -350,6 +355,30 @@ static int get_path(uint64_t type, char **buffer, const char **ret) {
|
||||
case SD_PATH_SYSTEMD_USER_ENVIRONMENT_GENERATOR:
|
||||
*ret = USER_ENV_GENERATOR_DIR;
|
||||
return 0;
|
||||
|
||||
case SD_PATH_SYSTEM_CREDENTIAL_STORE:
|
||||
*ret = "/etc/credstore";
|
||||
return 0;
|
||||
|
||||
case SD_PATH_SYSTEM_CREDENTIAL_STORE_ENCRYPTED:
|
||||
*ret = "/etc/credstore.encrypted";
|
||||
return 0;
|
||||
|
||||
case SD_PATH_USER_CREDENTIAL_STORE:
|
||||
r = xdg_user_config_dir("credstore", buffer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = *buffer;
|
||||
return 0;
|
||||
|
||||
case SD_PATH_USER_CREDENTIAL_STORE_ENCRYPTED:
|
||||
r = xdg_user_config_dir("credstore.encrypted", buffer);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = *buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
@ -366,12 +395,12 @@ static int get_path_alloc(uint64_t type, const char *suffix, char **ret) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (suffix) {
|
||||
if (!isempty(suffix)) {
|
||||
char *suffixed = path_join(p, suffix);
|
||||
if (!suffixed)
|
||||
return -ENOMEM;
|
||||
|
||||
path_simplify(suffixed);
|
||||
path_simplify_full(suffixed, PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
|
||||
|
||||
free_and_replace(buffer, suffixed);
|
||||
} else if (!buffer) {
|
||||
@ -601,8 +630,55 @@ static int get_search(uint64_t type, char ***ret) {
|
||||
case SD_PATH_SYSTEMD_SEARCH_NETWORK:
|
||||
return strv_from_nulstr(ret, NETWORK_DIRS_NULSTR);
|
||||
|
||||
case SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE:
|
||||
case SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE_ENCRYPTED: {
|
||||
const char *suffix =
|
||||
type == SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE_ENCRYPTED ? "credstore.encrypted" : "credstore";
|
||||
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
FOREACH_STRING(d, CONF_PATHS("")) {
|
||||
char *j = path_join(d, suffix);
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_consume(&l, TAKE_PTR(j));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case SD_PATH_USER_SEARCH_CREDENTIAL_STORE:
|
||||
case SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED: {
|
||||
const char *suffix =
|
||||
type == SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED ? "credstore.encrypted" : "credstore";
|
||||
|
||||
static const uint64_t dirs[] = {
|
||||
SD_PATH_USER_CONFIGURATION,
|
||||
SD_PATH_USER_RUNTIME,
|
||||
SD_PATH_USER_LIBRARY_PRIVATE,
|
||||
};
|
||||
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
FOREACH_ELEMENT(d, dirs) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
r = sd_path_lookup(*d, suffix, &p);
|
||||
if (r == -ENXIO)
|
||||
continue;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_consume(&l, TAKE_PTR(p));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(l);
|
||||
return 0;
|
||||
}}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@ -637,7 +713,7 @@ _public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***ret)
|
||||
if (!path_extend(i, suffix))
|
||||
return -ENOMEM;
|
||||
|
||||
path_simplify(*i);
|
||||
path_simplify_full(*i, PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(l);
|
||||
|
@ -1188,35 +1188,29 @@ static int bind_mount(int argc, char *argv[], void *userdata) {
|
||||
}
|
||||
|
||||
static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
|
||||
PTYForward ** forward = (PTYForward**) userdata;
|
||||
PTYForward *forward = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(forward);
|
||||
|
||||
if (*forward) {
|
||||
/* If the forwarder is already initialized, tell it to
|
||||
* exit on the next vhangup(), so that we still flush
|
||||
* out what might be queued and exit then. */
|
||||
|
||||
r = pty_forward_set_ignore_vhangup(*forward, false);
|
||||
if (r >= 0)
|
||||
return 0;
|
||||
/* Tell the forwarder to exit on the next vhangup(), so that we still flush out what might be queued
|
||||
* and exit then. */
|
||||
|
||||
r = pty_forward_set_ignore_vhangup(forward, false);
|
||||
if (r < 0) {
|
||||
/* On error, quit immediately. */
|
||||
log_error_errno(r, "Failed to set ignore_vhangup flag: %m");
|
||||
(void) sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* On error, or when the forwarder is not initialized yet, quit immediately */
|
||||
sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), EXIT_FAILURE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_forward(sd_event *event, PTYForward **forward, int master, PTYForwardFlags flags, const char *name) {
|
||||
char last_char = 0;
|
||||
bool machine_died;
|
||||
static int process_forward(sd_event *event, sd_bus_slot *machine_removed_slot, int master, PTYForwardFlags flags, const char *name) {
|
||||
int r;
|
||||
|
||||
assert(event);
|
||||
assert(machine_removed_slot);
|
||||
assert(master >= 0);
|
||||
assert(name);
|
||||
|
||||
@ -1231,24 +1225,21 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, PT
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enable SIGINT/SITERM handling: %m");
|
||||
|
||||
r = pty_forward_new(event, master, flags, forward);
|
||||
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
|
||||
r = pty_forward_new(event, master, flags, &forward);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create PTY forwarder: %m");
|
||||
|
||||
/* No userdata should not set previously. */
|
||||
assert_se(!sd_bus_slot_set_userdata(machine_removed_slot, forward));
|
||||
|
||||
r = sd_event_loop(event);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to run event loop: %m");
|
||||
|
||||
pty_forward_get_last_char(*forward, &last_char);
|
||||
|
||||
machine_died =
|
||||
bool machine_died =
|
||||
(flags & PTY_FORWARD_IGNORE_VHANGUP) &&
|
||||
pty_forward_get_ignore_vhangup(*forward) == 0;
|
||||
|
||||
*forward = pty_forward_free(*forward);
|
||||
|
||||
if (last_char != '\n')
|
||||
fputc('\n', stdout);
|
||||
pty_forward_get_ignore_vhangup(forward) == 0;
|
||||
|
||||
if (!arg_quiet) {
|
||||
if (machine_died)
|
||||
@ -1300,7 +1291,6 @@ static int parse_machine_uid(const char *spec, const char **machine, char **uid)
|
||||
static int login_machine(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
|
||||
_cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
|
||||
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||
int master = -1, r;
|
||||
@ -1334,7 +1324,7 @@ static int login_machine(int argc, char *argv[], void *userdata) {
|
||||
"member='MachineRemoved',"
|
||||
"arg0='", machine, "'");
|
||||
|
||||
r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward);
|
||||
r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to request machine removal match: %m");
|
||||
|
||||
@ -1346,13 +1336,12 @@ static int login_machine(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
return process_forward(event, &forward, master, PTY_FORWARD_IGNORE_VHANGUP, machine);
|
||||
return process_forward(event, slot, master, PTY_FORWARD_IGNORE_VHANGUP, machine);
|
||||
}
|
||||
|
||||
static int shell_machine(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
|
||||
_cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
|
||||
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||
int master = -1, r;
|
||||
@ -1396,7 +1385,7 @@ static int shell_machine(int argc, char *argv[], void *userdata) {
|
||||
"member='MachineRemoved',"
|
||||
"arg0='", machine, "'");
|
||||
|
||||
r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward);
|
||||
r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to request machine removal match: %m");
|
||||
|
||||
@ -1426,7 +1415,7 @@ static int shell_machine(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
return process_forward(event, &forward, master, 0, machine);
|
||||
return process_forward(event, slot, master, /* flags = */ 0, machine);
|
||||
}
|
||||
|
||||
static int normalize_nspawn_filename(const char *name, char **ret_file) {
|
||||
|
@ -252,7 +252,7 @@ static void link_free_engines(Link *link) {
|
||||
static Link *link_free(Link *link) {
|
||||
assert(link);
|
||||
|
||||
(void) sysctl_clear_link_shadows(link);
|
||||
(void) link_clear_sysctl_shadows(link);
|
||||
|
||||
link_ntp_settings_clear(link);
|
||||
link_dns_settings_clear(link);
|
||||
|
@ -666,7 +666,7 @@ Manager* manager_free(Manager *m) {
|
||||
if (!m)
|
||||
return NULL;
|
||||
|
||||
sysctl_remove_monitor(m);
|
||||
manager_remove_sysctl_monitor(m);
|
||||
|
||||
free(m->state_file);
|
||||
|
||||
@ -742,7 +742,7 @@ int manager_start(Manager *m) {
|
||||
|
||||
log_debug("Starting...");
|
||||
|
||||
(void) sysctl_add_monitor(m);
|
||||
(void) manager_install_sysctl_monitor(m);
|
||||
|
||||
/* Loading BPF programs requires CAP_SYS_ADMIN and CAP_BPF.
|
||||
* Drop the capabilities here, regardless if the load succeeds or not. */
|
||||
|
@ -47,7 +47,7 @@ static int sysctl_event_handler(void *ctx, void *data, size_t data_sz) {
|
||||
* so do it only in case of a fatal error like a version mismatch. */
|
||||
if (we->version != 1)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unexpected sysctl event, disabling sysctl monitoring: %d", we->version);
|
||||
"Unexpected sysctl event, disabling sysctl monitoring: %d", we->version);
|
||||
|
||||
if (we->errorcode != 0) {
|
||||
log_warning_errno(we->errorcode, "Sysctl monitor BPF returned error: %m");
|
||||
@ -56,7 +56,7 @@ static int sysctl_event_handler(void *ctx, void *data, size_t data_sz) {
|
||||
|
||||
path = path_join("/proc/sys", we->path);
|
||||
if (!path) {
|
||||
log_oom();
|
||||
log_oom_warning();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ static int on_ringbuf_io(sd_event_source *s, int fd, uint32_t revents, void *use
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sysctl_add_monitor(Manager *manager) {
|
||||
int manager_install_sysctl_monitor(Manager *manager) {
|
||||
_cleanup_(sysctl_monitor_bpf_freep) struct sysctl_monitor_bpf *obj = NULL;
|
||||
_cleanup_(bpf_link_freep) struct bpf_link *sysctl_link = NULL;
|
||||
_cleanup_(bpf_ring_buffer_freep) struct ring_buffer *sysctl_buffer = NULL;
|
||||
@ -102,10 +102,10 @@ int sysctl_add_monitor(Manager *manager) {
|
||||
assert(manager);
|
||||
|
||||
r = dlopen_bpf();
|
||||
if (r < 0) {
|
||||
log_info_errno(r, "sysctl monitor disabled, as BPF support is not available.");
|
||||
return 0;
|
||||
}
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
return log_debug_errno(r, "sysctl monitor disabled, as BPF support is not available.");
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to load libbpf, not installing sysctl monitor: %m");
|
||||
|
||||
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup);
|
||||
if (r < 0)
|
||||
@ -113,13 +113,12 @@ int sysctl_add_monitor(Manager *manager) {
|
||||
|
||||
root_cgroup_fd = cg_path_open(SYSTEMD_CGROUP_CONTROLLER, "/");
|
||||
if (root_cgroup_fd < 0)
|
||||
return log_warning_errno(root_cgroup_fd, "Failed to open cgroup, ignoring: %m.");
|
||||
return log_warning_errno(root_cgroup_fd, "Failed to open cgroup, ignoring: %m");
|
||||
|
||||
obj = sysctl_monitor_bpf__open_and_load();
|
||||
if (!obj) {
|
||||
log_info_errno(errno, "Unable to load sysctl monitor BPF program, ignoring: %m.");
|
||||
return 0;
|
||||
}
|
||||
if (!obj)
|
||||
return log_full_errno(errno == EINVAL ? LOG_DEBUG : LOG_INFO, errno,
|
||||
"Unable to load sysctl monitor BPF program, ignoring: %m");
|
||||
|
||||
cgroup_fd = cg_path_open(SYSTEMD_CGROUP_CONTROLLER, cgroup);
|
||||
if (cgroup_fd < 0)
|
||||
@ -130,10 +129,8 @@ int sysctl_add_monitor(Manager *manager) {
|
||||
|
||||
sysctl_link = sym_bpf_program__attach_cgroup(obj->progs.sysctl_monitor, root_cgroup_fd);
|
||||
r = bpf_get_error_translated(sysctl_link);
|
||||
if (r < 0) {
|
||||
log_info_errno(r, "Unable to attach sysctl monitor BPF program to cgroup, ignoring: %m.");
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Unable to attach sysctl monitor BPF program to cgroup, ignoring: %m");
|
||||
|
||||
fd = sym_bpf_map__fd(obj->maps.written_sysctls);
|
||||
if (fd < 0)
|
||||
@ -160,7 +157,7 @@ int sysctl_add_monitor(Manager *manager) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sysctl_remove_monitor(Manager *manager) {
|
||||
void manager_remove_sysctl_monitor(Manager *manager) {
|
||||
assert(manager);
|
||||
|
||||
manager->sysctl_event_source = sd_event_source_disable_unref(manager->sysctl_event_source);
|
||||
@ -171,7 +168,7 @@ void sysctl_remove_monitor(Manager *manager) {
|
||||
manager->sysctl_shadow = hashmap_free(manager->sysctl_shadow);
|
||||
}
|
||||
|
||||
int sysctl_clear_link_shadows(Link *link) {
|
||||
int link_clear_sysctl_shadows(Link *link) {
|
||||
_cleanup_free_ char *ipv4 = NULL, *ipv6 = NULL;
|
||||
char *key = NULL, *value = NULL;
|
||||
|
||||
@ -188,7 +185,7 @@ int sysctl_clear_link_shadows(Link *link) {
|
||||
|
||||
HASHMAP_FOREACH_KEY(value, key, link->manager->sysctl_shadow)
|
||||
if (path_startswith(key, ipv4) || path_startswith(key, ipv6)) {
|
||||
assert_se(hashmap_remove_value(link->manager->sysctl_shadow, key, value) == value);
|
||||
assert_se(hashmap_remove_value(link->manager->sysctl_shadow, key, value));
|
||||
free(key);
|
||||
free(value);
|
||||
}
|
||||
|
@ -28,13 +28,13 @@ typedef enum IPReversePathFilter {
|
||||
} IPReversePathFilter;
|
||||
|
||||
#if HAVE_VMLINUX_H
|
||||
int sysctl_add_monitor(Manager *manager);
|
||||
void sysctl_remove_monitor(Manager *manager);
|
||||
int sysctl_clear_link_shadows(Link *link);
|
||||
int manager_install_sysctl_monitor(Manager *manager);
|
||||
void manager_remove_sysctl_monitor(Manager *manager);
|
||||
int link_clear_sysctl_shadows(Link *link);
|
||||
#else
|
||||
static inline int sysctl_add_monitor(Manager *manager) { return 0; }
|
||||
static inline void sysctl_remove_monitor(Manager *manager) { }
|
||||
static inline int sysctl_clear_link_shadows(Link *link) { return 0; }
|
||||
static inline int manager_install_sysctl_monitor(Manager *manager) { return 0; }
|
||||
static inline void manager_remove_sysctl_monitor(Manager *manager) { }
|
||||
static inline int link_clear_sysctl_shadows(Link *link) { return 0; }
|
||||
#endif
|
||||
|
||||
void manager_set_sysctl(Manager *manager);
|
||||
|
@ -475,7 +475,8 @@ int mount_sysfs(const char *dest, MountSettingsMask mount_settings) {
|
||||
if (!full)
|
||||
return log_oom();
|
||||
|
||||
(void) mkdir(full, 0755);
|
||||
if (mkdir(full, 0755) < 0 && errno != EEXIST)
|
||||
return log_error_errno(errno, "Failed to create directory '%s': %m", full);
|
||||
|
||||
if (FLAGS_SET(mount_settings, MOUNT_APPLY_APIVFS_RO))
|
||||
extra_flags |= MS_RDONLY;
|
||||
@ -1405,9 +1406,11 @@ done:
|
||||
#define NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS "/run/host/proc"
|
||||
#define NSPAWN_PRIVATE_FULLY_VISIBLE_SYSFS "/run/host/sys"
|
||||
|
||||
int pin_fully_visible_fs(void) {
|
||||
int pin_fully_visible_api_fs(void) {
|
||||
int r;
|
||||
|
||||
log_debug("Pinning fully visible API FS");
|
||||
|
||||
(void) mkdir_p(NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS, 0755);
|
||||
(void) mkdir_p(NSPAWN_PRIVATE_FULLY_VISIBLE_SYSFS, 0755);
|
||||
|
||||
@ -1422,7 +1425,7 @@ int pin_fully_visible_fs(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_wipe_fully_visible_fs(void) {
|
||||
static int do_wipe_fully_visible_api_fs(void) {
|
||||
if (umount2(NSPAWN_PRIVATE_FULLY_VISIBLE_PROCFS, MNT_DETACH) < 0)
|
||||
return log_error_errno(errno, "Failed to unmount temporary proc: %m");
|
||||
|
||||
@ -1438,10 +1441,12 @@ static int do_wipe_fully_visible_fs(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wipe_fully_visible_fs(int mntns_fd) {
|
||||
int wipe_fully_visible_api_fs(int mntns_fd) {
|
||||
_cleanup_close_ int orig_mntns_fd = -EBADF;
|
||||
int r, rr;
|
||||
|
||||
log_debug("Wiping fully visible API FS");
|
||||
|
||||
r = namespace_open(0,
|
||||
/* ret_pidns_fd = */ NULL,
|
||||
&orig_mntns_fd,
|
||||
@ -1459,7 +1464,7 @@ int wipe_fully_visible_fs(int mntns_fd) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enter mount namespace: %m");
|
||||
|
||||
rr = do_wipe_fully_visible_fs();
|
||||
rr = do_wipe_fully_visible_api_fs();
|
||||
|
||||
r = namespace_enter(/* pidns_fd = */ -EBADF,
|
||||
orig_mntns_fd,
|
||||
|
@ -73,5 +73,6 @@ int pivot_root_parse(char **pivot_root_new, char **pivot_root_old, const char *s
|
||||
int setup_pivot_root(const char *directory, const char *pivot_root_new, const char *pivot_root_old);
|
||||
|
||||
int tmpfs_patch_options(const char *options,uid_t uid_shift, const char *selinux_apifs_context, char **ret);
|
||||
int pin_fully_visible_fs(void);
|
||||
int wipe_fully_visible_fs(int mntns_fd);
|
||||
|
||||
int pin_fully_visible_api_fs(void);
|
||||
int wipe_fully_visible_api_fs(int mntns_fd);
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "copy.h"
|
||||
#include "cpu-set-util.h"
|
||||
#include "dev-setup.h"
|
||||
#include "devnum-util.h"
|
||||
#include "discover-image.h"
|
||||
#include "dissect-image.h"
|
||||
#include "env-util.h"
|
||||
@ -1250,7 +1251,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_uid_range = UINT32_C(0x10000);
|
||||
|
||||
} else if (streq(optarg, "identity")) {
|
||||
/* identity: User namespaces on, UID range is map the 0…0xFFFF range to
|
||||
/* identity: User namespaces on, UID range is map of the 0…0xFFFF range to
|
||||
* itself, i.e. we don't actually map anything, but do take benefit of
|
||||
* isolation of capability sets. */
|
||||
arg_userns_mode = USER_NAMESPACE_FIXED;
|
||||
@ -2323,7 +2324,7 @@ static int copy_devnode_one(const char *dest, const char *node, bool ignore_mkno
|
||||
return log_error_errno(r, "Failed to create '%s': %m", dn);
|
||||
|
||||
_cleanup_free_ char *sl = NULL;
|
||||
if (asprintf(&sl, "%s/%u:%u", dn, major(st.st_rdev), minor(st.st_rdev)) < 0)
|
||||
if (asprintf(&sl, "%s/" DEVNUM_FORMAT_STR, dn, DEVNUM_FORMAT_VAL(st.st_rdev)) < 0)
|
||||
return log_oom();
|
||||
|
||||
_cleanup_free_ char *prefixed = path_join(dest, sl);
|
||||
@ -2847,7 +2848,7 @@ static int reset_audit_loginuid(void) {
|
||||
if (!arg_privileged)
|
||||
return 0;
|
||||
|
||||
r = read_one_line_file("/proc/self/loginuid", &p);
|
||||
r = read_virtual_file("/proc/self/loginuid", SIZE_MAX, &p, /* ret_size= */ NULL);
|
||||
if (r == -ENOENT)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
@ -3254,6 +3255,7 @@ static int chase_and_update(char **p, unsigned flags) {
|
||||
}
|
||||
|
||||
static int determine_uid_shift(const char *directory) {
|
||||
assert(directory);
|
||||
|
||||
if (arg_userns_mode == USER_NAMESPACE_NO) {
|
||||
arg_uid_shift = 0;
|
||||
@ -3932,7 +3934,7 @@ static int outer_child(
|
||||
|
||||
_cleanup_(bind_user_context_freep) BindUserContext *bind_user_context = NULL;
|
||||
_cleanup_strv_free_ char **os_release_pairs = NULL;
|
||||
_cleanup_close_ int fd = -EBADF, mntns_fd = -EBADF;
|
||||
_cleanup_close_ int mntns_fd = -EBADF;
|
||||
bool idmap = false, enable_fuse;
|
||||
const char *p;
|
||||
pid_t pid;
|
||||
@ -4325,6 +4327,7 @@ static int outer_child(
|
||||
* visible. Hence there we do it the other way round: we first allocate a new set of namespaces
|
||||
* (and fork for it) for which we then mount sysfs/procfs, and only then switch root. */
|
||||
|
||||
_cleanup_close_ int notify_fd = -EBADF;
|
||||
if (arg_privileged) {
|
||||
/* Mark everything as shared so our mounts get propagated down. This is required to make new
|
||||
* bind mounts available in systemd services inside the container that create a new mount
|
||||
@ -4355,16 +4358,16 @@ static int outer_child(
|
||||
* Note, the inner child wouldn't be able to unmount the instances on its own since
|
||||
* it doesn't own the originating mount namespace. IOW, the outer child needs to do
|
||||
* this. */
|
||||
r = pin_fully_visible_fs();
|
||||
r = pin_fully_visible_api_fs();
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
fd = setup_notify_child(NULL);
|
||||
notify_fd = setup_notify_child(NULL);
|
||||
} else
|
||||
fd = setup_notify_child(directory);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
notify_fd = setup_notify_child(directory);
|
||||
if (notify_fd < 0)
|
||||
return notify_fd;
|
||||
|
||||
pid = raw_clone(SIGCHLD|CLONE_NEWNS|
|
||||
arg_clone_ns_flags |
|
||||
@ -4429,7 +4432,7 @@ static int outer_child(
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO),
|
||||
"Short write while sending machine ID.");
|
||||
|
||||
l = send_one_fd(fd_outer_socket, fd, 0);
|
||||
l = send_one_fd(fd_outer_socket, notify_fd, 0);
|
||||
if (l < 0)
|
||||
return log_error_errno(l, "Failed to send notify fd: %m");
|
||||
|
||||
@ -5623,7 +5626,7 @@ static int run_container(
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Child died too early.");
|
||||
|
||||
if (arg_userns_mode != USER_NAMESPACE_NO) {
|
||||
r = wipe_fully_visible_fs(mntns_fd);
|
||||
r = wipe_fully_visible_api_fs(mntns_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
mntns_fd = safe_close(mntns_fd);
|
||||
@ -5740,16 +5743,6 @@ static int run_container(
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to run event loop: %m");
|
||||
|
||||
if (forward) {
|
||||
char last_char = 0;
|
||||
|
||||
(void) pty_forward_get_last_char(forward, &last_char);
|
||||
forward = pty_forward_free(forward);
|
||||
|
||||
if (!arg_quiet && last_char != '\n')
|
||||
putc('\n', stdout);
|
||||
}
|
||||
|
||||
/* Kill if it is not dead yet anyway */
|
||||
if (!arg_register && !arg_keep_unit && bus)
|
||||
terminate_scope(bus, arg_machine);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "main-func.h"
|
||||
#include "pager.h"
|
||||
#include "pretty-print.h"
|
||||
#include "sort-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
static const char *arg_suffix = NULL;
|
||||
@ -101,27 +102,50 @@ static const char* const path_table[_SD_PATH_MAX] = {
|
||||
[SD_PATH_SYSTEMD_USER_ENVIRONMENT_GENERATOR] = "systemd-user-environment-generator",
|
||||
[SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR] = "systemd-search-system-environment-generator",
|
||||
[SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR] = "systemd-search-user-environment-generator",
|
||||
|
||||
[SD_PATH_SYSTEM_CREDENTIAL_STORE] = "system-credential-store",
|
||||
[SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE] = "system-search-credential-store",
|
||||
[SD_PATH_SYSTEM_CREDENTIAL_STORE_ENCRYPTED] = "system-credential-store-encrypted",
|
||||
[SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE_ENCRYPTED] = "system-search-credential-store-encrypted",
|
||||
[SD_PATH_USER_CREDENTIAL_STORE] = "user-credential-store",
|
||||
[SD_PATH_USER_SEARCH_CREDENTIAL_STORE] = "user-search-credential-store",
|
||||
[SD_PATH_USER_CREDENTIAL_STORE_ENCRYPTED] = "user-credential-store-encrypted",
|
||||
[SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED] = "user-search-credential-store-encrypted",
|
||||
|
||||
};
|
||||
|
||||
static int order_cmp(const size_t *a, const size_t *b) {
|
||||
assert(*a < ELEMENTSOF(path_table));
|
||||
assert(*b < ELEMENTSOF(path_table));
|
||||
return strcmp(path_table[*a], path_table[*b]);
|
||||
}
|
||||
|
||||
static int list_paths(void) {
|
||||
int r = 0;
|
||||
int ret = 0, r;
|
||||
|
||||
pager_open(arg_pager_flags);
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(path_table); i++) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int q;
|
||||
size_t order[ELEMENTSOF(path_table)];
|
||||
|
||||
q = sd_path_lookup(i, arg_suffix, &p);
|
||||
if (q < 0) {
|
||||
log_full_errno(q == -ENXIO ? LOG_DEBUG : LOG_ERR,
|
||||
q, "Failed to query %s: %m", path_table[i]);
|
||||
if (q != -ENXIO)
|
||||
RET_GATHER(r, q);
|
||||
for (size_t i = 0; i < ELEMENTSOF(order); i++)
|
||||
order[i] = i;
|
||||
|
||||
typesafe_qsort(order, ELEMENTSOF(order), order_cmp);
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(order); i++) {
|
||||
size_t j = order[i];
|
||||
const char *t = ASSERT_PTR(path_table[j]);
|
||||
|
||||
_cleanup_free_ char *p = NULL;
|
||||
r = sd_path_lookup(j, arg_suffix, &p);
|
||||
if (r < 0) {
|
||||
log_full_errno(r == -ENXIO ? LOG_DEBUG : LOG_ERR, r, "Failed to query %s, proceeding: %m", t);
|
||||
if (r != -ENXIO)
|
||||
RET_GATHER(ret, r);
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("%s%s:%s %s\n", ansi_highlight(), path_table[i], ansi_normal(), p);
|
||||
printf("%s%s:%s %s\n", ansi_highlight(), t, ansi_normal(), p);
|
||||
}
|
||||
|
||||
return r;
|
||||
@ -154,14 +178,16 @@ static int help(void) {
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
printf("%s [OPTIONS...] [NAME...]\n\n"
|
||||
"Show system and user paths.\n\n"
|
||||
printf("%s [OPTIONS...] [NAME...]\n"
|
||||
"\n%sShow system and user paths.%s\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" --suffix=SUFFIX Suffix to append to paths\n"
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
ansi_normal(),
|
||||
link);
|
||||
|
||||
return 0;
|
||||
@ -224,10 +250,11 @@ static int run(int argc, char* argv[]) {
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (argc > optind)
|
||||
if (argc > optind) {
|
||||
r = 0;
|
||||
for (int i = optind; i < argc; i++)
|
||||
RET_GATHER(r, print_path(argv[i]));
|
||||
else
|
||||
} else
|
||||
r = list_paths();
|
||||
|
||||
return r;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "resolved-dns-query.h"
|
||||
#include "resolved-dns-synthesize.h"
|
||||
#include "resolved-etc-hosts.h"
|
||||
#include "resolved-timeouts.h"
|
||||
#include "string-util.h"
|
||||
|
||||
#define QUERIES_MAX 2048
|
||||
@ -48,6 +49,8 @@ static void dns_query_candidate_stop(DnsQueryCandidate *c) {
|
||||
|
||||
assert(c);
|
||||
|
||||
(void) event_source_disable(c->timeout_event_source);
|
||||
|
||||
/* Detach all the DnsTransactions attached to this query */
|
||||
|
||||
while ((t = set_steal_first(c->transactions))) {
|
||||
@ -62,6 +65,8 @@ static void dns_query_candidate_abandon(DnsQueryCandidate *c) {
|
||||
|
||||
assert(c);
|
||||
|
||||
(void) event_source_disable(c->timeout_event_source);
|
||||
|
||||
/* Abandon all the DnsTransactions attached to this query */
|
||||
|
||||
while ((t = set_steal_first(c->transactions))) {
|
||||
@ -94,6 +99,8 @@ static DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c) {
|
||||
if (!c)
|
||||
return NULL;
|
||||
|
||||
c->timeout_event_source = sd_event_source_disable_unref(c->timeout_event_source);
|
||||
|
||||
dns_query_candidate_stop(c);
|
||||
dns_query_candidate_unlink(c);
|
||||
|
||||
@ -312,6 +319,30 @@ fail:
|
||||
return r;
|
||||
}
|
||||
|
||||
static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c);
|
||||
|
||||
static int on_candidate_timeout(sd_event_source *s, usec_t usec, void *userdata) {
|
||||
DnsQueryCandidate *c = userdata;
|
||||
|
||||
assert(s);
|
||||
assert(c);
|
||||
|
||||
log_debug("Accepting incomplete query candidate after expedited timeout on partial success.");
|
||||
dns_query_accept(c->query, c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool dns_query_candidate_has_partially_succeeded(DnsQueryCandidate *c) {
|
||||
DnsTransaction *t;
|
||||
|
||||
SET_FOREACH(t, c->transactions)
|
||||
if (t->state == DNS_TRANSACTION_SUCCESS)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void dns_query_candidate_notify(DnsQueryCandidate *c) {
|
||||
DnsTransactionState state;
|
||||
int r;
|
||||
@ -323,11 +354,24 @@ void dns_query_candidate_notify(DnsQueryCandidate *c) {
|
||||
|
||||
state = dns_query_candidate_state(c);
|
||||
|
||||
if (DNS_TRANSACTION_IS_LIVE(state))
|
||||
if (DNS_TRANSACTION_IS_LIVE(state)) {
|
||||
if (dns_query_candidate_has_partially_succeeded(c))
|
||||
(void) event_reset_time_relative(
|
||||
c->query->manager->event,
|
||||
&c->timeout_event_source,
|
||||
CLOCK_BOOTTIME,
|
||||
CANDIDATE_EXPEDITED_TIMEOUT_USEC, /* accuracy_usec= */ 0,
|
||||
on_candidate_timeout, c,
|
||||
/* priority= */ 0, "candidate-timeout",
|
||||
/* force_reset= */ false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (state != DNS_TRANSACTION_SUCCESS && c->search_domain) {
|
||||
|
||||
(void) event_source_disable(c->timeout_event_source);
|
||||
|
||||
r = dns_query_candidate_next_search_domain(c);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
@ -25,6 +25,7 @@ struct DnsQueryCandidate {
|
||||
DnsSearchDomain *search_domain;
|
||||
|
||||
Set *transactions;
|
||||
sd_event_source *timeout_event_source;
|
||||
|
||||
LIST_FIELDS(DnsQueryCandidate, candidates_by_query);
|
||||
LIST_FIELDS(DnsQueryCandidate, candidates_by_scope);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "resolved-dns-zone.h"
|
||||
#include "resolved-llmnr.h"
|
||||
#include "resolved-mdns.h"
|
||||
#include "resolved-timeouts.h"
|
||||
#include "socket-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
|
@ -14,13 +14,10 @@
|
||||
#include "resolved-dns-transaction.h"
|
||||
#include "resolved-dnstls.h"
|
||||
#include "resolved-llmnr.h"
|
||||
#include "resolved-timeouts.h"
|
||||
#include "string-table.h"
|
||||
|
||||
#define TRANSACTIONS_MAX 4096
|
||||
#define TRANSACTION_TCP_TIMEOUT_USEC (10U*USEC_PER_SEC)
|
||||
|
||||
/* After how much time to repeat classic DNS requests */
|
||||
#define DNS_TIMEOUT_USEC (SD_RESOLVED_QUERY_TIMEOUT_USEC / DNS_TRANSACTION_ATTEMPTS_MAX)
|
||||
|
||||
static void dns_transaction_reset_answer(DnsTransaction *t) {
|
||||
assert(t);
|
||||
@ -1632,13 +1629,10 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
|
||||
|
||||
case DNS_PROTOCOL_DNS:
|
||||
|
||||
/* When we do TCP, grant a much longer timeout, as in this case there's no need for us to quickly
|
||||
* resend, as the kernel does that anyway for us, and we really don't want to interrupt it in that
|
||||
* needlessly. */
|
||||
if (t->stream)
|
||||
return TRANSACTION_TCP_TIMEOUT_USEC;
|
||||
|
||||
return DNS_TIMEOUT_USEC;
|
||||
return TRANSACTION_UDP_TIMEOUT_USEC;
|
||||
|
||||
case DNS_PROTOCOL_MDNS:
|
||||
if (t->probing)
|
||||
|
@ -203,24 +203,3 @@ DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_;
|
||||
|
||||
const char* dns_transaction_source_to_string(DnsTransactionSource p) _const_;
|
||||
DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_;
|
||||
|
||||
/* LLMNR Jitter interval, see RFC 4795 Section 7 */
|
||||
#define LLMNR_JITTER_INTERVAL_USEC (100 * USEC_PER_MSEC)
|
||||
|
||||
/* mDNS probing interval, see RFC 6762 Section 8.1 */
|
||||
#define MDNS_PROBING_INTERVAL_USEC (250 * USEC_PER_MSEC)
|
||||
|
||||
/* Maximum attempts to send DNS requests, across all DNS servers */
|
||||
#define DNS_TRANSACTION_ATTEMPTS_MAX 24
|
||||
|
||||
/* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */
|
||||
#define LLMNR_TRANSACTION_ATTEMPTS_MAX 3
|
||||
|
||||
/* Maximum attempts to send MDNS requests, see RFC 6762 Section 8.1 */
|
||||
#define MDNS_TRANSACTION_ATTEMPTS_MAX 3
|
||||
|
||||
#define TRANSACTION_ATTEMPTS_MAX(p) ((p) == DNS_PROTOCOL_LLMNR ? \
|
||||
LLMNR_TRANSACTION_ATTEMPTS_MAX : \
|
||||
(p) == DNS_PROTOCOL_MDNS ? \
|
||||
MDNS_TRANSACTION_ATTEMPTS_MAX : \
|
||||
DNS_TRANSACTION_ATTEMPTS_MAX)
|
||||
|
39
src/resolve/resolved-timeouts.h
Normal file
39
src/resolve/resolved-timeouts.h
Normal file
@ -0,0 +1,39 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "time-util.h"
|
||||
#include "resolved-def.h"
|
||||
|
||||
/* LLMNR Jitter interval, see RFC 4795 Section 7 */
|
||||
#define LLMNR_JITTER_INTERVAL_USEC (100 * USEC_PER_MSEC)
|
||||
|
||||
/* mDNS probing interval, see RFC 6762 Section 8.1 */
|
||||
#define MDNS_PROBING_INTERVAL_USEC (250 * USEC_PER_MSEC)
|
||||
|
||||
/* Maximum attempts to send DNS requests, across all DNS servers */
|
||||
#define DNS_TRANSACTION_ATTEMPTS_MAX 24
|
||||
|
||||
/* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */
|
||||
#define LLMNR_TRANSACTION_ATTEMPTS_MAX 3
|
||||
|
||||
/* Maximum attempts to send MDNS requests, see RFC 6762 Section 8.1 */
|
||||
#define MDNS_TRANSACTION_ATTEMPTS_MAX 3
|
||||
|
||||
#define TRANSACTION_ATTEMPTS_MAX(p) (\
|
||||
(p) == DNS_PROTOCOL_LLMNR ? \
|
||||
LLMNR_TRANSACTION_ATTEMPTS_MAX : \
|
||||
(p) == DNS_PROTOCOL_MDNS ? \
|
||||
MDNS_TRANSACTION_ATTEMPTS_MAX : \
|
||||
DNS_TRANSACTION_ATTEMPTS_MAX)
|
||||
|
||||
/* After how much time to repeat classic DNS requests */
|
||||
#define TRANSACTION_UDP_TIMEOUT_USEC (SD_RESOLVED_QUERY_TIMEOUT_USEC / DNS_TRANSACTION_ATTEMPTS_MAX)
|
||||
|
||||
/* When we do TCP, grant a much longer timeout, as in this case there's no need for us to quickly
|
||||
* resend, as the kernel does that anyway for us, and we really don't want to interrupt it in that
|
||||
* needlessly. */
|
||||
#define TRANSACTION_TCP_TIMEOUT_USEC (10 * USEC_PER_SEC)
|
||||
|
||||
/* Should be longer than transaction timeout for a single UDP transaction, so we get at least
|
||||
* one transaction retry before timeouting the whole candidate */
|
||||
#define CANDIDATE_EXPEDITED_TIMEOUT_USEC (TRANSACTION_UDP_TIMEOUT_USEC + 1 * USEC_PER_SEC)
|
@ -1555,16 +1555,9 @@ static int run_context_reconnect(RunContext *c) {
|
||||
}
|
||||
|
||||
static void run_context_check_done(RunContext *c) {
|
||||
bool done;
|
||||
|
||||
assert(c);
|
||||
|
||||
done = STRPTR_IN_SET(c->active_state, "inactive", "failed") && !c->has_job;
|
||||
|
||||
if (c->forward && !pty_forward_is_done(c->forward) && done) /* If the service is gone, it's time to drain the output */
|
||||
done = pty_forward_drain(c->forward);
|
||||
|
||||
if (done)
|
||||
if (STRPTR_IN_SET(c->active_state, "inactive", "failed") && !c->has_job)
|
||||
(void) sd_event_exit(c->event, EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@ -2094,21 +2087,8 @@ static int start_transient_service(sd_bus *bus) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to run event loop: %m");
|
||||
|
||||
if (c.forward) {
|
||||
char last_char = 0;
|
||||
|
||||
r = pty_forward_get_last_char(c.forward, &last_char);
|
||||
if (r >= 0 && !arg_quiet && last_char != '\n')
|
||||
fputc('\n', stdout);
|
||||
}
|
||||
|
||||
if (arg_wait && !arg_quiet) {
|
||||
|
||||
/* Explicitly destroy the PTY forwarder, so that the PTY device is usable again, with its
|
||||
* original settings (i.e. proper line breaks), so that we can show the summary in a pretty
|
||||
* way. */
|
||||
c.forward = pty_forward_free(c.forward);
|
||||
|
||||
if (!isempty(c.result))
|
||||
log_info("Finished with result: %s", strna(c.result));
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "battery-util.h"
|
||||
#include "blockdev-util.h"
|
||||
#include "cap-list.h"
|
||||
#include "capability-util.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "compare-operator.h"
|
||||
#include "condition.h"
|
||||
@ -701,45 +702,23 @@ static int condition_test_security(Condition *c, char **env) {
|
||||
}
|
||||
|
||||
static int condition_test_capability(Condition *c, char **env) {
|
||||
unsigned long long capabilities = (unsigned long long) -1;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int value, r;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(c->parameter);
|
||||
assert(c->type == CONDITION_CAPABILITY);
|
||||
|
||||
/* If it's an invalid capability, we don't have it */
|
||||
value = capability_from_name(c->parameter);
|
||||
int value = capability_from_name(c->parameter);
|
||||
if (value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* If it's a valid capability we default to assume
|
||||
* that we have it */
|
||||
CapabilityQuintet q;
|
||||
r = pidref_get_capability(&PIDREF_MAKE_FROM_PID(getpid_cached()), &q);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
f = fopen("/proc/self/status", "re");
|
||||
if (!f)
|
||||
return -errno;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
|
||||
r = read_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
const char *p = startswith(line, "CapBnd:");
|
||||
if (p) {
|
||||
if (sscanf(p, "%llx", &capabilities) != 1)
|
||||
return -EIO;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return !!(capabilities & (1ULL << value));
|
||||
return !!(q.bounding & ((UINT64_C(1) << value)));
|
||||
}
|
||||
|
||||
static int condition_test_needs_update(Condition *c, char **env) {
|
||||
|
@ -536,6 +536,8 @@ int mount_switch_root_full(const char *path, unsigned long mount_propagation_fla
|
||||
}
|
||||
}
|
||||
|
||||
log_debug("Successfully switched root to '%s'.", path);
|
||||
|
||||
/* Finally, let's establish the requested propagation flags. */
|
||||
if (mount_propagation_flag == 0)
|
||||
return 0;
|
||||
@ -1319,7 +1321,7 @@ int make_userns(uid_t uid_shift, uid_t uid_range, uid_t source_owner, uid_t dest
|
||||
* process whose only purpose is to give us a new user namespace. It's killed when we got it. */
|
||||
|
||||
if (!userns_shift_range_valid(uid_shift, uid_range))
|
||||
return -EINVAL;
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid UID range for user namespace.");
|
||||
|
||||
if (IN_SET(idmapping, REMOUNT_IDMAPPING_NONE, REMOUNT_IDMAPPING_HOST_ROOT)) {
|
||||
if (asprintf(&line, UID_FMT " " UID_FMT " " UID_FMT "\n", 0u, uid_shift, uid_range) < 0)
|
||||
|
@ -87,6 +87,8 @@ int nsresource_allocate_userns(const char *name, uint64_t size) {
|
||||
SD_JSON_BUILD_PAIR("userNamespaceFileDescriptor", SD_JSON_BUILD_UNSIGNED(userns_fd_idx)));
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to call AllocateUserRange() varlink call: %m");
|
||||
if (streq_ptr(error_id, "io.systemd.NamespaceResource.UserNamespaceInterfaceNotSupported"))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unprivileged user namespace delegation is not supported on this system.");
|
||||
if (error_id)
|
||||
return log_debug_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to allocate user namespace with %" PRIu64 " users: %s", size, error_id);
|
||||
|
||||
|
@ -58,8 +58,8 @@ struct PTYForward {
|
||||
sd_event_source *stdin_event_source;
|
||||
sd_event_source *stdout_event_source;
|
||||
sd_event_source *master_event_source;
|
||||
|
||||
sd_event_source *sigwinch_event_source;
|
||||
sd_event_source *exit_event_source;
|
||||
|
||||
struct termios saved_stdin_attr;
|
||||
struct termios saved_stdout_attr;
|
||||
@ -81,14 +81,16 @@ struct PTYForward {
|
||||
bool read_from_master:1;
|
||||
|
||||
bool done:1;
|
||||
bool drain:1;
|
||||
|
||||
bool last_char_set:1;
|
||||
char last_char;
|
||||
char last_char_safe;
|
||||
|
||||
char in_buffer[LINE_MAX], *out_buffer;
|
||||
size_t out_buffer_size;
|
||||
size_t in_buffer_full, out_buffer_full;
|
||||
size_t out_buffer_write_len; /* The length of the output in the buffer except for the trailing
|
||||
* truncated OSC, CSI, or some (but not all) ESC sequence. */
|
||||
|
||||
usec_t escape_timestamp;
|
||||
unsigned escape_counter;
|
||||
@ -114,9 +116,9 @@ static void pty_forward_disconnect(PTYForward *f) {
|
||||
|
||||
f->stdin_event_source = sd_event_source_unref(f->stdin_event_source);
|
||||
f->stdout_event_source = sd_event_source_unref(f->stdout_event_source);
|
||||
|
||||
f->master_event_source = sd_event_source_unref(f->master_event_source);
|
||||
f->sigwinch_event_source = sd_event_source_unref(f->sigwinch_event_source);
|
||||
f->exit_event_source = sd_event_source_unref(f->exit_event_source);
|
||||
f->event = sd_event_unref(f->event);
|
||||
|
||||
if (f->output_fd >= 0) {
|
||||
@ -133,6 +135,19 @@ static void pty_forward_disconnect(PTYForward *f) {
|
||||
(void) loop_write(f->output_fd, ANSI_WINDOW_TITLE_POP, SIZE_MAX);
|
||||
}
|
||||
|
||||
if (f->last_char_set && f->last_char != '\n') {
|
||||
const char *s;
|
||||
|
||||
if (isatty_safe(f->output_fd) && f->last_char != '\r')
|
||||
s = "\r\n";
|
||||
else
|
||||
s = "\n";
|
||||
(void) loop_write(f->output_fd, s, SIZE_MAX);
|
||||
|
||||
f->last_char_set = true;
|
||||
f->last_char = '\n';
|
||||
}
|
||||
|
||||
if (f->close_output_fd)
|
||||
f->output_fd = safe_close(f->output_fd);
|
||||
}
|
||||
@ -151,6 +166,7 @@ static void pty_forward_disconnect(PTYForward *f) {
|
||||
f->out_buffer = mfree(f->out_buffer);
|
||||
f->out_buffer_size = 0;
|
||||
f->out_buffer_full = 0;
|
||||
f->out_buffer_write_len = 0;
|
||||
f->in_buffer_full = 0;
|
||||
|
||||
f->csi_sequence = mfree(f->csi_sequence);
|
||||
@ -243,7 +259,7 @@ static bool drained(PTYForward *f) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *background_color_sequence(PTYForward *f) {
|
||||
static char* background_color_sequence(PTYForward *f) {
|
||||
assert(f);
|
||||
assert(f->background_color);
|
||||
|
||||
@ -277,6 +293,9 @@ static int insert_background_color(PTYForward *f, size_t offset) {
|
||||
|
||||
assert(f);
|
||||
|
||||
if (FLAGS_SET(f->flags, PTY_FORWARD_DUMB_TERMINAL))
|
||||
return 0;
|
||||
|
||||
if (!f->background_color)
|
||||
return 0;
|
||||
|
||||
@ -359,6 +378,9 @@ static int is_csi_background_reset_sequence(const char *seq) {
|
||||
static int insert_background_fix(PTYForward *f, size_t offset) {
|
||||
assert(f);
|
||||
|
||||
if (FLAGS_SET(f->flags, PTY_FORWARD_DUMB_TERMINAL))
|
||||
return 0;
|
||||
|
||||
if (!f->background_color)
|
||||
return 0;
|
||||
|
||||
@ -391,6 +413,9 @@ bool shall_set_terminal_title(void) {
|
||||
static int insert_window_title_fix(PTYForward *f, size_t offset) {
|
||||
assert(f);
|
||||
|
||||
if (FLAGS_SET(f->flags, PTY_FORWARD_DUMB_TERMINAL))
|
||||
return 0;
|
||||
|
||||
if (!f->title_prefix)
|
||||
return 0;
|
||||
|
||||
@ -414,12 +439,6 @@ static int pty_forward_ansi_process(PTYForward *f, size_t offset) {
|
||||
assert(f);
|
||||
assert(offset <= f->out_buffer_full);
|
||||
|
||||
if (!f->background_color && !f->title_prefix)
|
||||
return 0;
|
||||
|
||||
if (FLAGS_SET(f->flags, PTY_FORWARD_DUMB_TERMINAL))
|
||||
return 0;
|
||||
|
||||
for (size_t i = offset; i < f->out_buffer_full; i++) {
|
||||
char c = f->out_buffer[i];
|
||||
|
||||
@ -433,8 +452,11 @@ static int pty_forward_ansi_process(PTYForward *f, size_t offset) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
i += r;
|
||||
f->last_char_safe = c;
|
||||
} else if (c == 0x1B) /* ESC */
|
||||
f->ansi_color_state = ANSI_COLOR_STATE_ESC;
|
||||
else if (!char_is_cc(c))
|
||||
f->last_char_safe = c;
|
||||
break;
|
||||
|
||||
case ANSI_COLOR_STATE_ESC:
|
||||
@ -545,6 +567,9 @@ static int pty_forward_ansi_process(PTYForward *f, size_t offset) {
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (f->ansi_color_state == ANSI_COLOR_STATE_TEXT)
|
||||
f->out_buffer_write_len = i + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -579,7 +604,7 @@ static int do_shovel(PTYForward *f) {
|
||||
}
|
||||
|
||||
if (f->out_buffer) {
|
||||
f->out_buffer_full = strlen(f->out_buffer);
|
||||
f->out_buffer_full = f->out_buffer_write_len = strlen(f->out_buffer);
|
||||
f->out_buffer_size = MALLOC_SIZEOF_SAFE(f->out_buffer);
|
||||
}
|
||||
}
|
||||
@ -679,9 +704,10 @@ static int do_shovel(PTYForward *f) {
|
||||
}
|
||||
}
|
||||
|
||||
if (f->stdout_writable && f->out_buffer_full > 0) {
|
||||
if (f->stdout_writable && f->out_buffer_write_len > 0) {
|
||||
assert(f->out_buffer_write_len <= f->out_buffer_full);
|
||||
|
||||
k = write(f->output_fd, f->out_buffer, f->out_buffer_full);
|
||||
k = write(f->output_fd, f->out_buffer, f->out_buffer_write_len);
|
||||
if (k < 0) {
|
||||
|
||||
if (errno == EAGAIN)
|
||||
@ -695,14 +721,22 @@ static int do_shovel(PTYForward *f) {
|
||||
|
||||
} else {
|
||||
|
||||
if (k > 0) {
|
||||
f->last_char = f->out_buffer[k-1];
|
||||
if (k > 0 && f->last_char_safe != '\0') {
|
||||
if ((size_t) k == f->out_buffer_write_len)
|
||||
/* If we wrote all, then save the last safe character. */
|
||||
f->last_char = f->last_char_safe;
|
||||
else
|
||||
/* If we wrote partially, then tentatively save the last written character.
|
||||
* Hopefully, we will write more in the next loop. */
|
||||
f->last_char = f->out_buffer[k-1];
|
||||
|
||||
f->last_char_set = true;
|
||||
}
|
||||
|
||||
assert(f->out_buffer_full >= (size_t) k);
|
||||
assert(f->out_buffer_write_len >= (size_t) k);
|
||||
memmove(f->out_buffer, f->out_buffer + k, f->out_buffer_full - k);
|
||||
f->out_buffer_full -= k;
|
||||
f->out_buffer_write_len -= k;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -711,16 +745,11 @@ static int do_shovel(PTYForward *f) {
|
||||
/* Exit the loop if any side hung up and if there's
|
||||
* nothing more to write or nothing we could write. */
|
||||
|
||||
if ((f->out_buffer_full <= 0 || f->stdout_hangup) &&
|
||||
if ((f->out_buffer_write_len <= 0 || f->stdout_hangup) &&
|
||||
(f->in_buffer_full <= 0 || f->master_hangup))
|
||||
return pty_forward_done(f, 0);
|
||||
}
|
||||
|
||||
/* If we were asked to drain, and there's nothing more to handle from the master, then call the callback
|
||||
* too. */
|
||||
if (f->drain && drained(f))
|
||||
return pty_forward_done(f, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -795,6 +824,40 @@ static int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo *
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_exit_event(sd_event_source *e, void *userdata) {
|
||||
PTYForward *f = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(e);
|
||||
assert(e == f->exit_event_source);
|
||||
|
||||
/* Drain the buffer on exit. */
|
||||
|
||||
if (f->done)
|
||||
return 0;
|
||||
|
||||
for (unsigned trial = 0; trial < 1000; trial++) {
|
||||
if (drained(f))
|
||||
return pty_forward_done(f, 0);
|
||||
|
||||
if (!f->master_hangup)
|
||||
f->master_writable = f->master_readable = true;
|
||||
if (!f->stdin_hangup)
|
||||
f->stdin_readable = true;
|
||||
if (!f->stdout_hangup)
|
||||
f->stdout_writable = true;
|
||||
|
||||
r = shovel(f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (f->done)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If we could not drain, then propagate recognizable error code. */
|
||||
return pty_forward_done(f, -ELOOP);
|
||||
}
|
||||
|
||||
int pty_forward_new(
|
||||
sd_event *event,
|
||||
int master,
|
||||
@ -955,12 +1018,18 @@ int pty_forward_new(
|
||||
|
||||
(void) sd_event_source_set_description(f->sigwinch_event_source, "ptyfwd-sigwinch");
|
||||
|
||||
r = sd_event_add_exit(f->event, &f->exit_event_source, on_exit_event, f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) sd_event_source_set_description(f->exit_event_source, "ptyfwd-exit");
|
||||
|
||||
*ret = TAKE_PTR(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PTYForward *pty_forward_free(PTYForward *f) {
|
||||
PTYForward* pty_forward_free(PTYForward *f) {
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
@ -972,17 +1041,6 @@ PTYForward *pty_forward_free(PTYForward *f) {
|
||||
return mfree(f);
|
||||
}
|
||||
|
||||
int pty_forward_get_last_char(PTYForward *f, char *ch) {
|
||||
assert(f);
|
||||
assert(ch);
|
||||
|
||||
if (!f->last_char_set)
|
||||
return -ENXIO;
|
||||
|
||||
*ch = f->last_char;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pty_forward_set_ignore_vhangup(PTYForward *f, bool b) {
|
||||
int r;
|
||||
|
||||
@ -1012,12 +1070,6 @@ bool pty_forward_get_ignore_vhangup(PTYForward *f) {
|
||||
return FLAGS_SET(f->flags, PTY_FORWARD_IGNORE_VHANGUP);
|
||||
}
|
||||
|
||||
bool pty_forward_is_done(PTYForward *f) {
|
||||
assert(f);
|
||||
|
||||
return f->done;
|
||||
}
|
||||
|
||||
void pty_forward_set_handler(PTYForward *f, PTYForwardHandler cb, void *userdata) {
|
||||
assert(f);
|
||||
|
||||
@ -1025,20 +1077,6 @@ void pty_forward_set_handler(PTYForward *f, PTYForwardHandler cb, void *userdata
|
||||
f->userdata = userdata;
|
||||
}
|
||||
|
||||
bool pty_forward_drain(PTYForward *f) {
|
||||
assert(f);
|
||||
|
||||
/* Starts draining the forwarder. Specifically:
|
||||
*
|
||||
* - Returns true if there are no unprocessed bytes from the pty, false otherwise
|
||||
*
|
||||
* - Makes sure the handler function is called the next time the number of unprocessed bytes hits zero
|
||||
*/
|
||||
|
||||
f->drain = true;
|
||||
return drained(f);
|
||||
}
|
||||
|
||||
int pty_forward_set_priority(PTYForward *f, int64_t priority) {
|
||||
int r;
|
||||
|
||||
|
@ -25,20 +25,14 @@ typedef enum PTYForwardFlags {
|
||||
|
||||
typedef int (*PTYForwardHandler)(PTYForward *f, int rcode, void *userdata);
|
||||
|
||||
int pty_forward_new(sd_event *event, int master, PTYForwardFlags flags, PTYForward **f);
|
||||
PTYForward *pty_forward_free(PTYForward *f);
|
||||
|
||||
int pty_forward_get_last_char(PTYForward *f, char *ch);
|
||||
int pty_forward_new(sd_event *event, int master, PTYForwardFlags flags, PTYForward **ret);
|
||||
PTYForward* pty_forward_free(PTYForward *f);
|
||||
|
||||
int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup);
|
||||
bool pty_forward_get_ignore_vhangup(PTYForward *f);
|
||||
|
||||
bool pty_forward_is_done(PTYForward *f);
|
||||
|
||||
void pty_forward_set_handler(PTYForward *f, PTYForwardHandler handler, void *userdata);
|
||||
|
||||
bool pty_forward_drain(PTYForward *f);
|
||||
|
||||
int pty_forward_set_priority(PTYForward *f, int64_t priority);
|
||||
|
||||
int pty_forward_set_width_height(PTYForward *f, unsigned width, unsigned height);
|
||||
|
@ -120,6 +120,16 @@ enum {
|
||||
|
||||
SD_PATH_USER_STATE_PRIVATE,
|
||||
|
||||
/* credential store */
|
||||
SD_PATH_SYSTEM_CREDENTIAL_STORE,
|
||||
SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE,
|
||||
SD_PATH_SYSTEM_CREDENTIAL_STORE_ENCRYPTED,
|
||||
SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE_ENCRYPTED,
|
||||
SD_PATH_USER_CREDENTIAL_STORE,
|
||||
SD_PATH_USER_SEARCH_CREDENTIAL_STORE,
|
||||
SD_PATH_USER_CREDENTIAL_STORE_ENCRYPTED,
|
||||
SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED,
|
||||
|
||||
_SD_PATH_MAX
|
||||
};
|
||||
|
||||
|
@ -596,7 +596,7 @@ int transfer_read_definition(Transfer *t, const char *path, const char **dirs, H
|
||||
!IN_SET(t->target.type, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME)))
|
||||
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Target type '%s' is incompatible with source type '%s', refusing.",
|
||||
resource_type_to_string(t->source.type), resource_type_to_string(t->target.type));
|
||||
resource_type_to_string(t->target.type), resource_type_to_string(t->source.type));
|
||||
|
||||
if (!t->source.path && !t->source.path_auto)
|
||||
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||
|
@ -305,6 +305,18 @@ static void test_capability_get_ambient(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static void test_pidref_get_capability(void) {
|
||||
CapabilityQuintet q = CAPABILITY_QUINTET_NULL;
|
||||
|
||||
assert_se(pidref_get_capability(&PIDREF_MAKE_FROM_PID(getpid_cached()), &q) >= 0);
|
||||
|
||||
assert_se(q.effective != CAP_MASK_UNSET);
|
||||
assert_se(q.inheritable != CAP_MASK_UNSET);
|
||||
assert_se(q.permitted != CAP_MASK_UNSET);
|
||||
assert_se(q.effective != CAP_MASK_UNSET);
|
||||
assert_se(q.ambient != CAP_MASK_UNSET);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
bool run_ambient;
|
||||
|
||||
@ -336,5 +348,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
test_capability_get_ambient();
|
||||
|
||||
test_pidref_get_capability();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1398,6 +1398,10 @@ static void run_tests(RuntimeScope scope, char **patterns) {
|
||||
ASSERT_NOT_NULL(unit_paths = strjoin(PRIVATE_UNIT_DIR, ":", user_runtime_unit_dir));
|
||||
ASSERT_OK(setenv_unit_path(unit_paths));
|
||||
|
||||
/* Write credential for test-execute-load-credential to the fake runtime dir, too */
|
||||
_cleanup_free_ char *j = ASSERT_PTR(path_join(runtime_dir, "credstore/test-execute.load-credential"));
|
||||
ASSERT_OK(write_string_file(j, "foo", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755));
|
||||
|
||||
r = manager_new(scope, MANAGER_TEST_RUN_BASIC, &m);
|
||||
if (manager_errno_skip_test(r))
|
||||
return (void) log_tests_skipped_errno(r, "manager_new");
|
||||
|
@ -363,24 +363,6 @@ TEST(status_field) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(capeff) {
|
||||
for (int pid = 0; pid < 2; pid++) {
|
||||
_cleanup_free_ char *capeff = NULL;
|
||||
int r, p;
|
||||
|
||||
r = get_process_capeff(0, &capeff);
|
||||
log_info("capeff: '%s' (r=%d)", capeff, r);
|
||||
|
||||
if (IN_SET(r, -ENOENT, -EPERM))
|
||||
return;
|
||||
|
||||
assert_se(r == 0);
|
||||
assert_se(*capeff);
|
||||
p = capeff[strspn(capeff, HEXDIGITS)];
|
||||
assert_se(!p || isspace(p));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(read_one_line_file) {
|
||||
_cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-fileio-1lf-XXXXXX";
|
||||
int fd;
|
||||
|
@ -19,6 +19,7 @@ udevadm_sources = files(
|
||||
|
||||
libudevd_core_sources = files(
|
||||
'net/link-config.c',
|
||||
'udev-config.c',
|
||||
'udev-ctrl.c',
|
||||
'udev-event.c',
|
||||
'udev-format.c',
|
||||
|
@ -650,7 +650,16 @@ static int get_pci_slot_specifiers(
|
||||
* where the slot makes up the upper 5 bits. */
|
||||
func += slot * 8;
|
||||
|
||||
if (domain > 0 && asprintf(&domain_spec, "P%u", domain) < 0)
|
||||
/* Include the PCI domain in the name if the ID_NET_NAME_INCLUDE_DOMAIN property says so, if it is
|
||||
* set. If it is not set, include it if the domain is non-zero. */
|
||||
r = device_get_property_bool(dev, "ID_NET_NAME_INCLUDE_DOMAIN");
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
log_device_warning_errno(dev, r, "Failed to read property \"ID_NET_NAME_INCLUDE_DOMAIN\", ignoring: %m");
|
||||
|
||||
r = domain > 0;
|
||||
}
|
||||
if (r > 0 && asprintf(&domain_spec, "P%u", domain) < 0)
|
||||
return log_oom_debug();
|
||||
|
||||
if (asprintf(&bus_and_slot_spec, "p%us%u", bus, slot) < 0)
|
||||
|
@ -42,11 +42,28 @@ void udev_builtin_exit(void) {
|
||||
(*b)->exit();
|
||||
}
|
||||
|
||||
bool udev_builtin_should_reload(void) {
|
||||
UdevReloadFlags udev_builtin_should_reload(void) {
|
||||
UdevReloadFlags flags = 0;
|
||||
|
||||
for (UdevBuiltinCommand i = 0; i < _UDEV_BUILTIN_MAX; i++)
|
||||
if (builtins[i] && builtins[i]->should_reload && builtins[i]->should_reload())
|
||||
return true;
|
||||
return false;
|
||||
flags |= 1u << i;
|
||||
|
||||
if (flags != 0)
|
||||
flags |= UDEV_RELOAD_KILL_WORKERS;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
void udev_builtin_reload(UdevReloadFlags flags) {
|
||||
for (UdevBuiltinCommand i = 0; i < _UDEV_BUILTIN_MAX; i++) {
|
||||
if (!FLAGS_SET(flags, 1u << i) || !builtins[i])
|
||||
continue;
|
||||
if (builtins[i]->exit)
|
||||
builtins[i]->exit();
|
||||
if (builtins[i]->init)
|
||||
builtins[i]->init();
|
||||
}
|
||||
}
|
||||
|
||||
void udev_builtin_list(void) {
|
||||
|
@ -59,7 +59,8 @@ const char* udev_builtin_name(UdevBuiltinCommand cmd);
|
||||
bool udev_builtin_run_once(UdevBuiltinCommand cmd);
|
||||
int udev_builtin_run(UdevEvent *event, UdevBuiltinCommand cmd, const char *command);
|
||||
void udev_builtin_list(void);
|
||||
bool udev_builtin_should_reload(void);
|
||||
UdevReloadFlags udev_builtin_should_reload(void);
|
||||
void udev_builtin_reload(UdevReloadFlags flags);
|
||||
int udev_builtin_add_property(UdevEvent *event, const char *key, const char *val);
|
||||
int udev_builtin_add_propertyf(UdevEvent *event, const char *key, const char *valf, ...) _printf_(3, 4);
|
||||
int udev_builtin_import_property(UdevEvent *event, const char *key);
|
||||
|
365
src/udev/udev-config.c
Normal file
365
src/udev/udev-config.c
Normal file
@ -0,0 +1,365 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "conf-parser.h"
|
||||
#include "cpu-set-util.h"
|
||||
#include "limits-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "signal-util.h"
|
||||
#include "syslog-util.h"
|
||||
#include "udev-config.h"
|
||||
#include "udev-manager.h"
|
||||
#include "udev-rules.h"
|
||||
#include "udev-util.h"
|
||||
#include "udev-worker.h"
|
||||
#include "version.h"
|
||||
|
||||
#define WORKER_NUM_MAX UINT64_C(2048)
|
||||
|
||||
static bool arg_debug = false;
|
||||
bool arg_daemonize = false;
|
||||
|
||||
static DEFINE_CONFIG_PARSE_ENUM(config_parse_resolve_name_timing, resolve_name_timing, ResolveNameTiming);
|
||||
|
||||
static void manager_parse_udev_config(UdevConfig *config) {
|
||||
assert(config);
|
||||
|
||||
const ConfigTableItem config_table[] = {
|
||||
{ NULL, "udev_log", config_parse_log_level, 0, &config->log_level },
|
||||
{ NULL, "children_max", config_parse_unsigned, 0, &config->children_max },
|
||||
{ NULL, "exec_delay", config_parse_sec, 0, &config->exec_delay_usec },
|
||||
{ NULL, "event_timeout", config_parse_sec, 0, &config->timeout_usec },
|
||||
{ NULL, "resolve_names", config_parse_resolve_name_timing, 0, &config->resolve_name_timing },
|
||||
{ NULL, "timeout_signal", config_parse_signal, 0, &config->timeout_signal },
|
||||
{}
|
||||
};
|
||||
|
||||
(void) udev_parse_config_full(config_table);
|
||||
}
|
||||
|
||||
/*
|
||||
* read the kernel command line, in case we need to get into debug mode
|
||||
* udev.log_level=<level> syslog priority
|
||||
* udev.children_max=<number of workers> events are fully serialized if set to 1
|
||||
* udev.exec_delay=<number of seconds> delay execution of every executed program
|
||||
* udev.event_timeout=<number of seconds> seconds to wait before terminating an event
|
||||
* udev.blockdev_read_only<=bool> mark all block devices read-only when they appear
|
||||
*/
|
||||
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
|
||||
UdevConfig *config = ASSERT_PTR(data);
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
|
||||
if (proc_cmdline_key_streq(key, "udev.log_level") ||
|
||||
proc_cmdline_key_streq(key, "udev.log_priority")) { /* kept for backward compatibility */
|
||||
|
||||
if (proc_cmdline_value_missing(key, value))
|
||||
return 0;
|
||||
|
||||
r = log_level_from_string(value);
|
||||
if (r >= 0)
|
||||
config->log_level = r;
|
||||
|
||||
} else if (proc_cmdline_key_streq(key, "udev.event_timeout")) {
|
||||
|
||||
if (proc_cmdline_value_missing(key, value))
|
||||
return 0;
|
||||
|
||||
r = parse_sec(value, &config->timeout_usec);
|
||||
|
||||
} else if (proc_cmdline_key_streq(key, "udev.children_max")) {
|
||||
|
||||
if (proc_cmdline_value_missing(key, value))
|
||||
return 0;
|
||||
|
||||
r = safe_atou(value, &config->children_max);
|
||||
|
||||
} else if (proc_cmdline_key_streq(key, "udev.exec_delay")) {
|
||||
|
||||
if (proc_cmdline_value_missing(key, value))
|
||||
return 0;
|
||||
|
||||
r = parse_sec(value, &config->exec_delay_usec);
|
||||
|
||||
} else if (proc_cmdline_key_streq(key, "udev.timeout_signal")) {
|
||||
|
||||
if (proc_cmdline_value_missing(key, value))
|
||||
return 0;
|
||||
|
||||
r = signal_from_string(value);
|
||||
if (r > 0)
|
||||
config->timeout_signal = r;
|
||||
|
||||
} else if (proc_cmdline_key_streq(key, "udev.blockdev_read_only")) {
|
||||
|
||||
if (!value)
|
||||
config->blockdev_read_only = true;
|
||||
else {
|
||||
r = parse_boolean(value);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse udev.blockdev-read-only argument, ignoring: %s", value);
|
||||
else
|
||||
config->blockdev_read_only = r;
|
||||
}
|
||||
|
||||
if (config->blockdev_read_only)
|
||||
log_notice("All physical block devices will be marked read-only.");
|
||||
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
if (startswith(key, "udev."))
|
||||
log_warning("Unknown udev kernel command line option \"%s\", ignoring.", key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse \"%s=%s\", ignoring: %m", key, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("systemd-udevd.service", "8", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
printf("%s [OPTIONS...]\n\n"
|
||||
"Rule-based manager for device events and files.\n\n"
|
||||
" -h --help Print this message\n"
|
||||
" -V --version Print version of the program\n"
|
||||
" -d --daemon Detach and run in the background\n"
|
||||
" -D --debug Enable debug output\n"
|
||||
" -c --children-max=INT Set maximum number of workers\n"
|
||||
" -e --exec-delay=SECONDS Seconds to wait before executing RUN=\n"
|
||||
" -t --event-timeout=SECONDS Seconds to wait before terminating an event\n"
|
||||
" -N --resolve-names=early|late|never\n"
|
||||
" When to resolve users and groups\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[], UdevConfig *config) {
|
||||
enum {
|
||||
ARG_TIMEOUT_SIGNAL,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "daemon", no_argument, NULL, 'd' },
|
||||
{ "debug", no_argument, NULL, 'D' },
|
||||
{ "children-max", required_argument, NULL, 'c' },
|
||||
{ "exec-delay", required_argument, NULL, 'e' },
|
||||
{ "event-timeout", required_argument, NULL, 't' },
|
||||
{ "resolve-names", required_argument, NULL, 'N' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{ "timeout-signal", required_argument, NULL, ARG_TIMEOUT_SIGNAL },
|
||||
{}
|
||||
};
|
||||
|
||||
int c, r;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
assert(config);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "c:de:Dt:N:hV", options, NULL)) >= 0) {
|
||||
switch (c) {
|
||||
|
||||
case 'd':
|
||||
arg_daemonize = true;
|
||||
break;
|
||||
case 'c':
|
||||
r = safe_atou(optarg, &config->children_max);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse --children-max= value '%s', ignoring: %m", optarg);
|
||||
break;
|
||||
case 'e':
|
||||
r = parse_sec(optarg, &config->exec_delay_usec);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse --exec-delay= value '%s', ignoring: %m", optarg);
|
||||
break;
|
||||
case ARG_TIMEOUT_SIGNAL:
|
||||
r = signal_from_string(optarg);
|
||||
if (r <= 0)
|
||||
log_warning_errno(r, "Failed to parse --timeout-signal= value '%s', ignoring: %m", optarg);
|
||||
else
|
||||
config->timeout_signal = r;
|
||||
|
||||
break;
|
||||
case 't':
|
||||
r = parse_sec(optarg, &config->timeout_usec);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse --event-timeout= value '%s', ignoring: %m", optarg);
|
||||
break;
|
||||
case 'D':
|
||||
arg_debug = true;
|
||||
config->log_level = LOG_DEBUG;
|
||||
break;
|
||||
case 'N': {
|
||||
ResolveNameTiming t;
|
||||
|
||||
t = resolve_name_timing_from_string(optarg);
|
||||
if (t < 0)
|
||||
log_warning("Invalid --resolve-names= value '%s', ignoring.", optarg);
|
||||
else
|
||||
config->resolve_name_timing = t;
|
||||
break;
|
||||
}
|
||||
case 'h':
|
||||
return help();
|
||||
case 'V':
|
||||
printf("%s\n", GIT_VERSION);
|
||||
return 0;
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
default:
|
||||
assert_not_reached();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define MERGE_NON_NEGATIVE(name, default_value) \
|
||||
manager->config.name = \
|
||||
manager->config_by_control.name >= 0 ? manager->config_by_control.name : \
|
||||
manager->config_by_kernel.name >= 0 ? manager->config_by_kernel.name : \
|
||||
manager->config_by_command.name >= 0 ? manager->config_by_command.name : \
|
||||
manager->config_by_udev_conf.name >= 0 ? manager->config_by_udev_conf.name : \
|
||||
default_value;
|
||||
|
||||
#define MERGE_NON_ZERO(name, default_value) \
|
||||
manager->config.name = \
|
||||
manager->config_by_control.name ?: \
|
||||
manager->config_by_kernel.name ?: \
|
||||
manager->config_by_command.name ?: \
|
||||
manager->config_by_udev_conf.name ?: \
|
||||
default_value;
|
||||
|
||||
#define MERGE_BOOL(name) \
|
||||
manager->config.name = \
|
||||
manager->config_by_control.name || \
|
||||
manager->config_by_kernel.name || \
|
||||
manager->config_by_command.name || \
|
||||
manager->config_by_udev_conf.name;
|
||||
|
||||
static void manager_merge_config(Manager *manager) {
|
||||
assert(manager);
|
||||
|
||||
/* udev.conf has the lowest priority, then followed by command line arguments, kernel command line
|
||||
options, and values set by udev control. */
|
||||
|
||||
MERGE_NON_NEGATIVE(log_level, log_get_max_level());
|
||||
MERGE_NON_NEGATIVE(resolve_name_timing, RESOLVE_NAME_EARLY);
|
||||
MERGE_NON_ZERO(exec_delay_usec, 0);
|
||||
MERGE_NON_ZERO(timeout_usec, DEFAULT_WORKER_TIMEOUT_USEC);
|
||||
MERGE_NON_ZERO(timeout_signal, SIGKILL);
|
||||
MERGE_BOOL(blockdev_read_only);
|
||||
}
|
||||
|
||||
void udev_config_set_default_children_max(UdevConfig *config) {
|
||||
uint64_t cpu_limit, mem_limit, cpu_count = 1;
|
||||
int r;
|
||||
|
||||
assert(config);
|
||||
|
||||
if (config->children_max != 0)
|
||||
return;
|
||||
|
||||
r = cpus_in_affinity_mask();
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to determine number of local CPUs, ignoring: %m");
|
||||
else
|
||||
cpu_count = r;
|
||||
|
||||
cpu_limit = cpu_count * 2 + 16;
|
||||
mem_limit = MAX(physical_memory() / (128*1024*1024), UINT64_C(10));
|
||||
|
||||
config->children_max = MIN3(cpu_limit, mem_limit, WORKER_NUM_MAX);
|
||||
log_debug("Set children_max to %u", config->children_max);
|
||||
}
|
||||
|
||||
static void manager_adjust_config(UdevConfig *config) {
|
||||
assert(config);
|
||||
|
||||
if (config->timeout_usec < MIN_WORKER_TIMEOUT_USEC) {
|
||||
log_debug("Timeout (%s) for processing event is too small, using the default: %s",
|
||||
FORMAT_TIMESPAN(config->timeout_usec, 1),
|
||||
FORMAT_TIMESPAN(DEFAULT_WORKER_TIMEOUT_USEC, 1));
|
||||
|
||||
config->timeout_usec = DEFAULT_WORKER_TIMEOUT_USEC;
|
||||
}
|
||||
|
||||
if (config->exec_delay_usec >= config->timeout_usec) {
|
||||
log_debug("Delay (%s) for executing RUN= commands is too large compared with the timeout (%s) for event execution, ignoring the delay.",
|
||||
FORMAT_TIMESPAN(config->exec_delay_usec, 1),
|
||||
FORMAT_TIMESPAN(config->timeout_usec, 1));
|
||||
|
||||
config->exec_delay_usec = 0;
|
||||
}
|
||||
|
||||
udev_config_set_default_children_max(config);
|
||||
}
|
||||
|
||||
int manager_load(Manager *manager, int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
|
||||
manager_parse_udev_config(&manager->config_by_udev_conf);
|
||||
|
||||
r = parse_argv(argc, argv, &manager->config_by_command);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = proc_cmdline_parse(parse_proc_cmdline_item, &manager->config_by_kernel, PROC_CMDLINE_STRIP_RD_PREFIX);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
|
||||
|
||||
manager_merge_config(manager);
|
||||
|
||||
if (arg_debug)
|
||||
log_set_target(LOG_TARGET_CONSOLE);
|
||||
|
||||
log_set_max_level(manager->config.log_level);
|
||||
manager_adjust_config(&manager->config);
|
||||
return 1;
|
||||
}
|
||||
|
||||
UdevReloadFlags manager_reload_config(Manager *manager) {
|
||||
assert(manager);
|
||||
|
||||
UdevConfig old = manager->config;
|
||||
|
||||
manager->config_by_udev_conf = UDEV_CONFIG_INIT;
|
||||
manager_parse_udev_config(&manager->config_by_udev_conf);
|
||||
manager_merge_config(manager);
|
||||
log_set_max_level(manager->config.log_level);
|
||||
manager_adjust_config(&manager->config);
|
||||
|
||||
if (manager->config.resolve_name_timing != old.resolve_name_timing)
|
||||
return UDEV_RELOAD_RULES | UDEV_RELOAD_KILL_WORKERS;
|
||||
|
||||
if (manager->config.log_level != old.log_level ||
|
||||
manager->config.exec_delay_usec != old.exec_delay_usec ||
|
||||
manager->config.timeout_usec != old.timeout_usec ||
|
||||
manager->config.timeout_signal != old.timeout_signal ||
|
||||
manager->config.blockdev_read_only != old.blockdev_read_only)
|
||||
return UDEV_RELOAD_KILL_WORKERS;
|
||||
|
||||
return 0;
|
||||
}
|
31
src/udev/udev-config.h
Normal file
31
src/udev/udev-config.h
Normal file
@ -0,0 +1,31 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "time-util.h"
|
||||
#include "udev-def.h"
|
||||
|
||||
extern bool arg_daemonize;
|
||||
|
||||
typedef struct Manager Manager;
|
||||
|
||||
typedef struct UdevConfig {
|
||||
int log_level;
|
||||
ResolveNameTiming resolve_name_timing;
|
||||
unsigned children_max;
|
||||
usec_t exec_delay_usec;
|
||||
usec_t timeout_usec;
|
||||
int timeout_signal;
|
||||
bool blockdev_read_only;
|
||||
} UdevConfig;
|
||||
|
||||
#define UDEV_CONFIG_INIT \
|
||||
(UdevConfig) { \
|
||||
.log_level = -1, \
|
||||
.resolve_name_timing = _RESOLVE_NAME_TIMING_INVALID, \
|
||||
}
|
||||
|
||||
int manager_load(Manager *manager, int argc, char *argv[]);
|
||||
UdevReloadFlags manager_reload_config(Manager *manager);
|
||||
void udev_config_set_default_children_max(UdevConfig *c);
|
@ -55,3 +55,26 @@ typedef enum UdevBuiltinCommand {
|
||||
_UDEV_BUILTIN_MAX,
|
||||
_UDEV_BUILTIN_INVALID = -EINVAL,
|
||||
} UdevBuiltinCommand;
|
||||
|
||||
typedef enum UdevReloadFlags {
|
||||
#if HAVE_BLKID
|
||||
UDEV_RELOAD_BUILTIN_BLKID = 1u << UDEV_BUILTIN_BLKID,
|
||||
#endif
|
||||
UDEV_RELOAD_BUILTIN_BTRFS = 1u << UDEV_BUILTIN_BTRFS,
|
||||
UDEV_RELOAD_BUILTIN_HWDB = 1u << UDEV_BUILTIN_HWDB,
|
||||
UDEV_RELOAD_BUILTIN_INPUT_ID = 1u << UDEV_BUILTIN_INPUT_ID,
|
||||
UDEV_RELOAD_BUILTIN_KEYBOARD = 1u << UDEV_BUILTIN_KEYBOARD,
|
||||
#if HAVE_KMOD
|
||||
UDEV_RELOAD_BUILTIN_KMOD = 1u << UDEV_BUILTIN_KMOD,
|
||||
#endif
|
||||
UDEV_RELOAD_BUILTIN_DRIVER = 1u << UDEV_BUILTIN_NET_DRIVER,
|
||||
UDEV_RELOAD_BUILTIN_NET_ID = 1u << UDEV_BUILTIN_NET_ID,
|
||||
UDEV_RELOAD_BUILTIN_NET_LINK = 1u << UDEV_BUILTIN_NET_LINK,
|
||||
UDEV_RELOAD_BUILTIN_PATH_ID = 1u << UDEV_BUILTIN_PATH_ID,
|
||||
UDEV_RELOAD_BUILTIN_USB_ID = 1u << UDEV_BUILTIN_USB_ID,
|
||||
#if HAVE_ACL
|
||||
UDEV_RELOAD_BUILTIN_UACCESS = 1u << UDEV_BUILTIN_UACCESS,
|
||||
#endif
|
||||
UDEV_RELOAD_KILL_WORKERS = 1u << (_UDEV_BUILTIN_MAX + 0),
|
||||
UDEV_RELOAD_RULES = 1u << (_UDEV_BUILTIN_MAX + 1),
|
||||
} UdevReloadFlags;
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "user-util.h"
|
||||
|
||||
UdevEvent* udev_event_new(sd_device *dev, UdevWorker *worker, EventMode mode) {
|
||||
int log_level = worker ? worker->log_level : log_get_max_level();
|
||||
int log_level = worker ? worker->config.log_level : log_get_max_level();
|
||||
UdevEvent *event;
|
||||
|
||||
assert(dev);
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include "blockdev-util.h"
|
||||
#include "cgroup-util.h"
|
||||
#include "common-signal.h"
|
||||
#include "cpu-set-util.h"
|
||||
#include "daemon-util.h"
|
||||
#include "device-monitor-private.h"
|
||||
#include "device-private.h"
|
||||
@ -15,7 +14,6 @@
|
||||
#include "hashmap.h"
|
||||
#include "inotify-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "limits-util.h"
|
||||
#include "list.h"
|
||||
#include "mkdir.h"
|
||||
#include "process-util.h"
|
||||
@ -25,6 +23,7 @@
|
||||
#include "string-util.h"
|
||||
#include "syslog-util.h"
|
||||
#include "udev-builtin.h"
|
||||
#include "udev-config.h"
|
||||
#include "udev-ctrl.h"
|
||||
#include "udev-event.h"
|
||||
#include "udev-manager.h"
|
||||
@ -36,8 +35,6 @@
|
||||
#include "udev-watch.h"
|
||||
#include "udev-worker.h"
|
||||
|
||||
#define WORKER_NUM_MAX UINT64_C(2048)
|
||||
|
||||
#define EVENT_RETRY_INTERVAL_USEC (200 * USEC_PER_MSEC)
|
||||
#define EVENT_RETRY_TIMEOUT_USEC (3 * USEC_PER_MINUTE)
|
||||
|
||||
@ -243,7 +240,7 @@ static void notify_ready(Manager *manager) {
|
||||
|
||||
r = sd_notifyf(/* unset= */ false,
|
||||
"READY=1\n"
|
||||
"STATUS=Processing with %u children at max", manager->children_max);
|
||||
"STATUS=Processing with %u children at max", manager->config.children_max);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
|
||||
}
|
||||
@ -265,23 +262,26 @@ static void manager_reload(Manager *manager, bool force) {
|
||||
/* Reload SELinux label database, to make the child inherit the up-to-date database. */
|
||||
mac_selinux_maybe_reload();
|
||||
|
||||
/* Nothing changed. It is not necessary to reload. */
|
||||
if (!udev_rules_should_reload(manager->rules) && !udev_builtin_should_reload()) {
|
||||
UdevReloadFlags flags = udev_builtin_should_reload();
|
||||
if (udev_rules_should_reload(manager->rules))
|
||||
flags |= UDEV_RELOAD_RULES | UDEV_RELOAD_KILL_WORKERS;
|
||||
if (flags == 0 && !force)
|
||||
/* Neither .rules files nor config files for builtins e.g. .link files changed. It is not
|
||||
* necessary to reload configs. Note, udev.conf is not checked in the above, hence reloaded
|
||||
* when explicitly requested or at least one .rules file or friend is updated. */
|
||||
return;
|
||||
|
||||
if (!force)
|
||||
return;
|
||||
(void) notify_reloading();
|
||||
|
||||
/* If we eat this up, then tell our service manager to just continue */
|
||||
(void) notify_reloading_full("Skipping configuration reloading, nothing changed.");
|
||||
} else {
|
||||
(void) notify_reloading();
|
||||
flags |= manager_reload_config(manager);
|
||||
|
||||
if (FLAGS_SET(flags, UDEV_RELOAD_KILL_WORKERS))
|
||||
manager_kill_workers(manager, false);
|
||||
|
||||
udev_builtin_exit();
|
||||
udev_builtin_init();
|
||||
udev_builtin_reload(flags);
|
||||
|
||||
r = udev_rules_load(&rules, manager->resolve_name_timing);
|
||||
if (FLAGS_SET(flags, UDEV_RELOAD_RULES)) {
|
||||
r = udev_rules_load(&rules, manager->config.resolve_name_timing);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to read udev rules, using the previously loaded rules, ignoring: %m");
|
||||
else
|
||||
@ -306,7 +306,7 @@ static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
assert(event->manager);
|
||||
assert(event->worker);
|
||||
|
||||
kill_and_sigcont(event->worker->pid, event->manager->timeout_signal);
|
||||
kill_and_sigcont(event->worker->pid, event->manager->config.timeout_signal);
|
||||
event->worker->state = WORKER_KILLED;
|
||||
|
||||
log_device_error(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" killed", event->worker->pid, event->seqnum);
|
||||
@ -366,7 +366,7 @@ static void worker_attach_event(Worker *worker, Event *event) {
|
||||
event->worker = worker;
|
||||
|
||||
(void) sd_event_add_time_relative(e, &event->timeout_warning_event, CLOCK_MONOTONIC,
|
||||
udev_warn_timeout(manager->timeout_usec), USEC_PER_SEC,
|
||||
udev_warn_timeout(manager->config.timeout_usec), USEC_PER_SEC,
|
||||
on_event_timeout_warning, event);
|
||||
|
||||
/* Manager.timeout_usec is also used as the timeout for running programs specified in
|
||||
@ -374,7 +374,7 @@ static void worker_attach_event(Worker *worker, Event *event) {
|
||||
* kills a worker, to make it possible that the worker detects timed out of spawned programs,
|
||||
* kills them, and finalizes the event. */
|
||||
(void) sd_event_add_time_relative(e, &event->timeout_event, CLOCK_MONOTONIC,
|
||||
usec_add(manager->timeout_usec, extra_timeout_usec()), USEC_PER_SEC,
|
||||
usec_add(manager->config.timeout_usec, extra_timeout_usec()), USEC_PER_SEC,
|
||||
on_event_timeout, event);
|
||||
}
|
||||
|
||||
@ -408,11 +408,7 @@ static int worker_spawn(Manager *manager, Event *event) {
|
||||
.rules = TAKE_PTR(manager->rules),
|
||||
.pipe_fd = TAKE_FD(manager->worker_watch[WRITE_END]),
|
||||
.inotify_fd = TAKE_FD(manager->inotify_fd),
|
||||
.exec_delay_usec = manager->exec_delay_usec,
|
||||
.timeout_usec = manager->timeout_usec,
|
||||
.timeout_signal = manager->timeout_signal,
|
||||
.log_level = manager->log_level,
|
||||
.blockdev_read_only = manager->blockdev_read_only,
|
||||
.config = manager->config,
|
||||
};
|
||||
|
||||
/* Worker process */
|
||||
@ -461,10 +457,10 @@ static int event_run(Event *event) {
|
||||
return 1; /* event is now processing. */
|
||||
}
|
||||
|
||||
if (hashmap_size(manager->workers) >= manager->children_max) {
|
||||
if (hashmap_size(manager->workers) >= manager->config.children_max) {
|
||||
/* Avoid spamming the debug logs if the limit is already reached and
|
||||
* many events still need to be processed */
|
||||
if (log_children_max_reached && manager->children_max > 1) {
|
||||
if (log_children_max_reached && manager->config.children_max > 1) {
|
||||
log_debug("Maximum number (%u) of children reached.", hashmap_size(manager->workers));
|
||||
log_children_max_reached = false;
|
||||
}
|
||||
@ -845,28 +841,6 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void manager_set_default_children_max(Manager *manager) {
|
||||
uint64_t cpu_limit, mem_limit, cpu_count = 1;
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
|
||||
if (manager->children_max != 0)
|
||||
return;
|
||||
|
||||
r = cpus_in_affinity_mask();
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to determine number of local CPUs, ignoring: %m");
|
||||
else
|
||||
cpu_count = r;
|
||||
|
||||
cpu_limit = cpu_count * 2 + 16;
|
||||
mem_limit = MAX(physical_memory() / (128*1024*1024), UINT64_C(10));
|
||||
|
||||
manager->children_max = MIN3(cpu_limit, mem_limit, WORKER_NUM_MAX);
|
||||
log_debug("Set children_max to %u", manager->children_max);
|
||||
}
|
||||
|
||||
/* receive the udevd message from userspace */
|
||||
static int on_ctrl_msg(UdevCtrl *uctrl, UdevCtrlMessageType type, const UdevCtrlMessageValue *value, void *userdata) {
|
||||
Manager *manager = ASSERT_PTR(userdata);
|
||||
@ -888,7 +862,7 @@ static int on_ctrl_msg(UdevCtrl *uctrl, UdevCtrlMessageType type, const UdevCtrl
|
||||
break;
|
||||
|
||||
log_set_max_level(value->intval);
|
||||
manager->log_level = value->intval;
|
||||
manager->config.log_level = manager->config_by_control.log_level = value->intval;
|
||||
manager_kill_workers(manager, false);
|
||||
break;
|
||||
case UDEV_CTRL_STOP_EXEC_QUEUE:
|
||||
@ -959,10 +933,11 @@ static int on_ctrl_msg(UdevCtrl *uctrl, UdevCtrlMessageType type, const UdevCtrl
|
||||
}
|
||||
|
||||
log_debug("Received udev control message (SET_MAX_CHILDREN), setting children_max=%i", value->intval);
|
||||
manager->children_max = value->intval;
|
||||
manager->config_by_control.children_max = value->intval;
|
||||
|
||||
/* When 0 is specified, determine the maximum based on the system resources. */
|
||||
manager_set_default_children_max(manager);
|
||||
udev_config_set_default_children_max(&manager->config_by_control);
|
||||
manager->config.children_max = manager->config_by_control.children_max;
|
||||
|
||||
notify_ready(manager);
|
||||
break;
|
||||
@ -1199,35 +1174,16 @@ Manager* manager_new(void) {
|
||||
*manager = (Manager) {
|
||||
.inotify_fd = -EBADF,
|
||||
.worker_watch = EBADF_PAIR,
|
||||
.log_level = LOG_INFO,
|
||||
.resolve_name_timing = RESOLVE_NAME_EARLY,
|
||||
.timeout_usec = DEFAULT_WORKER_TIMEOUT_USEC,
|
||||
.timeout_signal = SIGKILL,
|
||||
.config_by_udev_conf = UDEV_CONFIG_INIT,
|
||||
.config_by_command = UDEV_CONFIG_INIT,
|
||||
.config_by_kernel = UDEV_CONFIG_INIT,
|
||||
.config_by_control = UDEV_CONFIG_INIT,
|
||||
.config = UDEV_CONFIG_INIT,
|
||||
};
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
void manager_adjust_arguments(Manager *manager) {
|
||||
assert(manager);
|
||||
|
||||
if (manager->timeout_usec < MIN_WORKER_TIMEOUT_USEC) {
|
||||
log_debug("Timeout (%s) for processing event is too small, using the default: %s",
|
||||
FORMAT_TIMESPAN(manager->timeout_usec, 1),
|
||||
FORMAT_TIMESPAN(DEFAULT_WORKER_TIMEOUT_USEC, 1));
|
||||
|
||||
manager->timeout_usec = DEFAULT_WORKER_TIMEOUT_USEC;
|
||||
}
|
||||
|
||||
if (manager->exec_delay_usec >= manager->timeout_usec) {
|
||||
log_debug("Delay (%s) for executing RUN= commands is too large compared with the timeout (%s) for event execution, ignoring the delay.",
|
||||
FORMAT_TIMESPAN(manager->exec_delay_usec, 1),
|
||||
FORMAT_TIMESPAN(manager->timeout_usec, 1));
|
||||
|
||||
manager->exec_delay_usec = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int listen_fds(int *ret_ctrl, int *ret_netlink) {
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
_cleanup_close_ int ctrl_fd = -EBADF, netlink_fd = -EBADF;
|
||||
@ -1303,8 +1259,6 @@ int manager_init(Manager *manager) {
|
||||
|
||||
(void) sd_device_monitor_set_description(manager->monitor, "manager");
|
||||
|
||||
manager->log_level = log_get_max_level();
|
||||
|
||||
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to get cgroup, ignoring: %m");
|
||||
@ -1319,8 +1273,6 @@ int manager_init(Manager *manager) {
|
||||
int manager_main(Manager *manager) {
|
||||
int fd_worker, r;
|
||||
|
||||
manager_set_default_children_max(manager);
|
||||
|
||||
/* unnamed socket from workers to the main daemon */
|
||||
r = socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, manager->worker_watch);
|
||||
if (r < 0)
|
||||
@ -1412,7 +1364,7 @@ int manager_main(Manager *manager) {
|
||||
|
||||
udev_builtin_init();
|
||||
|
||||
r = udev_rules_load(&manager->rules, manager->resolve_name_timing);
|
||||
r = udev_rules_load(&manager->rules, manager->config.resolve_name_timing);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read udev rules: %m");
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "hashmap.h"
|
||||
#include "macro.h"
|
||||
#include "time-util.h"
|
||||
#include "udev-config.h"
|
||||
#include "udev-ctrl.h"
|
||||
#include "udev-def.h"
|
||||
|
||||
@ -21,7 +22,6 @@ typedef struct Manager {
|
||||
Hashmap *workers;
|
||||
LIST_HEAD(Event, events);
|
||||
char *cgroup;
|
||||
int log_level;
|
||||
|
||||
UdevRules *rules;
|
||||
Hashmap *properties;
|
||||
@ -38,12 +38,11 @@ typedef struct Manager {
|
||||
|
||||
usec_t last_usec;
|
||||
|
||||
ResolveNameTiming resolve_name_timing;
|
||||
unsigned children_max;
|
||||
usec_t exec_delay_usec;
|
||||
usec_t timeout_usec;
|
||||
int timeout_signal;
|
||||
bool blockdev_read_only;
|
||||
UdevConfig config_by_udev_conf;
|
||||
UdevConfig config_by_command;
|
||||
UdevConfig config_by_kernel;
|
||||
UdevConfig config_by_control;
|
||||
UdevConfig config;
|
||||
|
||||
bool stop_exec_queue;
|
||||
bool exit;
|
||||
@ -53,7 +52,6 @@ Manager* manager_new(void);
|
||||
Manager* manager_free(Manager *manager);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
|
||||
|
||||
void manager_adjust_arguments(Manager *manager);
|
||||
int manager_init(Manager *manager);
|
||||
int manager_main(Manager *manager);
|
||||
|
||||
|
@ -240,8 +240,8 @@ int udev_event_spawn(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int timeout_signal = event->worker ? event->worker->timeout_signal : SIGKILL;
|
||||
usec_t timeout_usec = event->worker ? event->worker->timeout_usec : DEFAULT_WORKER_TIMEOUT_USEC;
|
||||
int timeout_signal = event->worker ? event->worker->config.timeout_signal : SIGKILL;
|
||||
usec_t timeout_usec = event->worker ? event->worker->config.timeout_usec : DEFAULT_WORKER_TIMEOUT_USEC;
|
||||
usec_t now_usec = now(CLOCK_MONOTONIC);
|
||||
usec_t age_usec = usec_sub_unsigned(now_usec, event->birth_usec);
|
||||
usec_t cmd_timeout_usec = usec_sub_unsigned(timeout_usec, age_usec);
|
||||
@ -349,20 +349,20 @@ void udev_event_execute_run(UdevEvent *event) {
|
||||
if (r < 0)
|
||||
log_device_debug_errno(event->dev, r, "Failed to run built-in command \"%s\", ignoring: %m", command);
|
||||
} else {
|
||||
if (event->worker && event->worker->exec_delay_usec > 0) {
|
||||
if (event->worker && event->worker->config.exec_delay_usec > 0) {
|
||||
usec_t now_usec = now(CLOCK_MONOTONIC);
|
||||
usec_t age_usec = usec_sub_unsigned(now_usec, event->birth_usec);
|
||||
|
||||
if (event->worker->exec_delay_usec >= usec_sub_unsigned(event->worker->timeout_usec, age_usec)) {
|
||||
if (event->worker->config.exec_delay_usec >= usec_sub_unsigned(event->worker->config.timeout_usec, age_usec)) {
|
||||
log_device_warning(event->dev,
|
||||
"Cannot delay execution of \"%s\" for %s, skipping.",
|
||||
command, FORMAT_TIMESPAN(event->worker->exec_delay_usec, USEC_PER_SEC));
|
||||
command, FORMAT_TIMESPAN(event->worker->config.exec_delay_usec, USEC_PER_SEC));
|
||||
continue;
|
||||
}
|
||||
|
||||
log_device_debug(event->dev, "Delaying execution of \"%s\" for %s.",
|
||||
command, FORMAT_TIMESPAN(event->worker->exec_delay_usec, USEC_PER_SEC));
|
||||
(void) usleep_safe(event->worker->exec_delay_usec);
|
||||
command, FORMAT_TIMESPAN(event->worker->config.exec_delay_usec, USEC_PER_SEC));
|
||||
(void) usleep_safe(event->worker->config.exec_delay_usec);
|
||||
}
|
||||
|
||||
log_device_debug(event->dev, "Running command \"%s\"", command);
|
||||
|
@ -194,7 +194,7 @@ static int worker_process_device(UdevWorker *worker, sd_device *dev) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (worker->blockdev_read_only)
|
||||
if (worker->config.blockdev_read_only)
|
||||
(void) worker_mark_block_device_read_only(dev);
|
||||
|
||||
/* Disable watch during event processing. */
|
||||
@ -321,7 +321,7 @@ static int worker_device_monitor_handler(sd_device_monitor *monitor, sd_device *
|
||||
log_device_warning_errno(dev, r, "Failed to send signal to main daemon, ignoring: %m");
|
||||
|
||||
/* Reset the log level, as it might be changed by "OPTIONS=log_level=". */
|
||||
log_set_max_level(worker->log_level);
|
||||
log_set_max_level(worker->config.log_level);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "errno-list.h"
|
||||
#include "hashmap.h"
|
||||
#include "time-util.h"
|
||||
#include "udev-config.h"
|
||||
|
||||
#define DEFAULT_WORKER_TIMEOUT_USEC (3 * USEC_PER_MINUTE)
|
||||
#define MIN_WORKER_TIMEOUT_USEC (1 * USEC_PER_MSEC)
|
||||
@ -27,11 +28,7 @@ typedef struct UdevWorker {
|
||||
int pipe_fd;
|
||||
int inotify_fd; /* Do not close! */
|
||||
|
||||
usec_t exec_delay_usec;
|
||||
usec_t timeout_usec;
|
||||
int timeout_signal;
|
||||
int log_level;
|
||||
bool blockdev_read_only;
|
||||
UdevConfig config;
|
||||
} UdevWorker;
|
||||
|
||||
/* passed from worker to main process */
|
||||
|
250
src/udev/udevd.c
250
src/udev/udevd.c
@ -5,250 +5,17 @@
|
||||
* Copyright © 2009 Scott James Remnant <scott@netsplit.com>
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "conf-parser.h"
|
||||
#include "env-file.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "mkdir.h"
|
||||
#include "parse-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "process-util.h"
|
||||
#include "rlimit-util.h"
|
||||
#include "selinux-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "syslog-util.h"
|
||||
#include "udev-config.h"
|
||||
#include "udev-manager.h"
|
||||
#include "udev-rules.h"
|
||||
#include "udev-util.h"
|
||||
#include "udevd.h"
|
||||
#include "version.h"
|
||||
|
||||
static bool arg_debug = false;
|
||||
static int arg_daemonize = false;
|
||||
|
||||
static DEFINE_CONFIG_PARSE_ENUM(config_parse_resolve_name_timing, resolve_name_timing, ResolveNameTiming);
|
||||
|
||||
static int manager_parse_udev_config(Manager *manager) {
|
||||
int r, log_val = -1;
|
||||
|
||||
assert(manager);
|
||||
|
||||
const ConfigTableItem config_table[] = {
|
||||
{ NULL, "udev_log", config_parse_log_level, 0, &log_val },
|
||||
{ NULL, "children_max", config_parse_unsigned, 0, &manager->children_max },
|
||||
{ NULL, "exec_delay", config_parse_sec, 0, &manager->exec_delay_usec },
|
||||
{ NULL, "event_timeout", config_parse_sec, 0, &manager->timeout_usec },
|
||||
{ NULL, "resolve_names", config_parse_resolve_name_timing, 0, &manager->resolve_name_timing },
|
||||
{ NULL, "timeout_signal", config_parse_signal, 0, &manager->timeout_signal },
|
||||
{}
|
||||
};
|
||||
|
||||
r = udev_parse_config_full(config_table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (log_val >= 0)
|
||||
log_set_max_level(log_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* read the kernel command line, in case we need to get into debug mode
|
||||
* udev.log_level=<level> syslog priority
|
||||
* udev.children_max=<number of workers> events are fully serialized if set to 1
|
||||
* udev.exec_delay=<number of seconds> delay execution of every executed program
|
||||
* udev.event_timeout=<number of seconds> seconds to wait before terminating an event
|
||||
* udev.blockdev_read_only<=bool> mark all block devices read-only when they appear
|
||||
*/
|
||||
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
|
||||
Manager *manager = ASSERT_PTR(data);
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
|
||||
if (proc_cmdline_key_streq(key, "udev.log_level") ||
|
||||
proc_cmdline_key_streq(key, "udev.log_priority")) { /* kept for backward compatibility */
|
||||
|
||||
if (proc_cmdline_value_missing(key, value))
|
||||
return 0;
|
||||
|
||||
r = log_level_from_string(value);
|
||||
if (r >= 0)
|
||||
log_set_max_level(r);
|
||||
|
||||
} else if (proc_cmdline_key_streq(key, "udev.event_timeout")) {
|
||||
|
||||
if (proc_cmdline_value_missing(key, value))
|
||||
return 0;
|
||||
|
||||
r = parse_sec(value, &manager->timeout_usec);
|
||||
|
||||
} else if (proc_cmdline_key_streq(key, "udev.children_max")) {
|
||||
|
||||
if (proc_cmdline_value_missing(key, value))
|
||||
return 0;
|
||||
|
||||
r = safe_atou(value, &manager->children_max);
|
||||
|
||||
} else if (proc_cmdline_key_streq(key, "udev.exec_delay")) {
|
||||
|
||||
if (proc_cmdline_value_missing(key, value))
|
||||
return 0;
|
||||
|
||||
r = parse_sec(value, &manager->exec_delay_usec);
|
||||
|
||||
} else if (proc_cmdline_key_streq(key, "udev.timeout_signal")) {
|
||||
|
||||
if (proc_cmdline_value_missing(key, value))
|
||||
return 0;
|
||||
|
||||
r = signal_from_string(value);
|
||||
if (r > 0)
|
||||
manager->timeout_signal = r;
|
||||
|
||||
} else if (proc_cmdline_key_streq(key, "udev.blockdev_read_only")) {
|
||||
|
||||
if (!value)
|
||||
manager->blockdev_read_only = true;
|
||||
else {
|
||||
r = parse_boolean(value);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse udev.blockdev-read-only argument, ignoring: %s", value);
|
||||
else
|
||||
manager->blockdev_read_only = r;
|
||||
}
|
||||
|
||||
if (manager->blockdev_read_only)
|
||||
log_notice("All physical block devices will be marked read-only.");
|
||||
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
if (startswith(key, "udev."))
|
||||
log_warning("Unknown udev kernel command line option \"%s\", ignoring.", key);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse \"%s=%s\", ignoring: %m", key, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("systemd-udevd.service", "8", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
printf("%s [OPTIONS...]\n\n"
|
||||
"Rule-based manager for device events and files.\n\n"
|
||||
" -h --help Print this message\n"
|
||||
" -V --version Print version of the program\n"
|
||||
" -d --daemon Detach and run in the background\n"
|
||||
" -D --debug Enable debug output\n"
|
||||
" -c --children-max=INT Set maximum number of workers\n"
|
||||
" -e --exec-delay=SECONDS Seconds to wait before executing RUN=\n"
|
||||
" -t --event-timeout=SECONDS Seconds to wait before terminating an event\n"
|
||||
" -N --resolve-names=early|late|never\n"
|
||||
" When to resolve users and groups\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[], Manager *manager) {
|
||||
enum {
|
||||
ARG_TIMEOUT_SIGNAL,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "daemon", no_argument, NULL, 'd' },
|
||||
{ "debug", no_argument, NULL, 'D' },
|
||||
{ "children-max", required_argument, NULL, 'c' },
|
||||
{ "exec-delay", required_argument, NULL, 'e' },
|
||||
{ "event-timeout", required_argument, NULL, 't' },
|
||||
{ "resolve-names", required_argument, NULL, 'N' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{ "timeout-signal", required_argument, NULL, ARG_TIMEOUT_SIGNAL },
|
||||
{}
|
||||
};
|
||||
|
||||
int c, r;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
assert(manager);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "c:de:Dt:N:hV", options, NULL)) >= 0) {
|
||||
switch (c) {
|
||||
|
||||
case 'd':
|
||||
arg_daemonize = true;
|
||||
break;
|
||||
case 'c':
|
||||
r = safe_atou(optarg, &manager->children_max);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse --children-max= value '%s', ignoring: %m", optarg);
|
||||
break;
|
||||
case 'e':
|
||||
r = parse_sec(optarg, &manager->exec_delay_usec);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse --exec-delay= value '%s', ignoring: %m", optarg);
|
||||
break;
|
||||
case ARG_TIMEOUT_SIGNAL:
|
||||
r = signal_from_string(optarg);
|
||||
if (r <= 0)
|
||||
log_warning_errno(r, "Failed to parse --timeout-signal= value '%s', ignoring: %m", optarg);
|
||||
else
|
||||
manager->timeout_signal = r;
|
||||
|
||||
break;
|
||||
case 't':
|
||||
r = parse_sec(optarg, &manager->timeout_usec);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse --event-timeout= value '%s', ignoring: %m", optarg);
|
||||
break;
|
||||
case 'D':
|
||||
arg_debug = true;
|
||||
break;
|
||||
case 'N': {
|
||||
ResolveNameTiming t;
|
||||
|
||||
t = resolve_name_timing_from_string(optarg);
|
||||
if (t < 0)
|
||||
log_warning("Invalid --resolve-names= value '%s', ignoring.", optarg);
|
||||
else
|
||||
manager->resolve_name_timing = t;
|
||||
break;
|
||||
}
|
||||
case 'h':
|
||||
return help();
|
||||
case 'V':
|
||||
printf("%s\n", GIT_VERSION);
|
||||
return 0;
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
default:
|
||||
assert_not_reached();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int run_udevd(int argc, char *argv[]) {
|
||||
_cleanup_(manager_freep) Manager *manager = NULL;
|
||||
int r;
|
||||
@ -259,23 +26,10 @@ int run_udevd(int argc, char *argv[]) {
|
||||
if (!manager)
|
||||
return log_oom();
|
||||
|
||||
manager_parse_udev_config(manager);
|
||||
|
||||
r = parse_argv(argc, argv, manager);
|
||||
r = manager_load(manager, argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = proc_cmdline_parse(parse_proc_cmdline_item, manager, PROC_CMDLINE_STRIP_RD_PREFIX);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
|
||||
|
||||
if (arg_debug) {
|
||||
log_set_target(LOG_TARGET_CONSOLE);
|
||||
log_set_max_level(LOG_DEBUG);
|
||||
}
|
||||
|
||||
manager_adjust_arguments(manager);
|
||||
|
||||
r = must_be_root();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -264,7 +264,7 @@ class UkifyConfig:
|
||||
sbat: Optional[list[str]]
|
||||
sections: list['Section']
|
||||
sections_by_name: dict[str, 'Section']
|
||||
sign_kernel: bool
|
||||
sign_kernel: Optional[bool]
|
||||
signing_engine: Optional[str]
|
||||
signing_provider: Optional[str]
|
||||
certificate_provider: Optional[str]
|
||||
@ -1108,7 +1108,7 @@ def make_uki(opts: UkifyConfig) -> None:
|
||||
assert opts.signtool is not None
|
||||
signtool = SignTool.from_string(opts.signtool)
|
||||
|
||||
if not sign_kernel:
|
||||
if sign_kernel is None:
|
||||
# figure out if we should sign the kernel
|
||||
sign_kernel = signtool.verify(opts)
|
||||
|
||||
|
@ -53,6 +53,12 @@ static int get_startup_monotonic_time(Context *c, usec_t *ret) {
|
||||
assert(c);
|
||||
assert(ret);
|
||||
|
||||
if (!c->bus) {
|
||||
r = bus_connect_system_systemd(&c->bus);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to get D-Bus connection, ignoring: %m");
|
||||
}
|
||||
|
||||
r = bus_get_property_trivial(
|
||||
c->bus,
|
||||
bus_systemd_mgr,
|
||||
@ -94,10 +100,13 @@ static int get_current_runlevel(Context *c) {
|
||||
UINT64_C(100) * USEC_PER_MSEC +
|
||||
random_u64_range(UINT64_C(1900) * USEC_PER_MSEC * n_attempts / MAX_ATTEMPTS);
|
||||
(void) usleep_safe(usec);
|
||||
}
|
||||
|
||||
if (!c->bus) {
|
||||
r = bus_connect_system_systemd(&c->bus);
|
||||
if (r == -ECONNREFUSED && n_attempts < 64) {
|
||||
log_debug_errno(r, "Failed to reconnect to system bus, retrying after a slight delay: %m");
|
||||
log_debug_errno(r, "Failed to %s to system bus, retrying after a slight delay: %m",
|
||||
n_attempts <= 1 ? "connect" : "reconnect");
|
||||
continue;
|
||||
}
|
||||
if (r < 0)
|
||||
@ -251,7 +260,6 @@ static int run(int argc, char *argv[]) {
|
||||
.audit_fd = -EBADF,
|
||||
#endif
|
||||
};
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
@ -264,9 +272,6 @@ static int run(int argc, char *argv[]) {
|
||||
log_full_errno(IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT) ? LOG_DEBUG : LOG_WARNING,
|
||||
errno, "Failed to connect to audit log, ignoring: %m");
|
||||
#endif
|
||||
r = bus_connect_system_systemd(&c.bus);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get D-Bus connection: %m");
|
||||
|
||||
return dispatch_verb(argc, argv, verbs, &c);
|
||||
}
|
||||
|
@ -3,5 +3,11 @@
|
||||
integration_tests += [
|
||||
integration_test_template + {
|
||||
'name' : fs.name(meson.current_source_dir()),
|
||||
'configuration' : integration_test_template['configuration'] + {
|
||||
# Do not request user session, as it may trigger to start
|
||||
# hostnamed in an unexpected timing, and the test may fail.
|
||||
'wants' : 'multi-user.target',
|
||||
'after' : 'multi-user.target',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
@ -42,6 +42,8 @@ create_dummy_container /var/lib/machines/long-running
|
||||
cat >/var/lib/machines/long-running/sbin/init <<\EOF
|
||||
#!/usr/bin/bash
|
||||
|
||||
set -x
|
||||
|
||||
PID=0
|
||||
|
||||
trap 'touch /terminate; kill 0' RTMIN+3
|
||||
@ -310,6 +312,10 @@ varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Unreg
|
||||
# test io.systemd.Machine.List with addresses, OSRelease, and UIDShift fields
|
||||
create_dummy_container "/var/lib/machines/container-without-os-release"
|
||||
cat >>/var/lib/machines/container-without-os-release/sbin/init <<\EOF
|
||||
#!/usr/bin/bash
|
||||
|
||||
set -x
|
||||
|
||||
ip link add hoge type dummy
|
||||
ip link set hoge up
|
||||
ip address add 192.0.2.1/24 dev hoge
|
||||
|
@ -27,7 +27,7 @@ event_timeout=10
|
||||
timeout_signal=SIGABRT
|
||||
EOF
|
||||
|
||||
systemctl restart systemd-udevd.service
|
||||
systemctl reload systemd-udevd.service
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2317
|
||||
@ -40,7 +40,7 @@ teardown() {
|
||||
|
||||
rm -rf "$TMPDIR"
|
||||
rm -f "$TEST_RULE" "$TEST_CONF"
|
||||
systemctl restart systemd-udevd.service
|
||||
systemctl reload systemd-udevd.service
|
||||
}
|
||||
|
||||
run_test_timeout() {
|
||||
|
@ -18,7 +18,7 @@ at_exit() {
|
||||
# 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
|
||||
udevadm control --reload
|
||||
ip link del "$IFNAME"
|
||||
}
|
||||
|
||||
@ -31,18 +31,50 @@ cat >/run/udev/udev.conf.d/timeout.conf <<EOF
|
||||
event_timeout=1h
|
||||
EOF
|
||||
|
||||
# First, test 'add' event.
|
||||
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"
|
||||
SUBSYSTEM=="net", ACTION=="add", KERNEL=="${IFNAME}", OPTIONS="log_level=debug", RUN+="/usr/bin/sleep 1000"
|
||||
EOF
|
||||
|
||||
systemctl restart systemd-udevd.service
|
||||
udevadm control --reload
|
||||
|
||||
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}" ]]
|
||||
timeout 30 bash -c "until [[ -e /run/udev/data/n${IFINDEX} ]] && grep -q -F 'ID_PROCESSING=1' /run/udev/data/n${IFINDEX}; do sleep .5; done"
|
||||
|
||||
(! systemctl is-active "sys-devices-virtual-net-${IFNAME}.device")
|
||||
(! systemctl is-active "sys-subsystem-net-devices-${IFNAME}.device")
|
||||
|
||||
for _ in {1..3}; do
|
||||
systemctl daemon-reexec
|
||||
(! systemctl is-active "sys-devices-virtual-net-${IFNAME}.device")
|
||||
(! systemctl is-active "sys-subsystem-net-devices-${IFNAME}.device")
|
||||
done
|
||||
|
||||
for _ in {1..3}; do
|
||||
systemctl daemon-reload
|
||||
(! systemctl is-active "sys-devices-virtual-net-${IFNAME}.device")
|
||||
(! systemctl is-active "sys-subsystem-net-devices-${IFNAME}.device")
|
||||
done
|
||||
|
||||
# Check if the reexec and reload have finished during processing the event.
|
||||
grep -q -F 'ID_PROCESSING=1' "/run/udev/data/n${IFINDEX}"
|
||||
|
||||
# Forcibly kill sleep command ivoked by the udev rule to finish processing the add event.
|
||||
killall sleep
|
||||
udevadm settle --timeout=20
|
||||
|
||||
# Check if ID_PROCESSING flag is unset, and the device units are active.
|
||||
(! grep -q -F 'ID_PROCESSING=1' "/run/udev/data/n${IFINDEX}")
|
||||
systemctl is-active "sys-devices-virtual-net-${IFNAME}.device"
|
||||
systemctl is-active "sys-subsystem-net-devices-${IFNAME}.device"
|
||||
|
||||
# Next, test 'change' event.
|
||||
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
|
||||
udevadm control --reload
|
||||
|
||||
systemd-run \
|
||||
-p After="sys-subsystem-net-devices-${IFNAME}.device" \
|
||||
@ -50,22 +82,29 @@ systemd-run \
|
||||
-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"
|
||||
timeout 30 bash -c "until grep -q -F 'ID_PROCESSING=1' /run/udev/data/n${IFINDEX}; do sleep .5; done"
|
||||
|
||||
# Check if the service and device units are still active even ID_PROCESSING flag is set.
|
||||
systemctl is-active testsleep.service
|
||||
systemctl is-active "sys-devices-virtual-net-${IFNAME}.device"
|
||||
systemctl is-active "sys-subsystem-net-devices-${IFNAME}.device"
|
||||
|
||||
for _ in {1..3}; do
|
||||
systemctl daemon-reexec
|
||||
systemctl is-active testsleep.service
|
||||
systemctl is-active "sys-devices-virtual-net-${IFNAME}.device"
|
||||
systemctl is-active "sys-subsystem-net-devices-${IFNAME}.device"
|
||||
done
|
||||
|
||||
for _ in {1..3}; do
|
||||
systemctl daemon-reload
|
||||
systemctl is-active testsleep.service
|
||||
systemctl is-active "sys-devices-virtual-net-${IFNAME}.device"
|
||||
systemctl is-active "sys-subsystem-net-devices-${IFNAME}.device"
|
||||
done
|
||||
|
||||
# Check if the reexec and reload have finished during processing the event.
|
||||
grep -F 'ID_PROCESSING=1' "/run/udev/data/n${IFINDEX}"
|
||||
grep -q -F 'ID_PROCESSING=1' "/run/udev/data/n${IFINDEX}"
|
||||
|
||||
exit 0
|
||||
|
@ -85,31 +85,25 @@ systemd-run \
|
||||
--wait \
|
||||
-p RootImage="$MINIMAL_IMAGE.raw" \
|
||||
-p NotifyAccess=all \
|
||||
--service-type=notify \
|
||||
--pipe \
|
||||
bash -xec \
|
||||
'
|
||||
printf MAINPID=$$$$\\nREADY=1 | ncat --unixsock --udp $NOTIFY_SOCKET --source /run/notify
|
||||
[[ "$$NOTIFY_SOCKET" == "/run/host/notify" ]]
|
||||
[[ "$$(env)" =~ "NOTIFY_SOCKET=/run/host/notify" ]]
|
||||
test -S /run/host/notify
|
||||
'
|
||||
if [[ "$(findmnt -n -o FSTYPE /)" == btrfs ]]; then
|
||||
[[ -d /test-dissect-btrfs-snapshot ]] && btrfs subvolume delete /test-dissect-btrfs-snapshot
|
||||
btrfs subvolume snapshot / /test-dissect-btrfs-snapshot
|
||||
|
||||
# Same test with systemd-notify and RootDirectory=
|
||||
# Same test with RootDirectory=, also try to send READY=1, as we can use systemd-notify.
|
||||
systemd-run \
|
||||
--wait \
|
||||
-p RootDirectory=/test-dissect-btrfs-snapshot \
|
||||
-p NotifyAccess=all \
|
||||
--service-type=notify \
|
||||
--pipe \
|
||||
bash -xec \
|
||||
'
|
||||
systemd-notify --pid=auto --ready
|
||||
[[ "$$NOTIFY_SOCKET" == "/run/host/notify" ]]
|
||||
[[ "$(env)" =~ "NOTIFY_SOCKET=/run/host/notify" ]]
|
||||
test -S /run/host/notify
|
||||
'
|
||||
|
||||
|
@ -490,7 +490,7 @@ cmp /tmp/vlcredsdata /tmp/vlcredsdata2
|
||||
rm /tmp/vlcredsdata /tmp/vlcredsdata2
|
||||
|
||||
clean_usertest() {
|
||||
rm -f /tmp/usertest.data /tmp/usertest.data
|
||||
rm -f /tmp/usertest.data /tmp/usertest.data /tmp/brummbaer.data
|
||||
}
|
||||
|
||||
trap clean_usertest EXIT
|
||||
@ -520,6 +520,12 @@ XDG_RUNTIME_DIR=/run/user/0 systemd-run --pipe --user --unit=waldi.service -p Lo
|
||||
# Test mount unit with credential
|
||||
test_mount_with_credential
|
||||
|
||||
# Fully unpriv operation
|
||||
dd if=/dev/urandom of=/tmp/brummbaer.data bs=4096 count=1
|
||||
run0 -u testuser --pipe mkdir -p /home/testuser/.config/credstore.encrypted
|
||||
run0 -u testuser --pipe systemd-creds encrypt --user --name=brummbaer - /home/testuser/.config/credstore.encrypted/brummbaer < /tmp/brummbaer.data
|
||||
run0 -u testuser --pipe systemd-run --user --pipe -p ImportCredential=brummbaer systemd-creds cat brummbaer | cmp /tmp/brummbaer.data
|
||||
|
||||
systemd-analyze log-level info
|
||||
|
||||
touch /testok
|
||||
|
@ -990,6 +990,11 @@ systemd-analyze architectures uname
|
||||
systemd-analyze smbios11
|
||||
systemd-analyze smbios11 -q
|
||||
|
||||
if test -f /sys/class/dmi/id/board_vendor && ! systemd-detect-virt --container ; then
|
||||
systemd-analyze chid
|
||||
systemd-analyze chid --json=pretty
|
||||
fi
|
||||
|
||||
systemd-analyze condition --instance=tmp --unit=systemd-growfs@.service
|
||||
systemd-analyze verify --instance=tmp --man=no systemd-growfs@.service
|
||||
systemd-analyze security --instance=tmp systemd-growfs@.service
|
||||
|
@ -69,6 +69,51 @@ SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
|
||||
link_endswith "$OUT_DIR/early/default.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
|
||||
grep -F "/dev/tty666" "$OUT_DIR/early/debug-shell.service.d/50-tty.conf"
|
||||
|
||||
# systemd.break (default)
|
||||
: "debug-shell: regular + systemd.break"
|
||||
CMDLINE="$CMDLINE systemd.break"
|
||||
SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
|
||||
test ! -h "$OUT_DIR/early/default.target.wants/breakpoint-pre-udev.service"
|
||||
test ! -h "$OUT_DIR/early/default.target.wants/breakpoint-pre-mount.service"
|
||||
test ! -h "$OUT_DIR/early/default.target.wants/breakpoint-pre-switch-root.service"
|
||||
|
||||
# systemd.break=pre-switch-root
|
||||
: "debug-shell: regular + systemd.break=pre-switch-root"
|
||||
CMDLINE="$CMDLINE systemd.break=pre-switch-root"
|
||||
SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
|
||||
test ! -h "$OUT_DIR/early/default.target.wants/breakpoint-pre-udev.service"
|
||||
test ! -h "$OUT_DIR/early/default.target.wants/breakpoint-pre-mount.service"
|
||||
test ! -h "$OUT_DIR/early/default.target.wants/breakpoint-pre-switch-root.service"
|
||||
|
||||
# systemd.break=pre-mount
|
||||
: "debug-shell: regular + systemd.break=pre-mount"
|
||||
CMDLINE="$CMDLINE systemd.break=pre-mount"
|
||||
SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
|
||||
test ! -h "$OUT_DIR/early/default.target.wants/breakpoint-pre-udev.service"
|
||||
test ! -h "$OUT_DIR/early/default.target.wants/breakpoint-pre-mount.service"
|
||||
test ! -h "$OUT_DIR/early/default.target.wants/breakpoint-pre-switch-root.service"
|
||||
|
||||
# systemd.break=pre-basic
|
||||
: "debug-shell: regular + systemd.break=pre-basic"
|
||||
CMDLINE="$CMDLINE systemd.break=pre-basic"
|
||||
SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
|
||||
link_endswith "$OUT_DIR/early/default.target.wants/breakpoint-pre-basic.service" /lib/systemd/system/breakpoint-pre-basic.service
|
||||
|
||||
# systemd.break=pre-udev
|
||||
: "debug-shell: regular + systemd.break=pre-udev"
|
||||
CMDLINE="$CMDLINE systemd.break=pre-udev"
|
||||
SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
|
||||
link_endswith "$OUT_DIR/early/default.target.wants/breakpoint-pre-udev.service" /lib/systemd/system/breakpoint-pre-udev.service
|
||||
|
||||
# systemd.break=pre-udev,pre-basic,pre-mount,pre-switch-root
|
||||
: "debug-shell: regular + systemd.break=pre-udev,pre-basic,pre-mount,pre-switch-root"
|
||||
rm -f "$OUT_DIR/early/default.target.wants/breakpoint-pre-udev.service"
|
||||
rm -f "$OUT_DIR/early/default.target.wants/breakpoint-pre-basic.service"
|
||||
CMDLINE="$CMDLINE systemd.break=pre-udev,pre-basic,pre-mount,pre-switch-root"
|
||||
SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
|
||||
link_endswith "$OUT_DIR/early/default.target.wants/breakpoint-pre-udev.service" /lib/systemd/system/breakpoint-pre-udev.service
|
||||
link_endswith "$OUT_DIR/early/default.target.wants/breakpoint-pre-basic.service" /lib/systemd/system/breakpoint-pre-basic.service
|
||||
|
||||
# Now override the default target via systemd.unit=
|
||||
: "debug-shell: regular + systemd.unit="
|
||||
CMDLINE="$CMDLINE systemd.unit=my-fancy.target"
|
||||
@ -103,6 +148,50 @@ CMDLINE="$CMDLINE rd.systemd.debug_shell"
|
||||
SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
|
||||
link_endswith "$OUT_DIR/early/initrd.target.wants/debug-shell.service" /lib/systemd/system/debug-shell.service
|
||||
|
||||
# rd.systemd.break (default)
|
||||
: "debug-shell: initrd + rd.systemd.break"
|
||||
CMDLINE="$CMDLINE rd.systemd.break"
|
||||
SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
|
||||
link_endswith "$OUT_DIR/early/initrd.target.wants/breakpoint-pre-switch-root.service" /lib/systemd/system/breakpoint-pre-switch-root.service
|
||||
|
||||
# rd.systemd.break=pre-udev
|
||||
: "debug-shell: initrd + rd.systemd.break=pre-udev"
|
||||
CMDLINE="$CMDLINE rd.systemd.break=pre-udev"
|
||||
SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
|
||||
link_endswith "$OUT_DIR/early/initrd.target.wants/breakpoint-pre-udev.service" /lib/systemd/system/breakpoint-pre-udev.service
|
||||
|
||||
# rd.systemd.break=pre-basic
|
||||
: "debug-shell: initrd + rd.systemd.break=pre-basic"
|
||||
CMDLINE="$CMDLINE rd.systemd.break=pre-basic"
|
||||
SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
|
||||
link_endswith "$OUT_DIR/early/initrd.target.wants/breakpoint-pre-basic.service" /lib/systemd/system/breakpoint-pre-basic.service
|
||||
|
||||
# rd.systemd.break=pre-mount
|
||||
: "debug-shell: initrd + rd.systemd.break=pre-mount"
|
||||
CMDLINE="$CMDLINE rd.systemd.break=pre-mount"
|
||||
SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
|
||||
link_endswith "$OUT_DIR/early/initrd.target.wants/breakpoint-pre-mount.service" /lib/systemd/system/breakpoint-pre-mount.service
|
||||
|
||||
# rd.systemd.break=pre-switch-root
|
||||
: "debug-shell: initrd + rd.systemd.break=pre-switch-root"
|
||||
rm -f "$OUT_DIR/early/initrd.target.wants/breakpoint-pre-switch-root.service"
|
||||
CMDLINE="$CMDLINE rd.systemd.break=pre-switch-root"
|
||||
SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
|
||||
link_endswith "$OUT_DIR/early/initrd.target.wants/breakpoint-pre-switch-root.service" /lib/systemd/system/breakpoint-pre-switch-root.service
|
||||
|
||||
# rd.systemd.break=pre-udev,pre-basic,pre-mount,pre-switch-root
|
||||
: "debug-shell: initrd + rd.systemd.break=pre-udev,pre-mount,pre-switch-root"
|
||||
rm -f "$OUT_DIR/early/initrd.target.wants/breakpoint-pre-udev.service"
|
||||
rm -f "$OUT_DIR/early/initrd.target.wants/breakpoint-pre-basic.service"
|
||||
rm -f "$OUT_DIR/early/initrd.target.wants/breakpoint-pre-mount.service"
|
||||
rm -f "$OUT_DIR/early/initrd.target.wants/breakpoint-pre-switch-root.service"
|
||||
CMDLINE="$CMDLINE rd.systemd.break=pre-udev,pre-basic,pre-mount,pre-switch-root"
|
||||
SYSTEMD_IN_INITRD=1 SYSTEMD_PROC_CMDLINE="$CMDLINE" run_and_list "$GENERATOR_BIN" "$OUT_DIR"
|
||||
link_endswith "$OUT_DIR/early/initrd.target.wants/breakpoint-pre-udev.service" /lib/systemd/system/breakpoint-pre-udev.service
|
||||
link_endswith "$OUT_DIR/early/initrd.target.wants/breakpoint-pre-basic.service" /lib/systemd/system/breakpoint-pre-basic.service
|
||||
link_endswith "$OUT_DIR/early/initrd.target.wants/breakpoint-pre-mount.service" /lib/systemd/system/breakpoint-pre-mount.service
|
||||
link_endswith "$OUT_DIR/early/initrd.target.wants/breakpoint-pre-switch-root.service" /lib/systemd/system/breakpoint-pre-switch-root.service
|
||||
|
||||
# Override the default target
|
||||
: "debug-shell: initrd + rd.systemd.unit"
|
||||
CMDLINE="$CMDLINE rd.systemd.unit=my-fancy-initrd.target"
|
||||
|
35
units/breakpoint-pre-basic.service.in
Normal file
35
units/breakpoint-pre-basic.service.in
Normal file
@ -0,0 +1,35 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=Breakpoint Before Basic System
|
||||
Documentation=man:systemd-debug-generator(8)
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target emergency.target
|
||||
After=sysinit.target sockets.target paths.target slices.target tmp.mount systemd-vconsole-setup.service
|
||||
Before=basic.target
|
||||
|
||||
[Service]
|
||||
Environment=SHELL_PROMPT_PREFIX="pre-basic "
|
||||
Type=oneshot
|
||||
ExecStartPre=-plymouth --wait quit
|
||||
# Execute shell with "-" prefix to not consider the unit failed if it exits with
|
||||
# a non-zero value
|
||||
ExecStart=-{{SUSHELL}}
|
||||
StandardInput=tty-force
|
||||
StandardOutput=inherit
|
||||
StandardError=inherit
|
||||
KillMode=process
|
||||
IgnoreSIGPIPE=no
|
||||
# bash ignores SIGTERM
|
||||
KillSignal=SIGHUP
|
||||
|
||||
# Unset locale for the console getty since the console has problems
|
||||
# displaying some internationalized messages.
|
||||
UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION
|
36
units/breakpoint-pre-mount.service.in
Normal file
36
units/breakpoint-pre-mount.service.in
Normal file
@ -0,0 +1,36 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=Breakpoint Before Mounting the Root Filesystem on /sysroot
|
||||
Documentation=man:systemd-debug-generator(8)
|
||||
AssertPathExists=/etc/initrd-release
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target emergency.target
|
||||
After=basic.target systemd-vconsole-setup.service
|
||||
Before=initrd-root-fs.target sysroot.mount systemd-fsck-root.service
|
||||
|
||||
[Service]
|
||||
Environment=SHELL_PROMPT_PREFIX="pre-mount "
|
||||
Type=oneshot
|
||||
ExecStartPre=-plymouth --wait quit
|
||||
# Execute shell with "-" prefix to not consider the unit failed if it exits with
|
||||
# a non-zero value
|
||||
ExecStart=-{{SUSHELL}}
|
||||
StandardInput=tty-force
|
||||
StandardOutput=inherit
|
||||
StandardError=inherit
|
||||
KillMode=process
|
||||
IgnoreSIGPIPE=no
|
||||
# bash ignores SIGTERM
|
||||
KillSignal=SIGHUP
|
||||
|
||||
# Unset locale for the console getty since the console has problems
|
||||
# displaying some internationalized messages.
|
||||
UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION
|
37
units/breakpoint-pre-switch-root.service.in
Normal file
37
units/breakpoint-pre-switch-root.service.in
Normal file
@ -0,0 +1,37 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=Breakpoint Before Switching Root
|
||||
Documentation=man:systemd-debug-generator(8)
|
||||
AssertPathExists=/etc/initrd-release
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target emergency.target
|
||||
Wants=remote-fs.target
|
||||
After=initrd.target initrd-parse-etc.service sysroot.mount remote-fs.target systemd-vconsole-setup.service
|
||||
Before=initrd-cleanup.service
|
||||
|
||||
[Service]
|
||||
Environment=SHELL_PROMPT_PREFIX="pre-switch-root "
|
||||
Type=oneshot
|
||||
ExecStartPre=-plymouth --wait quit
|
||||
# Execute shell with "-" prefix to not consider the unit failed if it exits with
|
||||
# a non-zero value
|
||||
ExecStart=-{{SUSHELL}}
|
||||
StandardInput=tty-force
|
||||
StandardOutput=inherit
|
||||
StandardError=inherit
|
||||
KillMode=process
|
||||
IgnoreSIGPIPE=no
|
||||
# bash ignores SIGTERM
|
||||
KillSignal=SIGHUP
|
||||
|
||||
# Unset locale for the console getty since the console has problems
|
||||
# displaying some internationalized messages.
|
||||
UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION
|
36
units/breakpoint-pre-udev.service.in
Normal file
36
units/breakpoint-pre-udev.service.in
Normal file
@ -0,0 +1,36 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
[Unit]
|
||||
Description=Breakpoint Before Starting to Process Kernel uevents
|
||||
Documentation=man:systemd-debug-generator(8)
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target emergency.target
|
||||
Wants=systemd-journald.socket
|
||||
After=systemd-journald.socket systemd-vconsole-setup.service
|
||||
Before=systemd-udevd.service systemd-udev-trigger.service
|
||||
|
||||
[Service]
|
||||
Environment=SHELL_PROMPT_PREFIX="pre-udev "
|
||||
Type=oneshot
|
||||
ExecStartPre=-plymouth --wait quit
|
||||
# Execute shell with "-" prefix to not consider the unit failed if it exits with
|
||||
# a non-zero value
|
||||
ExecStart=-{{SUSHELL}}
|
||||
StandardInput=tty-force
|
||||
StandardOutput=inherit
|
||||
StandardError=inherit
|
||||
KillMode=process
|
||||
IgnoreSIGPIPE=no
|
||||
# bash ignores SIGTERM
|
||||
KillSignal=SIGHUP
|
||||
|
||||
# Unset locale for the console getty since the console has problems
|
||||
# displaying some internationalized messages.
|
||||
UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION
|
@ -7,6 +7,10 @@ units = [
|
||||
{ 'file' : 'blockdev@.target' },
|
||||
{ 'file' : 'bluetooth.target' },
|
||||
{ 'file' : 'boot-complete.target' },
|
||||
{ 'file' : 'breakpoint-pre-basic.service.in' },
|
||||
{ 'file' : 'breakpoint-pre-mount.service.in' },
|
||||
{ 'file' : 'breakpoint-pre-switch-root.service.in' },
|
||||
{ 'file' : 'breakpoint-pre-udev.service.in' },
|
||||
{ 'file' : 'capsule@.service.in' },
|
||||
{ 'file' : 'capsule.slice' },
|
||||
{ 'file' : 'console-getty.service.in' },
|
||||
|
Loading…
Reference in New Issue
Block a user