1
0
mirror of https://github.com/systemd/systemd synced 2026-04-11 17:44:58 +02:00

Compare commits

...

39 Commits

Author SHA1 Message Date
Yu Watanabe
3e7bf8535f tree-wide: fix typo 2021-12-25 18:32:22 +09:00
Yu Watanabe
61d99ab9fb
Merge pull request #21774 from keszybz/make-libcore-shared-and-add-lib-tag-option
Make libcore shared and add private shared lib tag option
2021-12-25 18:02:54 +09:00
Yu Watanabe
1de6d117ef
Merge pull request #21765 from yuwata/udev-warn-truncation
udev: warn string truncation
2021-12-25 18:02:32 +09:00
Yu Watanabe
8585b7ca65
Merge pull request #20833 from pdmorrow/onfailure_env
service: pass exiting service state to triggered On{Failure,Success}= dependencies
2021-12-25 15:29:42 +09:00
Zbigniew Jędrzejewski-Szmek
ff254eea8f test: ignore the error about our own libraries missing during image creation
19:50:59 F: Missing a shared library required by /var/tmp/systemd-test.NIPT2q/root/lib/systemd/libsystemd-core-250.so.
19:50:59 F: Run "ldd /var/tmp/systemd-test.NIPT2q/root/lib/systemd/libsystemd-core-250.so" to find out what it is.
19:50:59 F: libsystemd-shared-250.so => not found
19:50:59 F: Cannot create a test image.
2021-12-25 15:18:50 +09:00
Zbigniew Jędrzejewski-Szmek
a2b0cd3f5a meson: allow specifying a custom "tag" for the private shared libaries
We have /usr/lib/systemd/libsystemd-{shared,core}-nnn.so. With this
path the 'nnn' part can be changed to something different. The idea
is that during a package build this will be set to the package version.

This way during in-place upgrades with the same major version both
the new and old libraries can cooexit. This should fix the issue
when systemd programs are called during package upgrades and fail
to exec because the expect different symbols in the library they
are linked to.

This should fix https://bugzilla.redhat.com/show_bug.cgi?id=1906010.
2021-12-25 15:18:50 +09:00
Zbigniew Jędrzejewski-Szmek
4287c85589 meson: create new libsystemd-core.so private shared library
The scheme is very similar to libsystemd-shared.so: instead of building a
static library, we build a shared library from the same objects and link the
two users to it. Both systemd and systemd-analyze consist mostly of the fairly
big code in libcore, so we save a bit on the installation:

(-0g, no strip)
-rwxr-xr-x 5238864 Dec 14 12:52 /var/tmp/inst1/usr/lib/systemd/systemd
-rwxr-xr-x 5399600 Dec 14 12:52 /var/tmp/inst1/usr/bin/systemd-analyze
-rwxr-xr-x  244912 Dec 14 13:17 /var/tmp/inst2/usr/lib/systemd/systemd
-rwxr-xr-x  461224 Dec 14 13:17 /var/tmp/inst2/usr/bin/systemd-analyze
-rwxr-xr-x 5271568 Dec 14 13:17 /var/tmp/inst2/usr/lib/systemd/libsystemd-core-250.so

(-0g, strip)
-rwxr-xr-x 2522080 Dec 14 13:19 /var/tmp/inst1/usr/lib/systemd/systemd
-rwxr-xr-x 2604160 Dec 14 13:19 /var/tmp/inst1/usr/bin/systemd-analyze
-rwxr-xr-x  113304 Dec 14 13:19 /var/tmp/inst2/usr/lib/systemd/systemd
-rwxr-xr-x  207656 Dec 14 13:19 /var/tmp/inst2/usr/bin/systemd-analyze
-rwxr-xr-x 2648520 Dec 14 13:19 /var/tmp/inst2/usr/lib/systemd/libsystemd-core-250.so

So for systemd itself we grow a bit (2522080 → 2648520+113304=2761824), but
overall we save. The most is saved on all the test files that link to libcore,
if they are installed, because there's 15 of them:

$ du -s /var/tmp/inst?
220096	/var/tmp/inst1
122960	/var/tmp/inst2

I also considered making systemd-analyze a symlink to /usr/lib/systemd/systemd
and turning systemd into a multicall binary. We did something like this with
udevd and udevadm. But that solution doesn't fit well in this case.
systemd-analyze has a bunch of functionality that is not used in systemd,
so the systemd binary would need to grow quite a bit. And we're likely to
add new types of verification or introspection features in analyze, and this
baggage would only grow. In addition, there are the test binaries which also
benefit from this.
2021-12-25 15:18:47 +09:00
Yu Watanabe
b0c01c9846 test: add test for truncation of program result invoked by udev 2021-12-25 15:13:19 +09:00
Yu Watanabe
567c19a62c udev: refuse to process line when invalid program output is obtained 2021-12-25 15:13:19 +09:00
Yu Watanabe
6b6e471a32 udev: do not import property value from truncated line of program result 2021-12-25 15:13:19 +09:00
Yu Watanabe
7056adbf16 udev: warn about truncation of program result
Closes #21078.
2021-12-25 15:13:19 +09:00
Yu Watanabe
f6caab8995 udev: warn when result of string substitution is truncated 2021-12-25 15:13:17 +09:00
Yu Watanabe
648a799fc9 test: add tests for strnpcpy_full() and friends 2021-12-25 15:12:46 +09:00
Yu Watanabe
e70151c94d util: introduce strnpcpy_full() and friends to provide whether result is truncated or not 2021-12-25 15:12:45 +09:00
Yu Watanabe
2588920059
Merge pull request #21868 from lucab/ups/factory-locale-conf
factory: populate /etc/locale.conf with systemd build-time setting
2021-12-25 15:09:35 +09:00
Yu Watanabe
37057fe93e
Merge pull request #21762 from yuwata/udev-ctrl-do-not-kill
udev: do not kill "udevadm control" processes in the same cgroup
2021-12-25 15:08:13 +09:00
Stephen Hemminger
7c4bd9ac98
bus-dump: change capture output to use pcapng (#21738)
This patch changes busctl capture to generate pcapng format
instead of the legacy pcap format files. It includes basic
meta-data in the file and still uses microsecond time
resolution. In future, more things can be added such as
high resolution timestams, statistics, etc.

PCAP Next Generation capture file format is what tshark uses
and is in process of being standardized in IETF. It is also
readable with libpcap.

$ capinfos /tmp/new.pcapng
File name:           /tmp/new.pcapng
File type:           Wireshark/... - pcapng
File encapsulation:  D-Bus
File timestamp precision:  microseconds (6)
Packet size limit:   file hdr: (not set)
Packet size limit:   inferred: 4096 bytes
Number of packets:   22
File size:           21kB
Data size:           20kB
Capture duration:    0.005694 seconds
First packet time:   2021-12-11 11:57:42.788374
Last packet time:    2021-12-11 11:57:42.794068
Data byte rate:      3,671kBps
Data bit rate:       29Mbps
Average packet size: 950.27 bytes
Average packet rate: 3,863 packets/s
SHA256:              b85ed8b094af60c64aa6d9db4a91404e841736d36b9e662d707db9e4096148f1
RIPEMD160:           81f9bac7ec0ec5cd1d55ede136a5c90413894e3a
SHA1:                8400822ef724b934d6000f5b7604b9e6e91be011
Strict time order:   True
Capture oper-sys:    Linux 5.14.0-0.bpo.2-amd64
Capture application: systemd 250 (250-rc2-33-gdc79ae2+)
Number of interfaces in file: 1
Interface #0 info:
                     Encapsulation = D-Bus (146 - dbus)
                     Capture length = 4096
                     Time precision = microseconds (6)
                     Time ticks per second = 1000000
                     Number of stat entries = 0
                     Number of packets = 22
2021-12-25 15:07:40 +09:00
Yu Watanabe
402b81ffd8
Merge pull request #21737 from yuwata/network-wait-dsa-master-up
network: wait for DSA master up
2021-12-25 15:05:36 +09:00
Yu Watanabe
7726526cc6
Merge pull request #21648 from yuwata/network-tunnel-local-automatic-address-selection
network: tunnel: automatic local address selection
2021-12-25 15:05:13 +09:00
Yu Watanabe
d3d6b38f46
Merge pull request #21871 from keszybz/meson-sbat-report
Report sbat settings in meson summary
2021-12-25 15:03:50 +09:00
Yu Watanabe
61cdbd8d76
Merge pull request #21888 from mrc0mmand/ci-more-build-coverage
ci: test build with supported cryptolibs to some degree
2021-12-25 15:03:08 +09:00
Frantisek Sumsal
298cff6171 ci: test build with supported cryptolibs to some degree
Let's assign a specific -Dcryptolib= value to each job to have at least
some coverage for all supported cryptolibs without unnecessarily
multiplying the test matrix.

Should provide coverage for #21880.
2021-12-24 16:24:16 +01:00
Frantisek Sumsal
7b55f29797 ci: drop build test with -O2, since it's covered by -O3 tests 2021-12-24 16:24:16 +01:00
Zbigniew Jędrzejewski-Szmek
e4e44a0107 meson: report SBAT settings 2021-12-23 20:06:17 +01:00
Luca BRUNO
8e85924fd6
factory/locale.conf: mention systemd ownership
This explicitly mentions that comments and empty lines are supported
(and ignored) in /etc/locale.conf. It then adds ownership reference
to the factory default.
2021-12-23 14:18:02 +00:00
Zbigniew Jędrzejewski-Szmek
3f871f1205 meson: move efi summary() section to src/boot/efi
This way we can add the entries more naturally in the same place where
they are defined.
2021-12-23 13:52:33 +01:00
Zbigniew Jędrzejewski-Szmek
65dcf9f9a0 meson: move efi file lists closer to where they are used
The goal is to have the detection of features and paths done first, and
then the build target constructions second.
2021-12-23 13:52:33 +01:00
Luca BRUNO
623370e643
factory: populate /etc/locale.conf with systemd build-time setting
This adds /etc/locale.conf to the set of configuration files
populated by tmpfiles.d factory /etc handling.
In particular, the build-time locale configuration in systemd is
now wired to a /usr factory file, and installed to the system.
On boot, if other locale customization tools did not write
/etc/locale.conf on the system, the factory default file gets
copied to /etc by systemd-tmpfiles.
This is done in order to avoid skews between different system
components when no locale settings are configured. At that point,
systemd can safely act as the fallback owner of /etc/locale.conf.
2021-12-23 11:01:12 +00:00
Yu Watanabe
ccadf9ac0d udev: do not kill "udevadm control" process in the same cgroup
Fixes #16867.
2021-12-18 20:05:26 +09:00
Yu Watanabe
f9da11ef1e udev-ctrl: make udev_ctrl_send() accept integer and string through a single argument 2021-12-18 19:41:14 +09:00
Yu Watanabe
b1d9c504d3 network: wait until the DSA master interface becomes up
This is for the DSA subsystem, which have several stacked interfaces
on the master interface. To bring up a stacked interface, it is necessary
that the master is already up. See
https://github.com/systemd/systemd/issues/7478#issuecomment-348508263.

Note this is not necessary for newer kernels which includes
9d5ef190e5.

Fixes #7478.
2021-12-17 02:00:25 +09:00
Peter Morrow
03e1b6664c man: document $MONITOR_METADATA usage
Decsribe when $MONITOR_METADATA will be set and how it's contents are
defined.
2021-12-13 14:44:06 +00:00
Peter Morrow
6fc2da644a tests: add test to cover service exit status propagation
Various tests to cover MONITOR_METADATA setup for OnFailure= and
OnSuccess= dependencies.
2021-12-13 14:44:06 +00:00
Peter Morrow
cdebedb4d4 service: pass service exit status to spawned On{Failure,Success}= dependency
When a service exits and triggers either an OnFailure= or OnSuccess=
dependency we now set a new environment variable for the ExecStart= and
ExecStartPre= process. This variable $MONITOR_METADATA exposes the
metadata relating to the service which triggered the dependency.
MONITOR_METADATA takes the following form:

MONITOR_METADATA="SERVICE_RESULT=<result-string0>,EXIT_CODE=<exit-code0>,EXIT_STATUS=<exit-status0>,INVOCATION_ID=<id>,UNIT=<triggering-unit0.service>;SERVICE_RESULT=<result-stringN>,EXIT_CODE=<exit-codeN>,=EXIT_STATUS=<exit-statusN>,INVOCATION_ID=<id>,UNIT=<triggering-unitN.service>"

MONITOR_METADATA is space separated set of metadata relating to the
service(s) which triggered the dependency. This is a list since if we
have 2 services which trigger the same dependency then the dependency
start job may be merged. In this case we need to pass both service
metadata to the triggered service. If there is no job merging then
MONITOR_METADATA will be a single entry.

For example, in the case we had a service "failer.service" which
triggers "failer-handler.service", the following variable is exported to
the ExecStart= and ExecStartPre= processes in failer-handler.service:

MONITOR_METADATA="SERVICE_RESULT=exit-code,EXIT_CODE=exited,EXIT_STATUS=1,INVOCATION_ID=67c657ed7b34466ea369abdf994c6393,UNIT=failer.service"

In another example where we have failer.service and failer2.service
which both also trigger failer-handler.service then the start job for
failer-handler.service may be merged and we might get the following:

MONITOR_METADATA="SERVICE_RESULT=exit-code,EXIT_CODE=exited,EXIT_STATUS=1,INVOCATION_ID=16a93ad196c94109990fb8b9aa5eef5f,UNIT=failer.service;SERVICE_RESULT=exit-code,EXIT_CODE=exited,EXIT_STATUS=1,INVOCATION_ID=ff70131e4cc145e994fb621de25a3e8f,UNIT=failer2.service"
2021-12-13 11:25:57 +00:00
Yu Watanabe
da7d684825 test-network: add testcase for automatic tunnel local address selection 2021-12-12 14:11:38 +09:00
Yu Watanabe
2be25d7557 network: tunnel: support to set an address assigned on underlying interface as local address
Closes #18732.
2021-12-12 13:42:45 +09:00
Yu Watanabe
6d1b59cec4 network/netdev: introduce link_get_local_address()
It will be used in later commits.
2021-12-12 13:41:48 +09:00
Yu Watanabe
562729d744 network/netdev: introduce .is_ready_to_create() entry in netdev vtable 2021-12-12 13:41:39 +09:00
Yu Watanabe
54cd4bb7c7 network: make activation error critical 2021-12-12 03:05:15 +09:00
62 changed files with 1915 additions and 384 deletions

View File

@ -9,9 +9,8 @@ success() { echo >&2 -e "\033[32;1m$1\033[0m"; }
ARGS=(
"--optimization=0"
"--optimization=2"
"--optimization=s"
"--optimization=3 -Db_lto=true"
"--optimization=3 -Db_lto=true -Ddns-over-tls=false"
"--optimization=3 -Db_lto=false"
"--optimization=3 -Ddns-over-tls=openssl"
"--optimization=3 -Dfexecve=true -Dstandalone-binaries=true -Dstatic-libsystemd=true -Dstatic-libudev=true"
@ -63,6 +62,7 @@ PACKAGES=(
COMPILER="${COMPILER:?}"
COMPILER_VERSION="${COMPILER_VERSION:?}"
LINKER="${LINKER:?}"
CRYPTOLIB="${CRYPTOLIB:?}"
RELEASE="$(lsb_release -cs)"
bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ $RELEASE main restricted universe multiverse' >>/etc/apt/sources.list"
@ -122,7 +122,7 @@ for args in "${ARGS[@]}"; do
CC="$CC" CC_LD="$LINKER" CFLAGS="-Werror" \
CXX="$CXX" CXX_LD="$LINKER" CXXFLAGS="-Werror" \
meson -Dtests=unsafe -Dslow-tests=true -Dfuzz-tests=true --werror \
$args build; then
-Dcryptolib="${CRYPTOLIB:?}" $args build; then
fatal "meson failed with $args"
fi

View File

@ -19,20 +19,20 @@ jobs:
build:
runs-on: ubuntu-20.04
concurrency:
group: ${{ github.workflow }}-${{ matrix.env.COMPILER }}-${{ matrix.env.COMPILER_VERSION }}-${{ matrix.env.LINKER }}-${{ github.ref }}
group: ${{ github.workflow }}-${{ toJSON(matrix.env) }}-${{ github.ref }}
cancel-in-progress: true
strategy:
fail-fast: false
matrix:
env:
- { COMPILER: "gcc", COMPILER_VERSION: "10", LINKER: "bfd" }
- { COMPILER: "gcc", COMPILER_VERSION: "11", LINKER: "gold" }
- { COMPILER: "clang", COMPILER_VERSION: "11", LINKER: "bfd" }
- { COMPILER: "clang", COMPILER_VERSION: "12", LINKER: "gold" }
- { COMPILER: "clang", COMPILER_VERSION: "13", LINKER: "lld" }
- { COMPILER: "gcc", COMPILER_VERSION: "10", LINKER: "bfd", CRYPTOLIB: "gcrypt" }
- { COMPILER: "gcc", COMPILER_VERSION: "11", LINKER: "gold", CRYPTOLIB: "openssl" }
- { COMPILER: "clang", COMPILER_VERSION: "11", LINKER: "bfd", CRYPTOLIB: "auto" }
- { COMPILER: "clang", COMPILER_VERSION: "12", LINKER: "gold", CRYPTOLIB: "gcrypt" }
- { COMPILER: "clang", COMPILER_VERSION: "13", LINKER: "lld", CRYPTOLIB: "openssl" }
env: ${{ matrix.env }}
steps:
- name: Repository checkout
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579
- name: Build check (${{ env.COMPILER }}-${{ env.COMPILER_VERSION }}-${{ env.LINKER }})
- name: ${{ format('Build check ({0}-{1}-{2}-{3})', env.COMPILER, env.COMPILER_VERSION, env.LINKER, env.CRYPTOLIB) }}
run: sudo -E .github/workflows/build_test.sh

View File

@ -0,0 +1,3 @@
# This is the fallback locale configuration provided by systemd.
LANG="{{ SYSTEMD_DEFAULT_LOCALE }}"

View File

@ -0,0 +1,11 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
factory_etc_dir = factorydir / 'etc'
custom_target(
'locale.conf',
input : 'locale.conf.in',
output : 'locale.conf',
command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'],
install : true,
install_dir : factory_etc_dir)

View File

@ -78,10 +78,10 @@
<term><command>capture</command> <arg choice="opt" rep="repeat"><replaceable>SERVICE</replaceable></arg></term>
<listitem><para>Similar to <command>monitor</command> but
writes the output in pcap format (for details, see the <ulink
url="https://wiki.wireshark.org/Development/LibpcapFileFormat">Libpcap
File Format</ulink> description). Make sure to redirect
standard output to a file. Tools like
writes the output in pcapng format (for details, see
<ulink url="https://github.com/pcapng/pcapng/">
PCAP Next Generation (pcapng) Capture File Format</ulink>).
Make sure to redirect standard output to a file or pipe. Tools like
<citerefentry project='die-net'><refentrytitle>wireshark</refentrytitle><manvolnum>1</manvolnum></citerefentry>
may be used to dissect and view the resulting
files.</para></listitem>

View File

@ -32,7 +32,8 @@
<para>The basic file format of <filename>locale.conf</filename> is
a newline-separated list of environment-like shell-compatible
variable assignments. It is possible to source the configuration
variable assignments, ignoring comments and empty lines.
It is possible to source the configuration
from shell scripts, however, beyond mere variable assignments, no
shell features are supported, allowing applications to read the
file without implementing a shell compatible execution
@ -64,14 +65,14 @@
might be checked for locale configuration as well, however only as
fallback.</para>
<para><filename>/etc/locale.conf</filename> is usually created and updated
<para><filename>/etc/locale.conf</filename> can be updated
using
<citerefentry><refentrytitle>systemd-localed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
<citerefentry project='man-pages'><refentrytitle>localectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
may be used to alter the settings in this file during runtime from
the command line. Use
<citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry>
to initialize them on mounted (but not booted) system images.</para>
to customize them on mounted (but not booted) system images.</para>
</refsect1>
<refsect1>
@ -107,7 +108,9 @@
<para><filename>/etc/locale.conf</filename>:</para>
<programlisting>LANG=de_DE.UTF-8
<programlisting># Custom settings
LANG=de_DE.UTF-8
LC_MESSAGES=en_US.UTF-8</programlisting>
</example>

View File

@ -3546,6 +3546,41 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
</table></listitem>
</varlistentry>
<varlistentry>
<term><varname>$MONITOR_METADATA</varname></term>
<listitem><para>Only defined for the service unit type, this environment variable is passed to all
<varname>ExecStart=</varname> and <varname>ExecStartPre=</varname> processes which run in services
triggered by <varname>OnFailure=</varname> or <varname>OnSuccess=</varname> dependencies.</para>
<para>
The contents of this variable consists of a semi-colon separated list of metadata fields associated with the triggering
service. For each service which triggered the <varname>OnFailure=</varname> or <varname>OnSuccess=</varname>
dependency the following fields will be set:
</para>
<itemizedlist>
<listitem><para><constant>SERVICE_RESULT</constant></para></listitem>
<listitem><para><constant>EXIT_CODE</constant></para></listitem>
<listitem><para><constant>EXIT_STATUS</constant></para></listitem>
<listitem><para><constant>INVOCATION_ID</constant></para></listitem>
<listitem><para><constant>UNIT</constant></para></listitem>
</itemizedlist>
<para>The fields <constant>SERVICE_RESULT</constant>, <constant>EXIT_CODE</constant> and
<constant>EXIT_STATUS</constant> may take the same values that are allowed when set for
<varname>ExecStop=</varname> and <varname>ExecStopPost=</varname> processes. The fields
<constant>INVOCATION_ID</constant> and <constant>UNIT</constant> are the invocaton id and unit
name of the service which triggered the dependency. Each field is comma separated, i.e.</para>
<programlisting>
SERVICE_RESULT=result-string,EXIT_CODE=exit-code,EXIT_STATUS=exit-status,INVOCATION_ID=invocation-id,UNIT=triggering.service
</programlisting>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>$PIDFILE</varname></term>
@ -3983,6 +4018,77 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
</table>
</refsect1>
<refsect1>
<title>Examples</title>
<example>
<title><varname>$MONITOR_METADATA</varname> usage</title>
<para>A service <filename index="false">myfailer.service</filename> which can trigger an
<varname>OnFailure=</varname> dependency.</para>
<programlisting>
[Unit]
Description=Service which can trigger an OnFailure= dependency
OnFailure=myhandler.service
[Service]
ExecStart=/bin/myprogram
</programlisting>
<para>A service <filename index="false">mysuccess.service</filename> which can trigger an
<varname>OnSuccess=</varname> dependency.</para>
<programlisting>
[Unit]
Description=Service which can trigger an OnSuccess= dependency
OnSuccess=myhandler.service
[Service]
ExecStart=/bin/mysecondprogram
</programlisting>
<para>A service <filename index="false">myhandler.service</filename> which can be triggered
by any of the above services.</para>
<programlisting>
[Unit]
Description=Acts on service failing or succeeding
[Service]
ExecStart=/bin/bash -c "echo $MONITOR_METADATA"
</programlisting>
<para>If <filename index="false">myfailer.service</filename> were to run and exit in failure,
then <filename index="false">myhandler.service</filename> would be triggered and the
<varname>$MONITOR_METADATA</varname> variable would be set as follows:</para>
<programlisting>
MONITOR_METADATA=SERVICE_RESULT=result-string,EXIT_CODE=exit-code,EXIT_STATUS=exit-status,INVOCATION_ID=invocation-id,UNIT=myfailer.service
</programlisting>
<para>If <filename index="false">mysuccess.service</filename> were to run and exit in success,
then <filename index="false">myhandler.service</filename> would be triggered and the
<varname>$MONITOR_METADATA</varname> variable would be set as follows:</para>
<programlisting>
MONITOR_METADATA=SERVICE_RESULT=result-string,EXIT_CODE=exit-code,EXIT_STATUS=exit-status,INVOCATION_ID=invocation-id,UNIT=mysuccess.service
</programlisting>
<para>If <filename index="false">myfailer.service</filename> and <filename index="false">mysuccess.service</filename> were to run and exit,
there is a chance that the triggered dependency start job might be merged. Thus only a single invocation of
<filename index="false">myhandler.service</filename> would be triggered. In this case the <varname>$MONITOR_METADATA</varname> variable
would be a list containing exit metadata for both of <filename index="false">myfailer.service</filename>
and <filename index="false">mysuccess.service</filename>.</para>
<programlisting>
MONITOR_METADATA=SERVICE_RESULT=result-string,EXIT_CODE=exit-code,EXIT_STATUS=exit-status,INVOCATION_ID=invocation-id,UNIT=myfailer.service;SERVICE_RESULT=result-string,EXIT_CODE=exit-code,EXIT_STATUS=exit-status,INVOCATION_ID=invocation-id,UNIT=mysuccess.service
</programlisting>
</example>
</refsect1>
<refsect1>
<title>See Also</title>
<para>

View File

@ -1148,8 +1148,13 @@
<varlistentry>
<term><varname>Local=</varname></term>
<listitem>
<para>A static local address for tunneled packets. It must be an address on another interface of
this host, or the special value <literal>any</literal>.</para>
<para>A static local address for tunneled packets. It must be an address on another interface
of this host, or one of the special values <literal>any</literal>,
<literal>ipv4_link_local</literal>, <literal>ipv6_link_local</literal>,
<literal>dhcp4</literal>, <literal>dhcp6</literal>, and <literal>slaac</literal>. If one
of the special values except for <literal>any</literal> is specified, an address which
matches the corresponding type on the underlying interface will be used. Defaults to
<literal>any</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>

View File

@ -43,6 +43,10 @@ endif
skip_deps = want_ossfuzz or want_libfuzzer
fuzzer_build = want_ossfuzz or want_libfuzzer
# Create a title-less summary section early, so it ends up first in the output.
# More items are added later after they have been detected.
summary({'build mode' : get_option('mode')})
#####################################################################
# Try to install the git pre-commit hook
@ -595,6 +599,11 @@ endif
versiondep = declare_dependency(sources: version_h)
shared_lib_tag = get_option('shared-lib-tag')
if shared_lib_tag == ''
shared_lib_tag = meson.project_version()
endif
sh = find_program('sh')
echo = find_program('echo')
sed = find_program('sed')
@ -2017,12 +2026,7 @@ dbus_programs += executable(
link_with : [libcore,
libshared],
dependencies : [versiondep,
threads,
librt,
libseccomp,
libselinux,
libmount,
libblkid],
libseccomp],
install_rpath : rootlibexecdir,
install : true,
install_dir : rootlibexecdir)
@ -2038,12 +2042,7 @@ public_programs += executable(
link_with : [libcore,
libshared],
dependencies : [versiondep,
threads,
librt,
libseccomp,
libselinux,
libmount,
libblkid],
libseccomp],
install_rpath : rootlibexecdir,
install : conf.get('ENABLE_ANALYZE'))
@ -3263,6 +3262,7 @@ public_programs += executable(
busctl_sources,
include_directories : includes,
link_with : [libshared],
dependencies : [versiondep],
install_rpath : rootlibexecdir,
install : true)
@ -3706,6 +3706,7 @@ subdir('docs/var-log')
install_subdir('factory/etc',
install_dir : factorydir)
subdir('factory/templates')
if install_sysconfdir
install_data('xorg/50-systemd-user.sh',
@ -3886,7 +3887,6 @@ alt_time_epoch = run_command('date', '-Is', '-u', '-d', '@@0@'.format(time_epoch
check : true).stdout().strip()
summary({
'build mode' : get_option('mode'),
'split /usr' : split_usr,
'split bin-sbin' : split_bin,
'prefix directory' : prefixdir,
@ -3907,6 +3907,7 @@ summary({
'D-Bus system directory' : dbussystemservicedir,
'bash completions directory' : bashcompletiondir,
'zsh completions directory' : zshcompletiondir,
'private shared lib version tag' : shared_lib_tag,
'extra start script' : get_option('rc-local'),
'debug shell' : '@0@ @ @1@'.format(get_option('debug-shell'),
get_option('debug-tty')),
@ -3944,17 +3945,6 @@ summary({
# CPPFLAGS: ${OUR_CPPFLAGS} ${CPPFLAGS}
# LDFLAGS: ${OUR_LDFLAGS} ${LDFLAGS}
if conf.get('ENABLE_EFI') == 1 and conf.get('HAVE_GNU_EFI') == 1
summary({
'EFI machine type' : efi_arch[0],
'EFI CC' : '@0@'.format(' '.join(efi_cc)),
'EFI LD' : efi_ld,
'EFI lds' : efi_lds,
'EFI crt0' : efi_crt0,
'EFI include directory' : efi_incdir},
section : 'Extensible Firmware Interface')
endif
found = []
missing = []

View File

@ -3,6 +3,8 @@
option('version-tag', type : 'string',
description : 'override the git version string')
option('shared-lib-tag', type : 'string',
description : 'override the private shared library version tag (defaults to project version)')
option('mode', type : 'combo', choices : ['developer', 'release'],
description : 'autoenable features suitable for systemd development/release builds')

View File

@ -25,7 +25,7 @@
"list:List bus names"
"status:Show bus service, process or bus owner credentials"
"monitor:Show bus traffic"
"capture:Capture bus traffix as pcap"
"capture:Capture bus traffic"
"tree:Show object tree of service"
"introspect:Introspect object"
"call:Call a method"

115
src/basic/pcapng.h Normal file
View File

@ -0,0 +1,115 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
/*
* For details about the file format see RFC:
* https://www.ietf.org/id/draft-tuexen-opsawg-pcapng-03.html
* and
* https://github.com/pcapng/pcapng/
*/
enum pcapng_block_types {
PCAPNG_INTERFACE_BLOCK = 1,
PCAPNG_PACKET_BLOCK, /* Obsolete */
PCAPNG_SIMPLE_PACKET_BLOCK,
PCAPNG_NAME_RESOLUTION_BLOCK,
PCAPNG_INTERFACE_STATS_BLOCK,
PCAPNG_ENHANCED_PACKET_BLOCK,
PCAPNG_SECTION_BLOCK = 0x0A0D0D0A,
};
struct pcapng_option {
uint16_t code;
uint16_t length;
uint8_t data[];
};
#define PCAPNG_BYTE_ORDER_MAGIC 0x1A2B3C4D
#define PCAPNG_MAJOR_VERS 1
#define PCAPNG_MINOR_VERS 0
enum pcapng_opt {
PCAPNG_OPT_END = 0,
PCAPNG_OPT_COMMENT = 1,
};
struct pcapng_section {
uint32_t block_type;
uint32_t block_length;
uint32_t byte_order_magic;
uint16_t major_version;
uint16_t minor_version;
uint64_t section_length;
};
enum pcapng_section_opt {
PCAPNG_SHB_HARDWARE = 2,
PCAPNG_SHB_OS = 3,
PCAPNG_SHB_USERAPPL = 4,
};
struct pcapng_interface_block {
uint32_t block_type; /* 1 */
uint32_t block_length;
uint16_t link_type;
uint16_t reserved;
uint32_t snap_len;
};
enum pcapng_interface_options {
PCAPNG_IFB_NAME = 2,
PCAPNG_IFB_DESCRIPTION,
PCAPNG_IFB_IPV4ADDR,
PCAPNG_IFB_IPV6ADDR,
PCAPNG_IFB_MACADDR,
PCAPNG_IFB_EUIADDR,
PCAPNG_IFB_SPEED,
PCAPNG_IFB_TSRESOL,
PCAPNG_IFB_TZONE,
PCAPNG_IFB_FILTER,
PCAPNG_IFB_OS,
PCAPNG_IFB_FCSLEN,
PCAPNG_IFB_TSOFFSET,
PCAPNG_IFB_HARDWARE,
};
struct pcapng_enhance_packet_block {
uint32_t block_type; /* 6 */
uint32_t block_length;
uint32_t interface_id;
uint32_t timestamp_hi;
uint32_t timestamp_lo;
uint32_t capture_length;
uint32_t original_length;
};
/* Flags values */
#define PCAPNG_IFB_INBOUND 0b01
#define PCAPNG_IFB_OUTBOUND 0b10
enum pcapng_epb_options {
PCAPNG_EPB_FLAGS = 2,
PCAPNG_EPB_HASH,
PCAPNG_EPB_DROPCOUNT,
PCAPNG_EPB_PACKETID,
PCAPNG_EPB_QUEUE,
PCAPNG_EPB_VERDICT,
};
struct pcapng_statistics_block {
uint32_t block_type; /* 5 */
uint32_t block_length;
uint32_t interface_id;
uint32_t timestamp_hi;
uint32_t timestamp_lo;
};
enum pcapng_isb_options {
PCAPNG_ISB_STARTTIME = 2,
PCAPNG_ISB_ENDTIME,
PCAPNG_ISB_IFRECV,
PCAPNG_ISB_IFDROP,
PCAPNG_ISB_FILTERACCEPT,
PCAPNG_ISB_OSDROP,
PCAPNG_ISB_USRDELIV,
};

View File

@ -15,57 +15,73 @@
#include <stdio.h>
#include <string.h>
#include "string-util.h"
#include "strxcpyx.h"
size_t strnpcpy(char **dest, size_t size, const char *src, size_t len) {
size_t strnpcpy_full(char **dest, size_t size, const char *src, size_t len, bool *ret_truncated) {
bool truncated = false;
assert(dest);
assert(src);
if (size == 0)
if (size == 0) {
if (ret_truncated)
*ret_truncated = len > 0;
return 0;
}
if (len >= size) {
if (size > 1)
*dest = mempcpy(*dest, src, size-1);
size = 0;
truncated = true;
} else if (len > 0) {
*dest = mempcpy(*dest, src, len);
size -= len;
}
if (ret_truncated)
*ret_truncated = truncated;
*dest[0] = '\0';
return size;
}
size_t strpcpy(char **dest, size_t size, const char *src) {
size_t strpcpy_full(char **dest, size_t size, const char *src, bool *ret_truncated) {
assert(dest);
assert(src);
return strnpcpy(dest, size, src, strlen(src));
return strnpcpy_full(dest, size, src, strlen(src), ret_truncated);
}
size_t strpcpyf(char **dest, size_t size, const char *src, ...) {
size_t strpcpyf_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) {
bool truncated = false;
va_list va;
int i;
assert(dest);
assert(src);
if (size == 0)
return 0;
va_start(va, src);
i = vsnprintf(*dest, size, src, va);
if (i < (int)size) {
va_end(va);
if (i < (int) size) {
*dest += i;
size -= i;
} else
} else {
size = 0;
va_end(va);
truncated = i > 0;
}
if (ret_truncated)
*ret_truncated = truncated;
return size;
}
size_t strpcpyl(char **dest, size_t size, const char *src, ...) {
size_t strpcpyl_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) {
bool truncated = false;
va_list va;
assert(dest);
@ -73,31 +89,38 @@ size_t strpcpyl(char **dest, size_t size, const char *src, ...) {
va_start(va, src);
do {
size = strpcpy(dest, size, src);
bool t;
size = strpcpy_full(dest, size, src, &t);
truncated = truncated || t;
src = va_arg(va, char *);
} while (src);
va_end(va);
if (ret_truncated)
*ret_truncated = truncated;
return size;
}
size_t strnscpy(char *dest, size_t size, const char *src, size_t len) {
size_t strnscpy_full(char *dest, size_t size, const char *src, size_t len, bool *ret_truncated) {
char *s;
assert(dest);
assert(src);
s = dest;
return strnpcpy(&s, size, src, len);
return strnpcpy_full(&s, size, src, len, ret_truncated);
}
size_t strscpy(char *dest, size_t size, const char *src) {
size_t strscpy_full(char *dest, size_t size, const char *src, bool *ret_truncated) {
assert(dest);
assert(src);
return strnscpy(dest, size, src, strlen(src));
return strnscpy_full(dest, size, src, strlen(src), ret_truncated);
}
size_t strscpyl(char *dest, size_t size, const char *src, ...) {
size_t strscpyl_full(char *dest, size_t size, bool *ret_truncated, const char *src, ...) {
bool truncated = false;
va_list va;
char *s;
@ -107,10 +130,16 @@ size_t strscpyl(char *dest, size_t size, const char *src, ...) {
va_start(va, src);
s = dest;
do {
size = strpcpy(&s, size, src);
bool t;
size = strpcpy_full(&s, size, src, &t);
truncated = truncated || t;
src = va_arg(va, char *);
} while (src);
va_end(va);
if (ret_truncated)
*ret_truncated = truncated;
return size;
}

View File

@ -1,14 +1,33 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include "macro.h"
size_t strnpcpy(char **dest, size_t size, const char *src, size_t len);
size_t strpcpy(char **dest, size_t size, const char *src);
size_t strpcpyf(char **dest, size_t size, const char *src, ...) _printf_(3, 4);
size_t strpcpyl(char **dest, size_t size, const char *src, ...) _sentinel_;
size_t strnscpy(char *dest, size_t size, const char *src, size_t len);
size_t strscpy(char *dest, size_t size, const char *src);
size_t strscpyl(char *dest, size_t size, const char *src, ...) _sentinel_;
size_t strnpcpy_full(char **dest, size_t size, const char *src, size_t len, bool *ret_truncated);
static inline size_t strnpcpy(char **dest, size_t size, const char *src, size_t len) {
return strnpcpy_full(dest, size, src, len, NULL);
}
size_t strpcpy_full(char **dest, size_t size, const char *src, bool *ret_truncated);
static inline size_t strpcpy(char **dest, size_t size, const char *src) {
return strpcpy_full(dest, size, src, NULL);
}
size_t strpcpyf_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) _printf_(4, 5);
#define strpcpyf(dest, size, src, ...) \
strpcpyf_full((dest), (size), NULL, (src), ##__VA_ARGS__)
size_t strpcpyl_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) _sentinel_;
#define strpcpyl(dest, size, src, ...) \
strpcpyl_full((dest), (size), NULL, (src), ##__VA_ARGS__)
size_t strnscpy_full(char *dest, size_t size, const char *src, size_t len, bool *ret_truncated);
static inline size_t strnscpy(char *dest, size_t size, const char *src, size_t len) {
return strnscpy_full(dest, size, src, len, NULL);
}
size_t strscpy_full(char *dest, size_t size, const char *src, bool *ret_truncated);
static inline size_t strscpy(char *dest, size_t size, const char *src) {
return strscpy_full(dest, size, src, NULL);
}
size_t strscpyl_full(char *dest, size_t size, bool *ret_truncated, const char *src, ...) _sentinel_;
#define strscpyl(dest, size, src, ...) \
strscpyl_full(dest, size, NULL, src, ##__VA_ARGS__)

View File

@ -32,7 +32,7 @@ static inline void EventClosep(EFI_EVENT *event) {
* Also, multiple input protocols can be backed by the same device, but they can be out of
* sync. Falling back on a different protocol can end up with double input.
*
* Therefore, we will perferrably use TextInputEx for ConIn if that is available. Additionally,
* Therefore, we will preferably use TextInputEx for ConIn if that is available. Additionally,
* we look for the first TextInputEx device the firmware gives us as a fallback option. It
* will replace ConInEx permanently if it ever reports a key press.
* Lastly, a timer event allows us to provide a input timeout without having to call into

View File

@ -99,59 +99,6 @@ if efi_lds == ''
subdir_done()
endif
efi_headers = files('''
bcd.h
console.h
cpio.h
devicetree.h
disk.h
drivers.h
graphics.h
linux.h
measure.h
missing_efi.h
pe.h
random-seed.h
shim.h
splash.h
util.h
xbootldr.h
'''.split())
common_sources = '''
assert.c
devicetree.c
disk.c
graphics.c
measure.c
pe.c
secure-boot.c
util.c
'''.split()
systemd_boot_sources = '''
bcd.c
boot.c
console.c
drivers.c
random-seed.c
shim.c
xbootldr.c
'''.split()
stub_sources = '''
cpio.c
initrd.c
splash.c
stub.c
'''.split()
if efi_arch[1] in ['ia32', 'x86_64']
stub_sources += 'linux_x86.c'
else
stub_sources += 'linux.c'
endif
conf.set10('HAVE_GNU_EFI', true)
conf.set_quoted('EFI_MACHINE_TYPE_NAME', efi_arch[0])
@ -183,7 +130,6 @@ elif get_option('sbat-distro') != ''
if (value == '' or value == 'auto') and not meson.is_cross_build()
cmd = 'if [ -e /etc/os-release ]; then . /etc/os-release; else . /usr/lib/os-release; fi; echo $@0@'.format(sbatvar[1])
value = run_command(sh, '-c', cmd).stdout().strip()
message('@0@ (from @1@): @2@'.format(sbatvar[0], sbatvar[1], value))
endif
if value == ''
error('Required @0@ option not set and autodetection failed'.format(sbatvar[0]))
@ -200,8 +146,11 @@ elif get_option('sbat-distro') != ''
pkgver = get_option('sbat-distro-version')
if pkgver == ''
efi_conf.set('SBAT_DISTRO_VERSION', 'GIT_VERSION')
# This is determined during build, not configuration, so we can't display it yet.
sbat_distro_version_display = '(git version)'
else
efi_conf.set_quoted('SBAT_DISTRO_VERSION', pkgver)
sbat_distro_version_display = pkgver
endif
endif
@ -332,6 +281,76 @@ if efi_cc_version.contains('clang') and efi_cc_version.split('.')[0].split(' ')[
efi_ldflags += ['-Wl,-T,' + efi_lds, '-Wno-unused-command-line-argument']
endif
summary({
'EFI machine type' : efi_arch[0],
'EFI CC' : '@0@'.format(' '.join(efi_cc)),
'EFI LD' : efi_ld,
'EFI lds' : efi_lds,
'EFI crt0' : efi_crt0,
'EFI include directory' : efi_incdir},
section : 'Extensible Firmware Interface')
if efi_conf.get('SBAT_DISTRO', '') != ''
summary({
'SBAT distro': efi_conf.get('SBAT_DISTRO'),
'SBAT distro generation': efi_conf.get('SBAT_DISTRO_GENERATION'),
'SBAT distro version': sbat_distro_version_display,
'SBAT distro summary': efi_conf.get('SBAT_DISTRO_SUMMARY'),
'SBAT distro URL': efi_conf.get('SBAT_DISTRO_URL')},
section : 'Extensible Firmware Interface')
endif
############################################################
efi_headers = files(
'bcd.h',
'console.h',
'cpio.h',
'devicetree.h',
'disk.h',
'drivers.h',
'graphics.h',
'linux.h',
'measure.h',
'missing_efi.h',
'pe.h',
'random-seed.h',
'shim.h',
'splash.h',
'util.h',
'xbootldr.h')
common_sources = [
'assert.c',
'devicetree.c',
'disk.c',
'graphics.c',
'measure.c',
'pe.c',
'secure-boot.c',
'util.c']
systemd_boot_sources = [
'bcd.c',
'boot.c',
'console.c',
'drivers.c',
'random-seed.c',
'shim.c',
'xbootldr.c']
stub_sources = [
'cpio.c',
'initrd.c',
'splash.c',
'stub.c']
if efi_arch[1] in ['ia32', 'x86_64']
stub_sources += 'linux_x86.c'
else
stub_sources += 'linux.c'
endif
systemd_boot_objects = []
stub_objects = []
foreach file : fundamental_source_paths + common_sources + systemd_boot_sources + stub_sources

View File

@ -20,6 +20,7 @@
#include "json.h"
#include "log.h"
#include "main-func.h"
#include "os-util.h"
#include "pager.h"
#include "parse-argument.h"
#include "parse-util.h"
@ -31,6 +32,7 @@
#include "terminal-util.h"
#include "user-util.h"
#include "verbs.h"
#include "version.h"
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
static PagerFlags arg_pager_flags = 0;
@ -1329,13 +1331,20 @@ static int verb_monitor(int argc, char **argv, void *userdata) {
}
static int verb_capture(int argc, char **argv, void *userdata) {
_cleanup_free_ char *osname = NULL;
static const char info[] =
"busctl (systemd) " STRINGIFY(PROJECT_VERSION) " (Git " GIT_VERSION ")";
int r;
if (isatty(fileno(stdout)) > 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Refusing to write message data to console, please redirect output to a file.");
bus_pcap_header(arg_snaplen, stdout);
r = parse_os_release(NULL, "PRETTY_NAME", &osname);
if (r < 0)
log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_INFO, r,
"Failed to read os-release file, ignoring: %m");
bus_pcap_header(arg_snaplen, osname, info, stdout);
r = monitor(argc, argv, message_pcap);
if (r < 0)

View File

@ -370,21 +370,22 @@ static inline bool exec_context_with_rootfs(const ExecContext *c) {
}
typedef enum ExecFlags {
EXEC_APPLY_SANDBOXING = 1 << 0,
EXEC_APPLY_CHROOT = 1 << 1,
EXEC_APPLY_TTY_STDIN = 1 << 2,
EXEC_PASS_LOG_UNIT = 1 << 3, /* Whether to pass the unit name to the service's journal stream connection */
EXEC_CHOWN_DIRECTORIES = 1 << 4, /* chown() the runtime/state/cache/log directories to the user we run as, under all conditions */
EXEC_NSS_BYPASS_BUS = 1 << 5, /* Set the SYSTEMD_NSS_BYPASS_BUS environment variable, to disable nss-systemd for dbus */
EXEC_CGROUP_DELEGATE = 1 << 6,
EXEC_IS_CONTROL = 1 << 7,
EXEC_CONTROL_CGROUP = 1 << 8, /* Place the process not in the indicated cgroup but in a subcgroup '/.control', but only EXEC_CGROUP_DELEGATE and EXEC_IS_CONTROL is set, too */
EXEC_WRITE_CREDENTIALS = 1 << 9, /* Set up the credential store logic */
EXEC_APPLY_SANDBOXING = 1 << 0,
EXEC_APPLY_CHROOT = 1 << 1,
EXEC_APPLY_TTY_STDIN = 1 << 2,
EXEC_PASS_LOG_UNIT = 1 << 3, /* Whether to pass the unit name to the service's journal stream connection */
EXEC_CHOWN_DIRECTORIES = 1 << 4, /* chown() the runtime/state/cache/log directories to the user we run as, under all conditions */
EXEC_NSS_BYPASS_BUS = 1 << 5, /* Set the SYSTEMD_NSS_BYPASS_BUS environment variable, to disable nss-systemd for dbus */
EXEC_CGROUP_DELEGATE = 1 << 6,
EXEC_IS_CONTROL = 1 << 7,
EXEC_CONTROL_CGROUP = 1 << 8, /* Place the process not in the indicated cgroup but in a subcgroup '/.control', but only EXEC_CGROUP_DELEGATE and EXEC_IS_CONTROL is set, too */
EXEC_WRITE_CREDENTIALS = 1 << 9, /* Set up the credential store logic */
/* The following are not used by execute.c, but by consumers internally */
EXEC_PASS_FDS = 1 << 10,
EXEC_SETENV_RESULT = 1 << 11,
EXEC_SET_WATCHDOG = 1 << 12,
EXEC_PASS_FDS = 1 << 10,
EXEC_SETENV_RESULT = 1 << 11,
EXEC_SET_WATCHDOG = 1 << 12,
EXEC_SETENV_MONITOR_RESULT = 1 << 13, /* Pass exit status to OnFailure= and OnSuccess= dependencies. */
} ExecFlags;
/* Parameters for a specific invocation of a command. This structure is put together right before a command is

View File

@ -99,6 +99,13 @@ Job* job_free(Job *j) {
assert(!j->subject_list);
assert(!j->object_list);
do {
Unit *tu = NULL;
LIST_FOREACH(triggered_by, tu, j->triggered_by)
LIST_REMOVE(triggered_by, j->triggered_by, tu);
} while (!LIST_IS_EMPTY(j->triggered_by));
job_unlink(j);
sd_bus_track_unref(j->bus_track);
@ -107,6 +114,13 @@ Job* job_free(Job *j) {
return mfree(j);
}
void job_add_triggering_unit(Job *j, Unit *u) {
assert(j);
assert(u);
LIST_APPEND(triggered_by, j->triggered_by, u);
}
static void job_set_state(Job *j, JobState state) {
assert(j);
assert(state >= 0);
@ -187,6 +201,8 @@ static void job_merge_into_installed(Job *j, Job *other) {
j->irreversible = j->irreversible || other->irreversible;
j->ignore_order = j->ignore_order || other->ignore_order;
if (other->triggered_by)
LIST_JOIN(triggered_by, j->triggered_by, other->triggered_by);
}
Job* job_install(Job *j) {

View File

@ -124,6 +124,8 @@ struct Job {
LIST_HEAD(JobDependency, subject_list);
LIST_HEAD(JobDependency, object_list);
LIST_HEAD(Unit, triggered_by);
/* Used for graph algs as a "I have been here" marker */
Job* marker;
unsigned generation;
@ -244,3 +246,5 @@ JobResult job_result_from_string(const char *s) _pure_;
const char* job_type_to_access_method(JobType t);
int job_compare(Job *a, Job *b, UnitDependencyAtom assume_dep);
void job_add_triggering_unit(Job *j, Unit *u);

View File

@ -167,12 +167,18 @@ load_fragment_gperf_nulstr_c = custom_target(
command : [awk, '-f', '@INPUT0@', '@INPUT1@'],
capture : true)
libcore = static_library(
'core',
libcore_name = 'systemd-core-@0@'.format(shared_lib_tag)
libcore = shared_library(
libcore_name,
libcore_sources,
load_fragment_gperf_c,
load_fragment_gperf_nulstr_c,
include_directories : includes,
c_args : ['-fvisibility=default'],
link_args : ['-shared',
'-Wl,--version-script=' + libshared_sym_path],
link_with : libshared,
dependencies : [versiondep,
threads,
libdl,
@ -184,8 +190,10 @@ libcore = static_library(
libapparmor,
libselinux,
libmount,
libblkid,
libacl],
build_by_default : false)
install : true,
install_dir : rootlibexecdir)
core_includes = [includes, include_directories('.')]

View File

@ -1440,6 +1440,93 @@ static bool service_exec_needs_notify_socket(Service *s, ExecFlags flags) {
return s->notify_access != NOTIFY_NONE;
}
static int service_create_monitor_md_env(Job *j, char **ret) {
_cleanup_free_ char *var = NULL;
const char *list_delim = ";";
bool first = true;
Unit *tu;
assert(j);
assert(ret);
/* Create an environment variable 'MONITOR_METADATA', if creation is successful
* a pointer to it is returned via ret.
*
* This variable contains a space separated set of fields which relate to
* the service(s) which triggered job 'j'. Job 'j' is the JOB_START job for
* an OnFailure= or OnSuccess= dependency. Format of the MONITOR_METADATA
* variable is as follows:
*
* MONITOR_METADATA="SERVICE_RESULT=<result-string0>,EXIT_CODE=<exit-code0>,EXIT_STATUS=<exit-status0>,
* INVOCATION_ID=<id>,UNIT=<triggering-unit0.service>;
* SERVICE_RESULT=<result-stringN>,EXIT_CODE=<exit-codeN>,EXIT_STATUS=<exit-statusN>,
* INVOCATION_ID=<id>,UNIT=<triggering-unitN.service>"
*
* Multiple results may be passed as in the above example if jobs are merged, i.e.
* some services a and b contain an OnFailure= or OnSuccess= dependency on the same
* service.
*
* For example:
*
* MONITOR_METADATA="SERVICE_RESULT=exit-code,EXIT_CODE=exited,EXIT_STATUS=1,INVOCATION_ID=02dd868af2f344b18edaf74b618b2f90,UNIT=failure.service;
* SERVICE_RESULT=exit-code,EXIT_CODE=exited,EXIT_STATUS=1,INVOCATION_ID=80cb228bd7344f77a090eda603a3cfe2,UNIT=failure2.service"
*/
LIST_FOREACH(triggered_by, tu, j->triggered_by) {
Service *env_source = SERVICE(tu);
int r;
if (!env_source)
continue;
if (first) {
/* Add the environment variable name first. */
r = strextendf(&var, "MONITOR_METADATA=");
if (r < 0)
return r;
}
r = strextendf(&var, "%sSERVICE_RESULT=%s",
!first ? list_delim : "", service_result_to_string(env_source->result));
if (r < 0)
return r;
first = false;
if (env_source->main_exec_status.pid > 0 &&
dual_timestamp_is_set(&env_source->main_exec_status.exit_timestamp)) {
r = strextendf(&var, ",EXIT_CODE=%s",
sigchld_code_to_string(env_source->main_exec_status.code));
if (r < 0)
return r;
if (env_source->main_exec_status.code == CLD_EXITED)
r = strextendf(&var, ",EXIT_STATUS=%i",
env_source->main_exec_status.status);
else
r = strextendf(&var, ",EXIT_STATUS=%s",
signal_to_string(env_source->main_exec_status.status));
if (r < 0)
return r;
}
if (!sd_id128_is_null(UNIT(env_source)->invocation_id)) {
r = strextendf(&var, ",INVOCATION_ID=" SD_ID128_FORMAT_STR,
SD_ID128_FORMAT_VAL(UNIT(env_source)->invocation_id));
if (r < 0)
return r;
}
r = strextendf(&var, ",UNIT=%s", UNIT(env_source)->id);
if (r < 0)
return r;
}
*ret = TAKE_PTR(var);
return 0;
}
static int service_spawn(
Service *s,
ExecCommand *c,
@ -1574,9 +1661,18 @@ static int service_spawn(
r = asprintf(our_env + n_env++, "EXIT_STATUS=%i", s->main_exec_status.status);
else
r = asprintf(our_env + n_env++, "EXIT_STATUS=%s", signal_to_string(s->main_exec_status.status));
if (r < 0)
return -ENOMEM;
}
} else if (flags & EXEC_SETENV_MONITOR_RESULT) {
Job *j = UNIT(s)->job;
if (j) {
r = service_create_monitor_md_env(j, our_env + n_env++);
if (r < 0)
return r;
}
}
r = unit_set_exec_params(UNIT(s), &exec_params);
@ -2164,7 +2260,7 @@ static void service_enter_start(Service *s) {
r = service_spawn(s,
c,
timeout,
EXEC_PASS_FDS|EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG|EXEC_WRITE_CREDENTIALS,
EXEC_PASS_FDS|EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG|EXEC_WRITE_CREDENTIALS|EXEC_SETENV_MONITOR_RESULT,
&pid);
if (r < 0)
goto fail;
@ -2222,7 +2318,7 @@ static void service_enter_start_pre(Service *s) {
r = service_spawn(s,
s->control_command,
s->timeout_start_usec,
EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN,
EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN|EXEC_SETENV_MONITOR_RESULT,
&s->control_pid);
if (r < 0)
goto fail;

View File

@ -82,11 +82,15 @@ static const UnitDependencyAtom atom_map[_UNIT_DEPENDENCY_MAX] = {
[UNIT_PROPAGATES_STOP_TO] = UNIT_ATOM_RETROACTIVE_STOP_ON_STOP |
UNIT_ATOM_PROPAGATE_STOP,
[UNIT_ON_FAILURE] = UNIT_ATOM_ON_FAILURE |
UNIT_ATOM_BACK_REFERENCE_IMPLIED,
[UNIT_ON_SUCCESS] = UNIT_ATOM_ON_SUCCESS |
UNIT_ATOM_BACK_REFERENCE_IMPLIED,
/* These are simple dependency types: they consist of a single atom only */
[UNIT_BEFORE] = UNIT_ATOM_BEFORE,
[UNIT_AFTER] = UNIT_ATOM_AFTER,
[UNIT_ON_SUCCESS] = UNIT_ATOM_ON_SUCCESS,
[UNIT_ON_FAILURE] = UNIT_ATOM_ON_FAILURE,
[UNIT_TRIGGERS] = UNIT_ATOM_TRIGGERS,
[UNIT_TRIGGERED_BY] = UNIT_ATOM_TRIGGERED_BY,
[UNIT_PROPAGATES_RELOAD_TO] = UNIT_ATOM_PROPAGATES_RELOAD_TO,
@ -196,6 +200,16 @@ UnitDependency unit_dependency_from_unique_atom(UnitDependencyAtom atom) {
case UNIT_ATOM_PROPAGATE_STOP_FAILURE:
return UNIT_CONFLICTED_BY;
case UNIT_ATOM_ON_FAILURE |
UNIT_ATOM_BACK_REFERENCE_IMPLIED:
case UNIT_ATOM_ON_FAILURE:
return UNIT_ON_FAILURE;
case UNIT_ATOM_ON_SUCCESS |
UNIT_ATOM_BACK_REFERENCE_IMPLIED:
case UNIT_ATOM_ON_SUCCESS:
return UNIT_ON_SUCCESS;
/* And now, the simple ones */
case UNIT_ATOM_BEFORE:
@ -204,12 +218,6 @@ UnitDependency unit_dependency_from_unique_atom(UnitDependencyAtom atom) {
case UNIT_ATOM_AFTER:
return UNIT_AFTER;
case UNIT_ATOM_ON_SUCCESS:
return UNIT_ON_SUCCESS;
case UNIT_ATOM_ON_FAILURE:
return UNIT_ON_FAILURE;
case UNIT_ATOM_TRIGGERS:
return UNIT_TRIGGERS;

View File

@ -66,20 +66,27 @@ typedef enum UnitDependencyAtom {
/* Recheck default target deps on other units (which are target units) */
UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES = UINT64_C(1) << 21,
/* Dependencies which include this atom automatically get a reverse
* REFERENCES/REFERENCED_BY dependency. */
UNIT_ATOM_BACK_REFERENCE_IMPLIED = UINT64_C(1) << 22,
/* Trigger a dependency on successful service exit. */
UNIT_ATOM_ON_SUCCESS = UINT64_C(1) << 23,
/* Trigger a dependency on unsuccessful service exit. */
UNIT_ATOM_ON_FAILURE = UINT64_C(1) << 24,
/* The remaining atoms map 1:1 to the equally named high-level deps */
UNIT_ATOM_BEFORE = UINT64_C(1) << 22,
UNIT_ATOM_AFTER = UINT64_C(1) << 23,
UNIT_ATOM_ON_SUCCESS = UINT64_C(1) << 24,
UNIT_ATOM_ON_FAILURE = UINT64_C(1) << 25,
UNIT_ATOM_TRIGGERS = UINT64_C(1) << 26,
UNIT_ATOM_TRIGGERED_BY = UINT64_C(1) << 27,
UNIT_ATOM_PROPAGATES_RELOAD_TO = UINT64_C(1) << 28,
UNIT_ATOM_JOINS_NAMESPACE_OF = UINT64_C(1) << 29,
UNIT_ATOM_REFERENCES = UINT64_C(1) << 30,
UNIT_ATOM_REFERENCED_BY = UINT64_C(1) << 31,
UNIT_ATOM_IN_SLICE = UINT64_C(1) << 32,
UNIT_ATOM_SLICE_OF = UINT64_C(1) << 33,
_UNIT_DEPENDENCY_ATOM_MAX = (UINT64_C(1) << 34) - 1,
UNIT_ATOM_BEFORE = UINT64_C(1) << 25,
UNIT_ATOM_AFTER = UINT64_C(1) << 26,
UNIT_ATOM_TRIGGERS = UINT64_C(1) << 27,
UNIT_ATOM_TRIGGERED_BY = UINT64_C(1) << 28,
UNIT_ATOM_PROPAGATES_RELOAD_TO = UINT64_C(1) << 29,
UNIT_ATOM_JOINS_NAMESPACE_OF = UINT64_C(1) << 30,
UNIT_ATOM_REFERENCES = UINT64_C(1) << 31,
UNIT_ATOM_REFERENCED_BY = UINT64_C(1) << 32,
UNIT_ATOM_IN_SLICE = UINT64_C(1) << 33,
UNIT_ATOM_SLICE_OF = UINT64_C(1) << 34,
_UNIT_DEPENDENCY_ATOM_MAX = (UINT64_C(1) << 35) - 1,
_UNIT_DEPENDENCY_ATOM_INVALID = -EINVAL,
} UnitDependencyAtom;

View File

@ -2222,17 +2222,24 @@ void unit_start_on_failure(
UNIT_FOREACH_DEPENDENCY(other, u, atom) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
Job *job = NULL;
if (!logged) {
log_unit_info(u, "Triggering %s dependencies.", dependency_name);
logged = true;
}
r = manager_add_job(u->manager, JOB_START, other, job_mode, NULL, &error, NULL);
r = manager_add_job(u->manager, JOB_START, other, job_mode, NULL, &error, &job);
if (r < 0)
log_unit_warning_errno(
u, r, "Failed to enqueue %s job, ignoring: %s",
dependency_name, bus_error_message(&error, r));
else if (job)
/* u will be kept pinned since both UNIT_ON_FAILURE and UNIT_ON_SUCCESS includes
* UNIT_ATOM_BACK_REFERENCE_IMPLIED. We save the triggering unit here since we
* want to be able to reference it when we come to run the OnFailure= or OnSuccess=
* dependency. */
job_add_triggering_unit(job, u);
}
if (logged)
@ -3114,6 +3121,20 @@ int unit_add_dependency(
noop = false;
}
if (FLAGS_SET(a, UNIT_ATOM_BACK_REFERENCE_IMPLIED)) {
r = unit_add_dependency_hashmap(&other->dependencies, UNIT_REFERENCES, u, 0, mask);
if (r < 0)
return r;
if (r)
noop = false;
r = unit_add_dependency_hashmap(&u->dependencies, UNIT_REFERENCED_BY, other, 0, mask);
if (r < 0)
return r;
if (r)
noop = false;
}
if (add_reference) {
r = unit_add_dependency_hashmap(&u->dependencies, UNIT_REFERENCES, other, mask, 0);
if (r < 0)

View File

@ -242,6 +242,9 @@ typedef struct Unit {
/* Queue of units that have a BindTo= dependency on some other unit, and should possibly be shut down */
LIST_FIELDS(Unit, stop_when_bound_queue);
/* Queue of units which have triggered an OnFailure= or OnSuccess= dependency job. */
LIST_FIELDS(Unit, triggered_by);
/* PIDs we keep an eye on. Note that a unit might have many
* more, but these are the ones we care enough about to
* process SIGCHLD for */

View File

@ -13,6 +13,7 @@
#include "format-util.h"
#include "glyph-util.h"
#include "macro.h"
#include "pcapng.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
@ -502,57 +503,99 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) {
return 0;
}
/*
* For details about the file format, see:
*
* http://wiki.wireshark.org/Development/LibpcapFileFormat
*/
static uint16_t pcapng_optlen(size_t len) {
return ALIGN4(len + sizeof(struct pcapng_option));
}
typedef struct _packed_ pcap_hdr_s {
uint32_t magic_number; /* magic number */
uint16_t version_major; /* major version number */
uint16_t version_minor; /* minor version number */
int32_t thiszone; /* GMT to local correction */
uint32_t sigfigs; /* accuracy of timestamps */
uint32_t snaplen; /* max length of captured packets, in octets */
uint32_t network; /* data link type */
} pcap_hdr_t ;
typedef struct _packed_ pcaprec_hdr_s {
uint32_t ts_sec; /* timestamp seconds */
uint32_t ts_usec; /* timestamp microseconds */
uint32_t incl_len; /* number of octets of packet saved in file */
uint32_t orig_len; /* actual length of packet */
} pcaprec_hdr_t;
int bus_pcap_header(size_t snaplen, FILE *f) {
pcap_hdr_t hdr = {
.magic_number = 0xa1b2c3d4U,
.version_major = 2,
.version_minor = 4,
.thiszone = 0, /* UTC */
.sigfigs = 0,
.network = 231, /* D-Bus */
static void pcapng_putopt(FILE *f, uint16_t code, const void *data, size_t len) {
struct pcapng_option opt = {
.code = code,
.length = len,
};
if (!f)
f = stdout;
assert(f);
assert((uint16_t) len == len);
assert(data || len == 0);
fwrite(&opt, 1, sizeof(opt), f);
if (len > 0) {
size_t pad = ALIGN4(len) - len;
fwrite(data, 1, len, f);
assert(pad < sizeof(uint32_t));
while (pad-- > 0)
fputc('\0', f);
}
}
static void pcapng_section_header(FILE *f, const char *os, const char *app) {
uint32_t len;
assert(f);
/* determine length of section header and options */
len = sizeof(struct pcapng_section);
if (os)
len += pcapng_optlen(strlen(os));
if (app)
len += pcapng_optlen(strlen(app));
len += pcapng_optlen(0); /* OPT_END */
len += sizeof(uint32_t); /* trailer length */
struct pcapng_section hdr = {
.block_type = PCAPNG_SECTION_BLOCK,
.block_length = len,
.byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC,
.major_version = PCAPNG_MAJOR_VERS,
.minor_version = PCAPNG_MINOR_VERS,
.section_length = UINT64_MAX,
};
fwrite(&hdr, 1, sizeof(hdr), f);
if (os)
pcapng_putopt(f, PCAPNG_SHB_OS, os, strlen(os));
if (app)
pcapng_putopt(f, PCAPNG_SHB_USERAPPL, app, strlen(app));
pcapng_putopt(f, PCAPNG_OPT_END, NULL, 0);
fwrite(&len, 1, sizeof(uint32_t), f);
}
/* Only have a single instance of dbus pseudo interface */
static void pcapng_interface_header(FILE *f, size_t snaplen) {
uint32_t len;
assert(f);
assert(snaplen > 0);
assert((size_t) (uint32_t) snaplen == snaplen);
hdr.snaplen = (uint32_t) snaplen;
/* no options (yet) */
len = sizeof(struct pcapng_interface_block) + sizeof(uint32_t);
struct pcapng_interface_block hdr = {
.block_type = PCAPNG_INTERFACE_BLOCK,
.block_length = len,
.link_type = 231, /* D-Bus */
.snap_len = snaplen,
};
fwrite(&hdr, 1, sizeof(hdr), f);
fwrite(&len, 1, sizeof(uint32_t), f);
}
int bus_pcap_header(size_t snaplen, const char *os, const char *info, FILE *f) {
if (!f)
f = stdout;
pcapng_section_header(f, os, info);
pcapng_interface_header(f, snaplen);
return fflush_and_check(f);
}
int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) {
struct bus_body_part *part;
pcaprec_hdr_t hdr = {};
struct timeval tv;
size_t msglen, caplen, pad;
uint32_t length;
uint64_t ts;
unsigned i;
size_t w;
@ -563,18 +606,27 @@ int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) {
assert(snaplen > 0);
assert((size_t) (uint32_t) snaplen == snaplen);
if (m->realtime != 0)
timeval_store(&tv, m->realtime);
else
assert_se(gettimeofday(&tv, NULL) >= 0);
ts = m->realtime ?: now(CLOCK_REALTIME);
msglen = BUS_MESSAGE_SIZE(m);
caplen = MIN(msglen, snaplen);
pad = ALIGN4(caplen) - caplen;
hdr.ts_sec = tv.tv_sec;
hdr.ts_usec = tv.tv_usec;
hdr.orig_len = BUS_MESSAGE_SIZE(m);
hdr.incl_len = MIN(hdr.orig_len, snaplen);
/* packet block has no options */
length = sizeof(struct pcapng_enhance_packet_block)
+ caplen + pad + sizeof(uint32_t);
/* write the pcap header */
fwrite(&hdr, 1, sizeof(hdr), f);
struct pcapng_enhance_packet_block epb = {
.block_type = PCAPNG_ENHANCED_PACKET_BLOCK,
.block_length = length,
.interface_id = 0,
.timestamp_hi = (uint32_t)(ts >> 32),
.timestamp_lo = (uint32_t)ts,
.original_length = msglen,
.capture_length = caplen,
};
/* write the pcapng enhanced packet block header */
fwrite(&epb, 1, sizeof(epb), f);
/* write the dbus header */
w = MIN(BUS_MESSAGE_BODY_BEGIN(m), snaplen);
@ -591,5 +643,11 @@ int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) {
snaplen -= w;
}
while (pad-- > 0)
fputc('\0', f);
/* trailing block length */
fwrite(&length, 1, sizeof(uint32_t), f);
return fflush_and_check(f);
}

View File

@ -8,5 +8,5 @@
int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse);
int bus_pcap_header(size_t snaplen, FILE *f);
int bus_pcap_header(size_t snaplen, const char *os, const char *app, FILE *f);
int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f);

View File

@ -20,6 +20,8 @@ sources = files('''
netdev/macvlan.c
netdev/macvlan.h
netdev/netdev.c
netdev/netdev-util.c
netdev/netdev-util.h
netdev/netdev.h
netdev/nlmon.c
netdev/nlmon.h

View File

@ -68,8 +68,8 @@ IPVLAN.Mode, config_parse_ipvlan_mode,
IPVLAN.Flags, config_parse_ipvlan_flags, 0, offsetof(IPVlan, flags)
IPVTAP.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode)
IPVTAP.Flags, config_parse_ipvlan_flags, 0, offsetof(IPVlan, flags)
Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local)
Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote)
Tunnel.Local, config_parse_tunnel_local_address, 0, 0
Tunnel.Remote, config_parse_tunnel_remote_address, 0, 0
Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos)
Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl)
Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key)

View File

@ -0,0 +1,97 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "netdev-util.h"
#include "networkd-address.h"
#include "networkd-link.h"
#include "string-table.h"
static const char * const netdev_local_address_type_table[_NETDEV_LOCAL_ADDRESS_TYPE_MAX] = {
[NETDEV_LOCAL_ADDRESS_IPV4LL] = "ipv4_link_local",
[NETDEV_LOCAL_ADDRESS_IPV6LL] = "ipv6_link_local",
[NETDEV_LOCAL_ADDRESS_DHCP4] = "dhcp4",
[NETDEV_LOCAL_ADDRESS_DHCP6] = "dhcp6",
[NETDEV_LOCAL_ADDRESS_SLAAC] = "slaac",
};
DEFINE_STRING_TABLE_LOOKUP(netdev_local_address_type, NetDevLocalAddressType);
int link_get_local_address(
Link *link,
NetDevLocalAddressType type,
int family,
int *ret_family,
union in_addr_union *ret_address) {
Address *a;
assert(link);
switch (type) {
case NETDEV_LOCAL_ADDRESS_IPV4LL:
assert(IN_SET(family, AF_UNSPEC, AF_INET));
family = AF_INET;
break;
case NETDEV_LOCAL_ADDRESS_IPV6LL:
assert(IN_SET(family, AF_UNSPEC, AF_INET6));
family = AF_INET6;
break;
case NETDEV_LOCAL_ADDRESS_DHCP4:
assert(IN_SET(family, AF_UNSPEC, AF_INET));
family = AF_INET;
break;
case NETDEV_LOCAL_ADDRESS_DHCP6:
assert(IN_SET(family, AF_UNSPEC, AF_INET6));
family = AF_INET6;
break;
case NETDEV_LOCAL_ADDRESS_SLAAC:
assert(IN_SET(family, AF_UNSPEC, AF_INET6));
family = AF_INET6;
break;
default:
assert_not_reached();
}
SET_FOREACH(a, link->addresses) {
if (!address_exists(a))
continue;
if (a->family != family)
continue;
if (in_addr_is_set(a->family, &a->in_addr_peer))
continue;
switch (type) {
case NETDEV_LOCAL_ADDRESS_IPV4LL:
if (a->source != NETWORK_CONFIG_SOURCE_IPV4LL)
continue;
break;
case NETDEV_LOCAL_ADDRESS_IPV6LL:
if (!in6_addr_is_link_local(&a->in_addr.in6))
continue;
break;
case NETDEV_LOCAL_ADDRESS_DHCP4:
if (a->source != NETWORK_CONFIG_SOURCE_DHCP4)
continue;
break;
case NETDEV_LOCAL_ADDRESS_DHCP6:
if (a->source != NETWORK_CONFIG_SOURCE_DHCP6)
continue;
break;
case NETDEV_LOCAL_ADDRESS_SLAAC:
if (a->source != NETWORK_CONFIG_SOURCE_NDISC)
continue;
break;
default:
assert_not_reached();
}
if (ret_family)
*ret_family = a->family;
if (ret_address)
*ret_address = a->in_addr;
return 1;
}
return -ENXIO;
}

View File

@ -0,0 +1,27 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "in-addr-util.h"
#include "macro.h"
typedef struct Link Link;
typedef enum NetDevLocalAddressType {
NETDEV_LOCAL_ADDRESS_IPV4LL,
NETDEV_LOCAL_ADDRESS_IPV6LL,
NETDEV_LOCAL_ADDRESS_DHCP4,
NETDEV_LOCAL_ADDRESS_DHCP6,
NETDEV_LOCAL_ADDRESS_SLAAC,
_NETDEV_LOCAL_ADDRESS_TYPE_MAX,
_NETDEV_LOCAL_ADDRESS_TYPE_INVALID = -EINVAL,
} NetDevLocalAddressType;
const char *netdev_local_address_type_to_string(NetDevLocalAddressType t) _const_;
NetDevLocalAddressType netdev_local_address_type_from_string(const char *s) _pure_;
int link_get_local_address(
Link *link,
NetDevLocalAddressType type,
int family,
int *ret_family,
union in_addr_union *ret_address);

View File

@ -628,6 +628,9 @@ static bool netdev_is_ready_to_create(NetDev *netdev, Link *link) {
if (link->set_link_messages > 0)
return false;
if (NETDEV_VTABLE(netdev)->is_ready_to_create)
return NETDEV_VTABLE(netdev)->is_ready_to_create(netdev, link);
return true;
}

View File

@ -153,6 +153,9 @@ typedef struct NetDevVTable {
/* specifies if netdev is independent, or a master device or a stacked device */
NetDevCreateType create_type;
/* This is used for stacked netdev. Return true when the underlying link is ready. */
int (*is_ready_to_create)(NetDev *netdev, Link *link);
/* create netdev, if not done via rtnl */
int (*create)(NetDev *netdev);

View File

@ -168,7 +168,20 @@ int dhcp4_pd_create_6rd_tunnel(Link *link, link_netlink_message_handler_t callba
return 0;
}
static int tunnel_get_local_address(Tunnel *t, Link *link, union in_addr_union *ret) {
assert(t);
if (t->local_type < 0) {
if (ret)
*ret = t->local;
return 0;
}
return link_get_local_address(link, t->local_type, t->family, NULL, ret);
}
static int netdev_ipip_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
union in_addr_union local;
Tunnel *t;
int r;
@ -188,7 +201,11 @@ static int netdev_ipip_sit_fill_message_create(NetDev *netdev, Link *link, sd_ne
return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
}
r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in);
r = tunnel_get_local_address(t, link, &local);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not find local address: %m");
r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &local.in);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m");
@ -247,6 +264,7 @@ static int netdev_ipip_sit_fill_message_create(NetDev *netdev, Link *link, sd_ne
}
static int netdev_gre_erspan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
union in_addr_union local;
uint32_t ikey = 0;
uint32_t okey = 0;
uint16_t iflags = 0;
@ -285,7 +303,11 @@ static int netdev_gre_erspan_fill_message_create(NetDev *netdev, Link *link, sd_
return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_ERSPAN_INDEX attribute: %m");
}
r = sd_netlink_message_append_in_addr(m, IFLA_GRE_LOCAL, &t->local.in);
r = tunnel_get_local_address(t, link, &local);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not find local address: %m");
r = sd_netlink_message_append_in_addr(m, IFLA_GRE_LOCAL, &local.in);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LOCAL attribute: %m");
@ -363,6 +385,7 @@ static int netdev_gre_erspan_fill_message_create(NetDev *netdev, Link *link, sd_
}
static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
union in_addr_union local;
uint32_t ikey = 0;
uint32_t okey = 0;
uint16_t iflags = 0;
@ -386,7 +409,11 @@ static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netl
return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK attribute: %m");
}
r = sd_netlink_message_append_in6_addr(m, IFLA_GRE_LOCAL, &t->local.in6);
r = tunnel_get_local_address(t, link, &local);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not find local address: %m");
r = sd_netlink_message_append_in6_addr(m, IFLA_GRE_LOCAL, &local.in6);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LOCAL attribute: %m");
@ -444,6 +471,7 @@ static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netl
}
static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
union in_addr_union local;
uint32_t ikey, okey;
Tunnel *t;
int r;
@ -479,7 +507,11 @@ static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_netlink
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_OKEY attribute: %m");
r = netlink_message_append_in_addr_union(m, IFLA_VTI_LOCAL, t->family, &t->local);
r = tunnel_get_local_address(t, link, &local);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not find local address: %m");
r = netlink_message_append_in_addr_union(m, IFLA_VTI_LOCAL, t->family, &local);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_LOCAL attribute: %m");
@ -491,6 +523,7 @@ static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_netlink
}
static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
union in_addr_union local;
uint8_t proto;
Tunnel *t;
int r;
@ -508,7 +541,11 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl
return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
}
r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_LOCAL, &t->local.in6);
r = tunnel_get_local_address(t, link, &local);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not find local address: %m");
r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_LOCAL, &local.in6);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m");
@ -562,6 +599,19 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl
return r;
}
static int netdev_tunnel_is_ready_to_create(NetDev *netdev, Link *link) {
Tunnel *t;
assert(netdev);
assert(link);
t = TUNNEL(netdev);
assert(t);
return tunnel_get_local_address(t, link, NULL) >= 0;
}
static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
Tunnel *t;
@ -611,10 +661,15 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
if (t->assign_to_loopback)
t->independent = true;
if (t->independent && t->local_type >= 0)
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
"The local address cannot be '%s' when Independent= or AssignToLoopback= is enabled, ignoring.",
strna(netdev_local_address_type_to_string(t->local_type)));
return 0;
}
int config_parse_tunnel_address(
int config_parse_tunnel_local_address(
const char *unit,
const char *filename,
unsigned line,
@ -626,28 +681,82 @@ int config_parse_tunnel_address(
void *data,
void *userdata) {
union in_addr_union buffer = IN_ADDR_NULL;
NetDevLocalAddressType type;
Tunnel *t = userdata;
union in_addr_union *addr = data, buffer;
int r, f;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
assert(userdata);
/* This is used to parse addresses on both local and remote ends of the tunnel.
* Address families must match.
*
* "any" is a special value which means that the address is unspecified.
*/
if (isempty(rvalue) || streq(rvalue, "any")) {
/* Unset the previous assignment. */
t->local = IN_ADDR_NULL;
t->local_type = _NETDEV_LOCAL_ADDRESS_TYPE_INVALID;
if (streq(rvalue, "any")) {
*addr = IN_ADDR_NULL;
/* If the remote address is not specified, also clear the address family. */
if (!in_addr_is_set(t->family, &t->remote))
t->family = AF_UNSPEC;
return 0;
}
/* As a special case, if both the local and remote addresses are
* unspecified, also clear the address family. */
if (!in_addr_is_set(t->family, &t->local) &&
!in_addr_is_set(t->family, &t->remote))
type = netdev_local_address_type_from_string(rvalue);
if (IN_SET(type, NETDEV_LOCAL_ADDRESS_IPV4LL, NETDEV_LOCAL_ADDRESS_DHCP4))
f = AF_INET;
else if (IN_SET(type, NETDEV_LOCAL_ADDRESS_IPV6LL, NETDEV_LOCAL_ADDRESS_DHCP6, NETDEV_LOCAL_ADDRESS_SLAAC))
f = AF_INET6;
else {
type = _NETDEV_LOCAL_ADDRESS_TYPE_INVALID;
r = in_addr_from_string_auto(rvalue, &f, &buffer);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Tunnel address \"%s\" invalid, ignoring assignment: %m", rvalue);
return 0;
}
}
if (t->family != AF_UNSPEC && t->family != f) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
"Address family does not match the previous assignment, ignoring assignment: %s", rvalue);
return 0;
}
t->family = f;
t->local = buffer;
t->local_type = type;
return 0;
}
int config_parse_tunnel_remote_address(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
union in_addr_union buffer;
Tunnel *t = userdata;
int r, f;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(userdata);
if (isempty(rvalue) || streq(rvalue, "any")) {
/* Unset the previous assignment. */
t->remote = IN_ADDR_NULL;
/* If the local address is not specified, also clear the address family. */
if (t->local_type == _NETDEV_LOCAL_ADDRESS_TYPE_INVALID &&
!in_addr_is_set(t->family, &t->local))
t->family = AF_UNSPEC;
return 0;
}
@ -661,12 +770,12 @@ int config_parse_tunnel_address(
if (t->family != AF_UNSPEC && t->family != f) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
"Tunnel addresses incompatible, ignoring assignment: %s", rvalue);
"Address family does not match the previous assignment, ignoring assignment: %s", rvalue);
return 0;
}
t->family = f;
*addr = buffer;
t->remote = buffer;
return 0;
}
@ -840,6 +949,7 @@ static void netdev_tunnel_init(NetDev *netdev) {
assert(t);
t->local_type = _NETDEV_LOCAL_ADDRESS_TYPE_INVALID;
t->pmtudisc = true;
t->fou_encap_type = NETDEV_FOO_OVER_UDP_ENCAP_DIRECT;
t->isatap = -1;
@ -859,6 +969,7 @@ const NetDevVTable ipip_vtable = {
.sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_ipip_sit_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
.iftype = ARPHRD_TUNNEL,
};
@ -869,6 +980,7 @@ const NetDevVTable sit_vtable = {
.sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_ipip_sit_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
.iftype = ARPHRD_SIT,
};
@ -879,6 +991,7 @@ const NetDevVTable vti_vtable = {
.sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_vti_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
.iftype = ARPHRD_TUNNEL,
};
@ -889,6 +1002,7 @@ const NetDevVTable vti6_vtable = {
.sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_vti_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
.iftype = ARPHRD_TUNNEL6,
};
@ -899,6 +1013,7 @@ const NetDevVTable gre_vtable = {
.sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_gre_erspan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
.iftype = ARPHRD_IPGRE,
};
@ -909,6 +1024,7 @@ const NetDevVTable gretap_vtable = {
.sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_gre_erspan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
@ -920,6 +1036,7 @@ const NetDevVTable ip6gre_vtable = {
.sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_ip6gre_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
.iftype = ARPHRD_IP6GRE,
};
@ -930,6 +1047,7 @@ const NetDevVTable ip6gretap_vtable = {
.sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_ip6gre_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
.iftype = ARPHRD_ETHER,
.generate_mac = true,
@ -941,6 +1059,7 @@ const NetDevVTable ip6tnl_vtable = {
.sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_ip6tnl_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
.iftype = ARPHRD_TUNNEL6,
};
@ -951,6 +1070,7 @@ const NetDevVTable erspan_vtable = {
.sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_gre_erspan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.is_ready_to_create = netdev_tunnel_is_ready_to_create,
.config_verify = netdev_tunnel_verify,
.iftype = ARPHRD_ETHER,
.generate_mac = true,

View File

@ -5,6 +5,7 @@
#include "conf-parser.h"
#include "fou-tunnel.h"
#include "netdev-util.h"
#include "netdev.h"
#include "networkd-link.h"
@ -42,6 +43,7 @@ typedef struct Tunnel {
uint32_t okey;
uint32_t erspan_index;
NetDevLocalAddressType local_type;
union in_addr_union local;
union in_addr_union remote;
@ -119,7 +121,8 @@ const char *ip6tnl_mode_to_string(Ip6TnlMode d) _const_;
Ip6TnlMode ip6tnl_mode_from_string(const char *d) _pure_;
CONFIG_PARSER_PROTOTYPE(config_parse_ip6tnl_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_address);
CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_local_address);
CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_remote_address);
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_flowlabel);
CONFIG_PARSER_PROTOTYPE(config_parse_encap_limit);
CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_key);

View File

@ -2550,6 +2550,23 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
if (r < 0)
log_link_debug_errno(link, r, "Failed to get driver, continuing without: %m");
if (streq_ptr(link->driver, "dsa")) {
uint32_t dsa_master_ifindex;
r = sd_netlink_message_read_u32(message, IFLA_LINK, &dsa_master_ifindex);
if (r < 0) {
dsa_master_ifindex = 0;
if (r != -ENODATA)
log_link_warning_errno(link, r, "rtnl: failed to read ifindex of the DSA master interface, ignoring: %m");
} else if (dsa_master_ifindex > INT_MAX) {
dsa_master_ifindex = 0;
log_link_warning(link, "rtnl: received too large DSA master ifindex (%"PRIu32" > INT_MAX), ignoring.",
dsa_master_ifindex);
}
link->dsa_master_ifindex = (int) dsa_master_ifindex;
}
*ret = TAKE_PTR(link);
return 0;
}

View File

@ -48,6 +48,7 @@ typedef struct Link {
int ifindex;
int master_ifindex;
int dsa_master_ifindex;
char *ifname;
char **alternative_names;
char *kind;

View File

@ -948,7 +948,42 @@ int link_configure_mtu(Link *link) {
return link_request_to_set_mtu(link, mtu);
}
static int link_up_or_down_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool up, bool check_ready) {
static int link_up_dsa_slave(Link *link) {
Link *master;
int r;
assert(link);
/* For older kernels (specifically, older than 9d5ef190e5615a7b63af89f88c4106a5bc127974, kernel-5.12),
* it is necessary to bring up a DSA slave that its master interface is already up. And bringing up
* the slave fails with -ENETDOWN. So, let's bring up the master even if it is not managed by us,
* and try to bring up the slave after the master becomes up. */
if (link->dsa_master_ifindex <= 0)
return 0;
if (!streq_ptr(link->driver, "dsa"))
return 0;
if (link_get_by_index(link->manager, link->dsa_master_ifindex, &master) < 0)
return 0;
if (master->state == LINK_STATE_UNMANAGED) {
/* If the DSA master interface is unmanaged, then it will never become up.
* Let's request to bring up the master. */
r = link_request_to_bring_up_or_down(master, /* up = */ true);
if (r < 0)
return r;
}
r = link_request_to_bring_up_or_down(link, /* up = */ true);
if (r < 0)
return r;
return 1;
}
static int link_up_or_down_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool up, bool on_activate) {
int r;
assert(m);
@ -958,10 +993,19 @@ static int link_up_or_down_handler_internal(sd_netlink *rtnl, sd_netlink_message
goto on_error;
r = sd_netlink_message_get_errno(m);
if (r < 0)
log_link_message_warning_errno(link, m, r, up ?
"Could not bring up interface, ignoring" :
"Could not bring down interface, ignoring");
if (r == -ENETDOWN && up && link_up_dsa_slave(link) > 0)
log_link_message_debug_errno(link, m, r, "Could not bring up dsa slave, retrying again after dsa master becomes up");
else if (r < 0) {
const char *error_msg;
error_msg = up ?
(on_activate ? "Could not bring up interface" : "Could not bring up interface, ignoring") :
(on_activate ? "Could not bring down interface" : "Could not bring down interface, ignoring");
log_link_message_warning_errno(link, m, r, error_msg);
if (on_activate)
goto on_error;
}
r = link_call_getlink(link, get_link_update_flag_handler);
if (r < 0) {
@ -969,7 +1013,7 @@ static int link_up_or_down_handler_internal(sd_netlink *rtnl, sd_netlink_message
goto on_error;
}
if (check_ready) {
if (on_activate) {
link->activated = true;
link_check_ready(link);
}
@ -984,19 +1028,19 @@ on_error:
}
static int link_activate_up_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
return link_up_or_down_handler_internal(rtnl, m, link, true, true);
return link_up_or_down_handler_internal(rtnl, m, link, /* up = */ true, /* on_activate = */ true);
}
static int link_activate_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
return link_up_or_down_handler_internal(rtnl, m, link, false, true);
return link_up_or_down_handler_internal(rtnl, m, link, /* up = */ false, /* on_activate = */ true);
}
static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
return link_up_or_down_handler_internal(rtnl, m, link, true, false);
return link_up_or_down_handler_internal(rtnl, m, link, /* up = */ true, /* on_activate = */ false);
}
static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
return link_up_or_down_handler_internal(rtnl, m, link, false, false);
return link_up_or_down_handler_internal(rtnl, m, link, /* up = */ false, /* on_activate = */ false);
}
static const char *up_or_down(bool up) {
@ -1123,9 +1167,21 @@ int link_request_to_activate(Link *link) {
return 0;
}
static bool link_is_ready_to_bring_up_or_down(Link *link) {
static bool link_is_ready_to_bring_up_or_down(Link *link, bool up) {
assert(link);
if (up && link->dsa_master_ifindex > 0) {
Link *master;
/* The master interface must be up. See comments in link_up_dsa_slave(). */
if (link_get_by_index(link->manager, link->dsa_master_ifindex, &master) < 0)
return false;
if (!FLAGS_SET(master->flags, IFF_UP))
return false;
}
if (link->state == LINK_STATE_UNMANAGED)
return true;
@ -1153,7 +1209,7 @@ int request_process_link_up_or_down(Request *req) {
link = req->link;
up = PTR_TO_INT(req->userdata);
if (!link_is_ready_to_bring_up_or_down(link))
if (!link_is_ready_to_bring_up_or_down(link, up))
return 0;
r = link_up_or_down(link, up, req->netlink_handler);

View File

@ -430,7 +430,7 @@ target2 = custom_target(
shared_generated_gperf_headers = [target1, target2]
shared_sources += shared_generated_gperf_headers
libshared_name = 'systemd-shared-@0@'.format(meson.project_version())
libshared_name = 'systemd-shared-@0@'.format(shared_lib_tag)
libshared_deps = [threads,
libacl,
@ -465,13 +465,13 @@ libshared_static = static_library(
libshared = shared_library(
libshared_name,
include_directories : includes,
c_args : ['-fvisibility=default'],
link_args : ['-shared',
'-Wl,--version-script=' + libshared_sym_path],
link_whole : [libshared_static,
libbasic,
libbasic_gcrypt,
libsystemd_static],
c_args : ['-fvisibility=default'],
dependencies : libshared_deps,
install : true,
install_dir : rootlibexecdir)

View File

@ -419,7 +419,8 @@ tests += [
libmount,
libxz,
liblz4,
libblkid],
libblkid,
libselinux],
[core_includes, journal_includes, udev_includes]],
[['src/test/test-prioq.c']],

View File

@ -11,34 +11,86 @@ TEST(strpcpy) {
char target[25];
char *s = target;
size_t space_left;
bool truncated;
space_left = sizeof(target);
space_left = strpcpy(&s, space_left, "12345");
space_left = strpcpy(&s, space_left, "hey hey hey");
space_left = strpcpy(&s, space_left, "waldo");
space_left = strpcpy(&s, space_left, "ba");
space_left = strpcpy(&s, space_left, "r");
space_left = strpcpy(&s, space_left, "foo");
space_left = strpcpy_full(&s, space_left, "12345", &truncated);
assert_se(!truncated);
space_left = strpcpy_full(&s, space_left, "hey hey hey", &truncated);
assert_se(!truncated);
space_left = strpcpy_full(&s, space_left, "waldo", &truncated);
assert_se(!truncated);
space_left = strpcpy_full(&s, space_left, "ba", &truncated);
assert_se(!truncated);
space_left = strpcpy_full(&s, space_left, "r", &truncated);
assert_se(!truncated);
assert_se(space_left == 1);
assert_se(streq(target, "12345hey hey heywaldobar"));
space_left = strpcpy_full(&s, space_left, "", &truncated);
assert_se(!truncated);
assert_se(space_left == 1);
assert_se(streq(target, "12345hey hey heywaldobar"));
space_left = strpcpy_full(&s, space_left, "f", &truncated);
assert_se(truncated);
assert_se(space_left == 0);
assert_se(streq(target, "12345hey hey heywaldobar"));
space_left = strpcpy_full(&s, space_left, "", &truncated);
assert_se(!truncated);
assert_se(space_left == 0);
assert_se(streq(target, "12345hey hey heywaldobar"));
space_left = strpcpy_full(&s, space_left, "foo", &truncated);
assert_se(truncated);
assert_se(space_left == 0);
assert_se(streq(target, "12345hey hey heywaldobar"));
}
TEST(strpcpyf) {
char target[25];
char *s = target;
size_t space_left;
bool truncated;
space_left = sizeof(target);
space_left = strpcpyf(&s, space_left, "space left: %zu. ", space_left);
space_left = strpcpyf(&s, space_left, "foo%s", "bar");
assert_se(streq(target, "space left: 25. foobar"));
space_left = strpcpyf_full(&s, space_left, &truncated, "space left: %zu. ", space_left);
assert_se(!truncated);
space_left = strpcpyf_full(&s, space_left, &truncated, "foo%s", "bar");
assert_se(!truncated);
assert_se(space_left == 3);
assert_se(streq(target, "space left: 25. foobar"));
space_left = strpcpyf_full(&s, space_left, &truncated, "%i", 42);
assert_se(!truncated);
assert_se(space_left == 1);
assert_se(streq(target, "space left: 25. foobar42"));
space_left = strpcpyf_full(&s, space_left, &truncated, "%s", "");
assert_se(!truncated);
assert_se(space_left == 1);
assert_se(streq(target, "space left: 25. foobar42"));
space_left = strpcpyf_full(&s, space_left, &truncated, "%c", 'x');
assert_se(truncated);
assert_se(space_left == 0);
assert_se(streq(target, "space left: 25. foobar42"));
space_left = strpcpyf_full(&s, space_left, &truncated, "%s", "");
assert_se(!truncated);
assert_se(space_left == 0);
assert_se(streq(target, "space left: 25. foobar42"));
space_left = strpcpyf_full(&s, space_left, &truncated, "abc%s", "hoge");
assert_se(truncated);
assert_se(space_left == 0);
assert_se(streq(target, "space left: 25. foobar42"));
/* test overflow */
s = target;
space_left = strpcpyf(&s, 12, "00 left: %i. ", 999);
space_left = strpcpyf_full(&s, 12, &truncated, "00 left: %i. ", 999);
assert_se(truncated);
assert_se(streq(target, "00 left: 99"));
assert_se(space_left == 0);
assert_se(target[12] == '2');
@ -48,21 +100,40 @@ TEST(strpcpyl) {
char target[25];
char *s = target;
size_t space_left;
bool truncated;
space_left = sizeof(target);
space_left = strpcpyl(&s, space_left, "waldo", " test", " waldo. ", NULL);
space_left = strpcpyl(&s, space_left, "Banana", NULL);
assert_se(streq(target, "waldo test waldo. Banana"));
space_left = strpcpyl_full(&s, space_left, &truncated, "waldo", " test", " waldo. ", NULL);
assert_se(!truncated);
space_left = strpcpyl_full(&s, space_left, &truncated, "Banana", NULL);
assert_se(!truncated);
assert_se(space_left == 1);
assert_se(streq(target, "waldo test waldo. Banana"));
space_left = strpcpyl_full(&s, space_left, &truncated, "", "", "", NULL);
assert_se(!truncated);
assert_se(space_left == 1);
assert_se(streq(target, "waldo test waldo. Banana"));
space_left = strpcpyl_full(&s, space_left, &truncated, "", "x", "", NULL);
assert_se(truncated);
assert_se(space_left == 0);
assert_se(streq(target, "waldo test waldo. Banana"));
space_left = strpcpyl_full(&s, space_left, &truncated, "hoge", NULL);
assert_se(truncated);
assert_se(space_left == 0);
assert_se(streq(target, "waldo test waldo. Banana"));
}
TEST(strscpy) {
char target[25];
size_t space_left;
bool truncated;
space_left = sizeof(target);
space_left = strscpy(target, space_left, "12345");
space_left = strscpy_full(target, space_left, "12345", &truncated);
assert_se(!truncated);
assert_se(streq(target, "12345"));
assert_se(space_left == 20);
@ -71,9 +142,11 @@ TEST(strscpy) {
TEST(strscpyl) {
char target[25];
size_t space_left;
bool truncated;
space_left = sizeof(target);
space_left = strscpyl(target, space_left, "12345", "waldo", "waldo", NULL);
space_left = strscpyl_full(target, space_left, &truncated, "12345", "waldo", "waldo", NULL);
assert_se(!truncated);
assert_se(streq(target, "12345waldowaldo"));
assert_se(space_left == 10);

View File

@ -9,7 +9,7 @@
#define BUF_SIZE 1024
static void test_event_spawn_core(bool with_pidfd, const char *cmd, char result_buf[BUF_SIZE]) {
static void test_event_spawn_core(bool with_pidfd, const char *cmd, char *result_buf, size_t buf_size) {
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
_cleanup_(udev_event_freep) UdevEvent *event = NULL;
@ -17,12 +17,12 @@ static void test_event_spawn_core(bool with_pidfd, const char *cmd, char result_
assert_se(sd_device_new_from_syspath(&dev, "/sys/class/net/lo") >= 0);
assert_se(event = udev_event_new(dev, 0, NULL, LOG_DEBUG));
assert_se(udev_event_spawn(event, 5 * USEC_PER_SEC, SIGKILL, false, cmd, result_buf, BUF_SIZE) == 0);
assert_se(udev_event_spawn(event, 5 * USEC_PER_SEC, SIGKILL, false, cmd, result_buf, buf_size, NULL) == 0);
assert_se(unsetenv("SYSTEMD_PIDFD") >= 0);
}
static void test_event_spawn_cat(bool with_pidfd) {
static void test_event_spawn_cat(bool with_pidfd, size_t buf_size) {
_cleanup_strv_free_ char **lines = NULL;
_cleanup_free_ char *cmd = NULL;
char result_buf[BUF_SIZE];
@ -32,13 +32,16 @@ static void test_event_spawn_cat(bool with_pidfd) {
assert_se(find_executable("cat", &cmd) >= 0);
assert_se(strextend_with_separator(&cmd, " ", "/sys/class/net/lo/uevent"));
test_event_spawn_core(with_pidfd, cmd, result_buf);
test_event_spawn_core(with_pidfd, cmd, result_buf,
buf_size >= BUF_SIZE ? BUF_SIZE : buf_size);
assert_se(lines = strv_split_newlines(result_buf));
strv_print(lines);
assert_se(strv_contains(lines, "INTERFACE=lo"));
assert_se(strv_contains(lines, "IFINDEX=1"));
if (buf_size >= BUF_SIZE) {
assert_se(strv_contains(lines, "INTERFACE=lo"));
assert_se(strv_contains(lines, "IFINDEX=1"));
}
}
static void test_event_spawn_self(const char *self, const char *arg, bool with_pidfd) {
@ -50,7 +53,7 @@ static void test_event_spawn_self(const char *self, const char *arg, bool with_p
assert_se(cmd = strjoin(self, " ", arg));
test_event_spawn_core(with_pidfd, cmd, result_buf);
test_event_spawn_core(with_pidfd, cmd, result_buf, BUF_SIZE);
assert_se(lines = strv_split_newlines(result_buf));
strv_print(lines);
@ -92,8 +95,10 @@ int main(int argc, char *argv[]) {
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
test_event_spawn_cat(true);
test_event_spawn_cat(false);
test_event_spawn_cat(true, SIZE_MAX);
test_event_spawn_cat(false, SIZE_MAX);
test_event_spawn_cat(true, 5);
test_event_spawn_cat(false, 5);
assert_se(path_make_absolute_cwd(argv[0], &self) >= 0);
path_simplify(self);

View File

@ -12,6 +12,7 @@
#include "alloc-util.h"
#include "errno-util.h"
#include "event-util.h"
#include "fd-util.h"
#include "format-util.h"
#include "io-util.h"
@ -105,6 +106,13 @@ static void udev_ctrl_disconnect(UdevCtrl *uctrl) {
uctrl->sock_connect = safe_close(uctrl->sock_connect);
}
int udev_ctrl_is_connected(UdevCtrl *uctrl) {
if (!uctrl)
return false;
return event_source_is_enabled(uctrl->event_source_connect);
}
static UdevCtrl *udev_ctrl_free(UdevCtrl *uctrl) {
assert(uctrl);
@ -291,7 +299,7 @@ int udev_ctrl_start(UdevCtrl *uctrl, udev_ctrl_handler_t callback, void *userdat
return 0;
}
int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, int intval, const char *buf) {
int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, const void *data) {
UdevCtrlMessageWire ctrl_msg_wire = {
.version = "udev-" STRINGIFY(PROJECT_VERSION),
.magic = UDEV_CTRL_MAGIC,
@ -301,10 +309,13 @@ int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, int intval, const
if (uctrl->maybe_disconnected)
return -ENOANO; /* to distinguish this from other errors. */
if (buf)
strscpy(ctrl_msg_wire.value.buf, sizeof(ctrl_msg_wire.value.buf), buf);
else
ctrl_msg_wire.value.intval = intval;
if (type == UDEV_CTRL_SET_ENV) {
assert(data);
strscpy(ctrl_msg_wire.value.buf, sizeof(ctrl_msg_wire.value.buf), data);
} else if (IN_SET(type, UDEV_CTRL_SET_LOG_LEVEL, UDEV_CTRL_SET_CHILDREN_MAX))
ctrl_msg_wire.value.intval = PTR_TO_INT(data);
else if (type == UDEV_CTRL_SENDER_PID)
ctrl_msg_wire.value.pid = PTR_TO_PID(data);
if (!uctrl->connected) {
if (connect(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0)
@ -333,7 +344,7 @@ int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout) {
return 0;
if (!uctrl->maybe_disconnected) {
r = udev_ctrl_send(uctrl, _UDEV_CTRL_END_MESSAGES, 0, NULL);
r = udev_ctrl_send(uctrl, _UDEV_CTRL_END_MESSAGES, NULL);
if (r < 0)
return r;
}

View File

@ -4,6 +4,7 @@
#include "sd-event.h"
#include "macro.h"
#include "process-util.h"
#include "time-util.h"
typedef struct UdevCtrl UdevCtrl;
@ -18,10 +19,12 @@ typedef enum UdevCtrlMessageType {
UDEV_CTRL_SET_CHILDREN_MAX,
UDEV_CTRL_PING,
UDEV_CTRL_EXIT,
UDEV_CTRL_SENDER_PID,
} UdevCtrlMessageType;
typedef union UdevCtrlMessageValue {
int intval;
pid_t pid;
char buf[256];
} UdevCtrlMessageValue;
@ -39,40 +42,45 @@ UdevCtrl *udev_ctrl_unref(UdevCtrl *uctrl);
int udev_ctrl_attach_event(UdevCtrl *uctrl, sd_event *event);
int udev_ctrl_start(UdevCtrl *uctrl, udev_ctrl_handler_t callback, void *userdata);
sd_event_source *udev_ctrl_get_event_source(UdevCtrl *uctrl);
int udev_ctrl_is_connected(UdevCtrl *uctrl);
int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout);
int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, int intval, const char *buf);
int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, const void *data);
static inline int udev_ctrl_send_set_log_level(UdevCtrl *uctrl, int priority) {
return udev_ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL);
return udev_ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, INT_TO_PTR(priority));
}
static inline int udev_ctrl_send_stop_exec_queue(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL);
return udev_ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, NULL);
}
static inline int udev_ctrl_send_start_exec_queue(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL);
return udev_ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, NULL);
}
static inline int udev_ctrl_send_reload(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL);
return udev_ctrl_send(uctrl, UDEV_CTRL_RELOAD, NULL);
}
static inline int udev_ctrl_send_set_env(UdevCtrl *uctrl, const char *key) {
return udev_ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key);
return udev_ctrl_send(uctrl, UDEV_CTRL_SET_ENV, key);
}
static inline int udev_ctrl_send_set_children_max(UdevCtrl *uctrl, int count) {
return udev_ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL);
return udev_ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, INT_TO_PTR(count));
}
static inline int udev_ctrl_send_ping(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL);
return udev_ctrl_send(uctrl, UDEV_CTRL_PING, NULL);
}
static inline int udev_ctrl_send_exit(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL);
return udev_ctrl_send(uctrl, UDEV_CTRL_EXIT, NULL);
}
static inline int udev_ctrl_send_pid(UdevCtrl *uctrl) {
return udev_ctrl_send(uctrl, UDEV_CTRL_SENDER_PID, PID_TO_PTR(getpid_cached()));
}
DEFINE_TRIVIAL_CLEANUP_FUNC(UdevCtrl*, udev_ctrl_unref);

View File

@ -50,6 +50,7 @@ typedef struct Spawn {
char *result;
size_t result_size;
size_t result_len;
bool truncated;
} Spawn;
UdevEvent *udev_event_new(sd_device *dev, usec_t exec_delay_usec, sd_netlink *rtnl, int log_level) {
@ -237,9 +238,12 @@ static ssize_t udev_event_subst_format(
FormatSubstitutionType type,
const char *attr,
char *dest,
size_t l) {
size_t l,
bool *ret_truncated) {
sd_device *parent, *dev = event->dev;
const char *val = NULL;
bool truncated = false;
char *s = dest;
int r;
@ -248,13 +252,13 @@ static ssize_t udev_event_subst_format(
r = sd_device_get_devpath(dev, &val);
if (r < 0)
return r;
strpcpy(&s, l, val);
strpcpy_full(&s, l, val, &truncated);
break;
case FORMAT_SUBST_KERNEL:
r = sd_device_get_sysname(dev, &val);
if (r < 0)
return r;
strpcpy(&s, l, val);
strpcpy_full(&s, l, val, &truncated);
break;
case FORMAT_SUBST_KERNEL_NUMBER:
r = sd_device_get_sysnum(dev, &val);
@ -262,7 +266,7 @@ static ssize_t udev_event_subst_format(
goto null_terminate;
if (r < 0)
return r;
strpcpy(&s, l, val);
strpcpy_full(&s, l, val, &truncated);
break;
case FORMAT_SUBST_ID:
if (!event->dev_parent)
@ -270,7 +274,7 @@ static ssize_t udev_event_subst_format(
r = sd_device_get_sysname(event->dev_parent, &val);
if (r < 0)
return r;
strpcpy(&s, l, val);
strpcpy_full(&s, l, val, &truncated);
break;
case FORMAT_SUBST_DRIVER:
if (!event->dev_parent)
@ -280,7 +284,7 @@ static ssize_t udev_event_subst_format(
goto null_terminate;
if (r < 0)
return r;
strpcpy(&s, l, val);
strpcpy_full(&s, l, val, &truncated);
break;
case FORMAT_SUBST_MAJOR:
case FORMAT_SUBST_MINOR: {
@ -289,7 +293,7 @@ static ssize_t udev_event_subst_format(
r = sd_device_get_devnum(dev, &devnum);
if (r < 0 && r != -ENOENT)
return r;
strpcpyf(&s, l, "%u", r < 0 ? 0 : type == FORMAT_SUBST_MAJOR ? major(devnum) : minor(devnum));
strpcpyf_full(&s, l, &truncated, "%u", r < 0 ? 0 : type == FORMAT_SUBST_MAJOR ? major(devnum) : minor(devnum));
break;
}
case FORMAT_SUBST_RESULT: {
@ -308,7 +312,7 @@ static ssize_t udev_event_subst_format(
}
if (index == 0)
strpcpy(&s, l, event->program_result);
strpcpy_full(&s, l, event->program_result, &truncated);
else {
const char *start, *p;
unsigned i;
@ -330,11 +334,11 @@ static ssize_t udev_event_subst_format(
start = p;
/* %c{2+} copies the whole string from the second part on */
if (has_plus)
strpcpy(&s, l, start);
strpcpy_full(&s, l, start, &truncated);
else {
while (*p && !strchr(WHITESPACE, *p))
p++;
strnpcpy(&s, l, start, p - start);
strnpcpy_full(&s, l, start, p - start, &truncated);
}
}
break;
@ -342,6 +346,7 @@ static ssize_t udev_event_subst_format(
case FORMAT_SUBST_ATTR: {
char vbuf[UDEV_NAME_SIZE];
int count;
bool t;
if (isempty(attr))
return -EINVAL;
@ -363,12 +368,13 @@ static ssize_t udev_event_subst_format(
/* strip trailing whitespace, and replace unwanted characters */
if (val != vbuf)
strscpy(vbuf, sizeof(vbuf), val);
strscpy_full(vbuf, sizeof(vbuf), val, &truncated);
delete_trailing_chars(vbuf, NULL);
count = udev_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT);
if (count > 0)
log_device_debug(dev, "%i character(s) replaced", count);
strpcpy(&s, l, vbuf);
strpcpy_full(&s, l, vbuf, &t);
truncated = truncated || t;
break;
}
case FORMAT_SUBST_PARENT:
@ -382,7 +388,7 @@ static ssize_t udev_event_subst_format(
goto null_terminate;
if (r < 0)
return r;
strpcpy(&s, l, val + STRLEN("/dev/"));
strpcpy_full(&s, l, val + STRLEN("/dev/"), &truncated);
break;
case FORMAT_SUBST_DEVNODE:
r = sd_device_get_devname(dev, &val);
@ -390,34 +396,37 @@ static ssize_t udev_event_subst_format(
goto null_terminate;
if (r < 0)
return r;
strpcpy(&s, l, val);
strpcpy_full(&s, l, val, &truncated);
break;
case FORMAT_SUBST_NAME:
if (event->name)
strpcpy(&s, l, event->name);
strpcpy_full(&s, l, event->name, &truncated);
else if (sd_device_get_devname(dev, &val) >= 0)
strpcpy(&s, l, val + STRLEN("/dev/"));
strpcpy_full(&s, l, val + STRLEN("/dev/"), &truncated);
else {
r = sd_device_get_sysname(dev, &val);
if (r < 0)
return r;
strpcpy(&s, l, val);
strpcpy_full(&s, l, val, &truncated);
}
break;
case FORMAT_SUBST_LINKS:
FOREACH_DEVICE_DEVLINK(dev, val)
FOREACH_DEVICE_DEVLINK(dev, val) {
if (s == dest)
strpcpy(&s, l, val + STRLEN("/dev/"));
strpcpy_full(&s, l, val + STRLEN("/dev/"), &truncated);
else
strpcpyl(&s, l, " ", val + STRLEN("/dev/"), NULL);
strpcpyl_full(&s, l, &truncated, " ", val + STRLEN("/dev/"), NULL);
if (truncated)
break;
}
if (s == dest)
goto null_terminate;
break;
case FORMAT_SUBST_ROOT:
strpcpy(&s, l, "/dev");
strpcpy_full(&s, l, "/dev", &truncated);
break;
case FORMAT_SUBST_SYS:
strpcpy(&s, l, "/sys");
strpcpy_full(&s, l, "/sys", &truncated);
break;
case FORMAT_SUBST_ENV:
if (isempty(attr))
@ -427,22 +436,34 @@ static ssize_t udev_event_subst_format(
goto null_terminate;
if (r < 0)
return r;
strpcpy(&s, l, val);
strpcpy_full(&s, l, val, &truncated);
break;
default:
assert_not_reached();
}
if (ret_truncated)
*ret_truncated = truncated;
return s - dest;
null_terminate:
if (ret_truncated)
*ret_truncated = truncated;
*s = '\0';
return 0;
}
size_t udev_event_apply_format(UdevEvent *event,
const char *src, char *dest, size_t size,
bool replace_whitespace) {
size_t udev_event_apply_format(
UdevEvent *event,
const char *src,
char *dest,
size_t size,
bool replace_whitespace,
bool *ret_truncated) {
bool truncated = false;
const char *s = src;
int r;
@ -456,20 +477,24 @@ size_t udev_event_apply_format(UdevEvent *event,
FormatSubstitutionType type;
char attr[UDEV_PATH_SIZE];
ssize_t subst_len;
bool t;
r = get_subst_type(&s, false, &type, attr);
if (r < 0) {
log_device_warning_errno(event->dev, r, "Invalid format string, ignoring: %s", src);
break;
} else if (r == 0) {
if (size < 2) /* need space for this char and the terminating NUL */
if (size < 2) {
/* need space for this char and the terminating NUL */
truncated = true;
break;
}
*dest++ = *s++;
size--;
continue;
}
subst_len = udev_event_subst_format(event, type, attr, dest, size);
subst_len = udev_event_subst_format(event, type, attr, dest, size, &t);
if (subst_len < 0) {
log_device_warning_errno(event->dev, subst_len,
"Failed to substitute variable '$%s' or apply format '%%%c', ignoring: %m",
@ -477,6 +502,8 @@ size_t udev_event_apply_format(UdevEvent *event,
break;
}
truncated = truncated || t;
/* FORMAT_SUBST_RESULT handles spaces itself */
if (replace_whitespace && type != FORMAT_SUBST_RESULT)
/* udev_replace_whitespace can replace in-place,
@ -488,6 +515,10 @@ size_t udev_event_apply_format(UdevEvent *event,
}
assert(size >= 1);
if (ret_truncated)
*ret_truncated = truncated;
*dest = '\0';
return size;
}
@ -555,7 +586,7 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd
size = sizeof(buf);
}
l = read(fd, p, size - 1);
l = read(fd, p, size - (p == buf));
if (l < 0) {
if (errno == EAGAIN)
goto reenable;
@ -566,6 +597,13 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd
return 0;
}
if ((size_t) l == size) {
log_device_warning(spawn->device, "Truncating stdout of '%s' up to %zu byte.",
spawn->cmd, spawn->result_size);
l--;
spawn->truncated = true;
}
p[l] = '\0';
if (fd == spawn->fd_stdout && spawn->result)
spawn->result_len += l;
@ -586,7 +624,7 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd
fd == spawn->fd_stdout ? "out" : "err", *q);
}
if (l == 0)
if (l == 0 || spawn->truncated)
return 0;
reenable:
@ -725,12 +763,16 @@ static int spawn_wait(Spawn *spawn) {
return sd_event_loop(e);
}
int udev_event_spawn(UdevEvent *event,
usec_t timeout_usec,
int timeout_signal,
bool accept_failure,
const char *cmd,
char *result, size_t ressize) {
int udev_event_spawn(
UdevEvent *event,
usec_t timeout_usec,
int timeout_signal,
bool accept_failure,
const char *cmd,
char *result,
size_t ressize,
bool *ret_truncated) {
_cleanup_close_pair_ int outpipe[2] = {-1, -1}, errpipe[2] = {-1, -1};
_cleanup_strv_free_ char **argv = NULL;
char **envp = NULL;
@ -821,6 +863,9 @@ int udev_event_spawn(UdevEvent *event,
if (result)
result[spawn.result_len] = '\0';
if (ret_truncated)
*ret_truncated = spawn.truncated;
return r; /* 0 for success, and positive if the program failed */
}
@ -1095,7 +1140,7 @@ void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_s
log_device_debug(event->dev, "Running command \"%s\"", command);
r = udev_event_spawn(event, timeout_usec, timeout_signal, false, command, NULL, 0);
r = udev_event_spawn(event, timeout_usec, timeout_signal, false, command, NULL, 0, NULL);
if (r < 0)
log_device_warning_errno(event->dev, r, "Failed to execute '%s', ignoring: %m", command);
else if (r > 0) /* returned value is positive when program fails */

View File

@ -56,7 +56,8 @@ size_t udev_event_apply_format(
const char *src,
char *dest,
size_t size,
bool replace_whitespace);
bool replace_whitespace,
bool *ret_truncated);
int udev_check_format(const char *value, size_t *offset, const char **hint);
int udev_event_spawn(
UdevEvent *event,
@ -65,7 +66,8 @@ int udev_event_spawn(
bool accept_failure,
const char *cmd,
char *result,
size_t ressize);
size_t ressize,
bool *ret_truncated);
int udev_event_execute_rules(
UdevEvent *event,
int inotify_fd,

View File

@ -1381,11 +1381,14 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
return token->op == (match ? OP_MATCH : OP_NOMATCH);
}
static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *event) {
static bool token_match_attr(UdevRules *rules, UdevRuleToken *token, sd_device *dev, UdevEvent *event) {
char nbuf[UDEV_NAME_SIZE], vbuf[UDEV_NAME_SIZE];
const char *name, *value;
bool truncated;
assert(rules);
assert(token);
assert(IN_SET(token->type, TK_M_ATTR, TK_M_PARENTS_ATTR));
assert(dev);
assert(event);
@ -1393,7 +1396,15 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev
switch (token->attr_subst_type) {
case SUBST_TYPE_FORMAT:
(void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false);
(void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false, &truncated);
if (truncated) {
log_rule_debug(dev, rules,
"The sysfs attribute name '%s' is truncated while substituting into '%s', "
"assuming the %s key does not match.", nbuf, name,
token->type == TK_M_ATTR ? "ATTR" : "ATTRS");
return false;
}
name = nbuf;
_fallthrough_;
case SUBST_TYPE_PLAIN:
@ -1497,19 +1508,22 @@ static int attr_subst_subdir(char attr[static UDEV_PATH_SIZE]) {
char buf[UDEV_PATH_SIZE], *p;
const char *tail;
size_t len, size;
bool truncated;
assert(attr);
tail = strstr(attr, "/*/");
if (!tail)
return 0;
return 0;
len = tail - attr + 1; /* include slash at the end */
tail += 2; /* include slash at the beginning */
p = buf;
size = sizeof(buf);
size -= strnpcpy(&p, size, attr, len);
size -= strnpcpy_full(&p, size, attr, len, &truncated);
if (truncated)
return -ENOENT;
dir = opendir(buf);
if (!dir)
@ -1519,7 +1533,10 @@ static int attr_subst_subdir(char attr[static UDEV_PATH_SIZE]) {
if (de->d_name[0] == '.')
continue;
strscpyl(p, size, de->d_name, tail, NULL);
strscpyl_full(p, size, &truncated, de->d_name, tail, NULL);
if (truncated)
continue;
if (faccessat(dirfd(dir), p, F_OK, 0) < 0)
continue;
@ -1645,12 +1662,19 @@ static int udev_rule_apply_token_to_event(
}
case TK_M_ATTR:
case TK_M_PARENTS_ATTR:
return token_match_attr(token, dev, event);
return token_match_attr(rules, token, dev, event);
case TK_M_SYSCTL: {
_cleanup_free_ char *value = NULL;
char buf[UDEV_PATH_SIZE];
bool truncated;
(void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_rule_debug(dev, rules, "The sysctl entry name '%s' is truncated while substituting into '%s', "
"assuming the SYSCTL key does not match.", buf, (const char*) token->data);
return false;
}
(void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false);
r = sysctl_read(sysctl_normalize(buf), &value);
if (r < 0 && r != -ENOENT)
return log_rule_error_errno(dev, rules, r, "Failed to read sysctl '%s': %m", buf);
@ -1661,9 +1685,15 @@ static int udev_rule_apply_token_to_event(
mode_t mode = PTR_TO_MODE(token->data);
char buf[UDEV_PATH_SIZE];
struct stat statbuf;
bool match;
bool match, truncated;
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_rule_debug(dev, rules, "The file name '%s' is truncated while substituting into '%s', "
"assuming the TEST key does not match", buf, token->value);
return false;
}
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
if (!path_is_absolute(buf) &&
udev_resolve_subsys_kernel(buf, buf, sizeof(buf), false) < 0) {
char tmp[UDEV_PATH_SIZE];
@ -1673,8 +1703,11 @@ static int udev_rule_apply_token_to_event(
if (r < 0)
return log_rule_error_errno(dev, rules, r, "Failed to get syspath: %m");
strscpy(tmp, sizeof(tmp), buf);
strscpyl(buf, sizeof(buf), val, "/", tmp, NULL);
strscpy_full(tmp, sizeof(tmp), buf, &truncated);
assert(!truncated);
strscpyl_full(buf, sizeof(buf), &truncated, val, "/", tmp, NULL);
if (truncated)
return false;
}
r = attr_subst_subdir(buf);
@ -1694,13 +1727,20 @@ static int udev_rule_apply_token_to_event(
}
case TK_M_PROGRAM: {
char buf[UDEV_PATH_SIZE], result[UDEV_LINE_SIZE];
bool truncated;
size_t count;
event->program_result = mfree(event->program_result);
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_rule_debug(dev, rules, "The command '%s' is truncated while substituting into '%s', "
"assuming the PROGRAM key does not match.", buf, token->value);
return false;
}
log_rule_debug(dev, rules, "Running PROGRAM '%s'", buf);
r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof(result));
r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof(result), NULL);
if (r != 0) {
if (r < 0)
log_rule_warning_errno(dev, rules, r, "Failed to execute \"%s\": %m", buf);
@ -1721,8 +1761,15 @@ static int udev_rule_apply_token_to_event(
case TK_M_IMPORT_FILE: {
_cleanup_fclose_ FILE *f = NULL;
char buf[UDEV_PATH_SIZE];
bool truncated;
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_rule_debug(dev, rules, "The file name '%s' to be imported is truncated while substituting into '%s', "
"assuming the IMPORT key does not match.", buf, token->value);
return false;
}
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
log_rule_debug(dev, rules, "Importing properties from '%s'", buf);
f = fopen(buf, "re");
@ -1768,11 +1815,18 @@ static int udev_rule_apply_token_to_event(
case TK_M_IMPORT_PROGRAM: {
_cleanup_strv_free_ char **lines = NULL;
char buf[UDEV_PATH_SIZE], result[UDEV_LINE_SIZE], **line;
bool truncated;
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_rule_debug(dev, rules, "The command '%s' is truncated while substituting into '%s', "
"assuming the IMPORT key does not match.", buf, token->value);
return false;
}
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
log_rule_debug(dev, rules, "Importing properties from results of '%s'", buf);
r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof result);
r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof result, &truncated);
if (r != 0) {
if (r < 0)
log_rule_warning_errno(dev, rules, r, "Failed to execute '%s', ignoring: %m", buf);
@ -1781,10 +1835,26 @@ static int udev_rule_apply_token_to_event(
return token->op == OP_NOMATCH;
}
if (truncated) {
bool found = false;
/* Drop the last line. */
for (char *p = buf + strlen(buf) - 1; p >= buf; p--)
if (strchr(NEWLINE, *p)) {
*p = '\0';
found = true;
} else if (found)
break;
}
r = strv_split_newlines_full(&lines, result, EXTRACT_RETAIN_ESCAPE);
if (r < 0)
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_rule_warning_errno(dev, rules, r,
"Failed to extract lines from result of command \"%s\", ignoring: %m", buf);
return false;
}
STRV_FOREACH(line, lines) {
char *key, *value;
@ -1813,6 +1883,7 @@ static int udev_rule_apply_token_to_event(
assert(cmd >= 0 && cmd < _UDEV_BUILTIN_MAX);
unsigned mask = 1U << (int) cmd;
char buf[UDEV_PATH_SIZE];
bool truncated;
if (udev_builtin_run_once(cmd)) {
/* check if we ran already */
@ -1826,7 +1897,13 @@ static int udev_rule_apply_token_to_event(
event->builtin_run |= mask;
}
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_rule_debug(dev, rules, "The builtin command '%s' is truncated while substituting into '%s', "
"assuming the IMPORT key does not match", buf, token->value);
return false;
}
log_rule_debug(dev, rules, "Importing properties from results of builtin command '%s'", buf);
r = udev_builtin_run(dev, &event->rtnl, cmd, buf, false);
@ -1875,8 +1952,15 @@ static int udev_rule_apply_token_to_event(
}
case TK_M_IMPORT_PARENT: {
char buf[UDEV_PATH_SIZE];
bool truncated;
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_rule_debug(dev, rules, "The property name '%s' is truncated while substituting into '%s', "
"assuming the IMPORT key does not match.", buf, token->value);
return false;
}
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
r = import_parent_into_properties(dev, buf);
if (r < 0)
return log_rule_error_errno(dev, rules, r,
@ -1925,13 +2009,20 @@ static int udev_rule_apply_token_to_event(
case TK_A_OWNER: {
char owner[UDEV_NAME_SIZE];
const char *ow = owner;
bool truncated;
if (event->owner_final)
break;
if (token->op == OP_ASSIGN_FINAL)
event->owner_final = true;
(void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false);
(void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false, &truncated);
if (truncated) {
log_rule_warning(dev, rules, "The user name '%s' is truncated while substituting into '%s', "
"refusing to apply the OWNER key.", owner, token->value);
break;
}
r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
if (r < 0)
log_unknown_owner(dev, rules, r, "user", owner);
@ -1942,13 +2033,20 @@ static int udev_rule_apply_token_to_event(
case TK_A_GROUP: {
char group[UDEV_NAME_SIZE];
const char *gr = group;
bool truncated;
if (event->group_final)
break;
if (token->op == OP_ASSIGN_FINAL)
event->group_final = true;
(void) udev_event_apply_format(event, token->value, group, sizeof(group), false);
(void) udev_event_apply_format(event, token->value, group, sizeof(group), false, &truncated);
if (truncated) {
log_rule_warning(dev, rules, "The group name '%s' is truncated while substituting into '%s', "
"refusing to apply the GROUP key.", group, token->value);
break;
}
r = get_group_creds(&gr, &event->gid, USER_CREDS_ALLOW_MISSING);
if (r < 0)
log_unknown_owner(dev, rules, r, "group", group);
@ -1958,13 +2056,20 @@ static int udev_rule_apply_token_to_event(
}
case TK_A_MODE: {
char mode_str[UDEV_NAME_SIZE];
bool truncated;
if (event->mode_final)
break;
if (token->op == OP_ASSIGN_FINAL)
event->mode_final = true;
(void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false);
(void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false, &truncated);
if (truncated) {
log_rule_warning(dev, rules, "The mode '%s' is truncated while substituting into %s, "
"refusing to apply the MODE key.", mode_str, token->value);
break;
}
r = parse_mode(mode_str, &event->mode);
if (r < 0)
log_rule_error_errno(dev, rules, r, "Failed to parse mode '%s', ignoring: %m", mode_str);
@ -2005,12 +2110,19 @@ static int udev_rule_apply_token_to_event(
case TK_A_SECLABEL: {
_cleanup_free_ char *name = NULL, *label = NULL;
char label_str[UDEV_LINE_SIZE] = {};
bool truncated;
name = strdup(token->data);
if (!name)
return log_oom();
(void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false);
(void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false, &truncated);
if (truncated) {
log_rule_warning(dev, rules, "The security label '%s' is truncated while substituting into '%s', "
"refusing to apply the SECLABEL key.", label_str, token->value);
break;
}
if (!isempty(label_str))
label = strdup(label_str);
else
@ -2037,6 +2149,7 @@ static int udev_rule_apply_token_to_event(
const char *val, *name = token->data;
char value_new[UDEV_NAME_SIZE], *p = value_new;
size_t count, l = sizeof(value_new);
bool truncated;
if (isempty(token->value)) {
if (token->op == OP_ADD)
@ -2048,10 +2161,22 @@ static int udev_rule_apply_token_to_event(
}
if (token->op == OP_ADD &&
sd_device_get_property_value(dev, name, &val) >= 0)
l = strpcpyl(&p, l, val, " ", NULL);
sd_device_get_property_value(dev, name, &val) >= 0) {
l = strpcpyl_full(&p, l, &truncated, val, " ", NULL);
if (truncated) {
log_rule_warning(dev, rules, "The buffer for the property '%s' is full, "
"refusing to append the new value '%s'.", name, token->value);
break;
}
}
(void) udev_event_apply_format(event, token->value, p, l, false, &truncated);
if (truncated) {
log_rule_warning(dev, rules, "The property value '%s' is truncated while substituting into '%s', "
"refusing to add property '%s'.", p, token->value, name);
break;
}
(void) udev_event_apply_format(event, token->value, p, l, false);
if (event->esc == ESCAPE_REPLACE) {
count = udev_replace_chars(p, NULL);
if (count > 0)
@ -2066,8 +2191,16 @@ static int udev_rule_apply_token_to_event(
}
case TK_A_TAG: {
char buf[UDEV_PATH_SIZE];
bool truncated;
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_rule_warning(dev, rules, "The tag name '%s' is truncated while substituting into '%s',"
"refusing to %s the tag.", buf, token->value,
token->op == OP_REMOVE ? "remove" : "add");
break;
}
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
if (token->op == OP_ASSIGN)
device_cleanup_tags(dev);
@ -2086,6 +2219,7 @@ static int udev_rule_apply_token_to_event(
}
case TK_A_NAME: {
char buf[UDEV_PATH_SIZE];
bool truncated;
size_t count;
if (event->name_final)
@ -2100,7 +2234,13 @@ static int udev_rule_apply_token_to_event(
break;
}
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_rule_warning(dev, rules, "The network interface name '%s' is truncated while substituting into '%s', "
"refusing to set the name.", buf, token->value);
break;
}
if (IN_SET(event->esc, ESCAPE_UNSET, ESCAPE_REPLACE)) {
if (naming_scheme_has(NAMING_REPLACE_STRICTLY))
count = udev_replace_ifname(buf);
@ -2119,6 +2259,7 @@ static int udev_rule_apply_token_to_event(
}
case TK_A_DEVLINK: {
char buf[UDEV_PATH_SIZE], *p;
bool truncated;
size_t count;
if (event->devlink_final)
@ -2131,7 +2272,13 @@ static int udev_rule_apply_token_to_event(
device_cleanup_devlinks(dev);
/* allow multiple symlinks separated by spaces */
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), event->esc != ESCAPE_NONE);
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), event->esc != ESCAPE_NONE, &truncated);
if (truncated) {
log_rule_warning(dev, rules, "The symbolic link path '%s' is truncated while substituting into '%s', "
"refusing to add the device symbolic link.", buf, token->value);
break;
}
if (event->esc == ESCAPE_UNSET)
count = udev_replace_chars(buf, "/ ");
else if (event->esc == ESCAPE_REPLACE)
@ -2152,7 +2299,10 @@ static int udev_rule_apply_token_to_event(
next = skip_leading_chars(next, NULL);
}
strscpyl(filename, sizeof(filename), "/dev/", p, NULL);
strscpyl_full(filename, sizeof(filename), &truncated, "/dev/", p, NULL);
if (truncated)
continue;
r = device_add_devlink(dev, filename);
if (r < 0)
return log_rule_error_errno(dev, rules, r, "Failed to add devlink '%s': %m", filename);
@ -2165,17 +2315,30 @@ static int udev_rule_apply_token_to_event(
case TK_A_ATTR: {
char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE];
const char *val, *key_name = token->data;
bool truncated;
if (udev_resolve_subsys_kernel(key_name, buf, sizeof(buf), false) < 0 &&
sd_device_get_syspath(dev, &val) >= 0)
strscpyl(buf, sizeof(buf), val, "/", key_name, NULL);
sd_device_get_syspath(dev, &val) >= 0) {
strscpyl_full(buf, sizeof(buf), &truncated, val, "/", key_name, NULL);
if (truncated) {
log_rule_warning(dev, rules,
"The path to the attribute '%s/%s' is too long, refusing to set the attribute.",
val, key_name);
break;
}
}
r = attr_subst_subdir(buf);
if (r < 0) {
log_rule_error_errno(dev, rules, r, "Could not find file matches '%s', ignoring: %m", buf);
break;
}
(void) udev_event_apply_format(event, token->value, value, sizeof(value), false);
(void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated);
if (truncated) {
log_rule_warning(dev, rules, "The attribute value '%s' is truncated while substituting into '%s', "
"refusing to set the attribute '%s'", value, token->value, buf);
break;
}
log_rule_debug(dev, rules, "ATTR '%s' writing '%s'", buf, value);
r = write_string_file(buf, value,
@ -2189,9 +2352,22 @@ static int udev_rule_apply_token_to_event(
}
case TK_A_SYSCTL: {
char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE];
bool truncated;
(void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_rule_warning(dev, rules, "The sysctl entry name '%s' is truncated while substituting into '%s', "
"refusing to set the sysctl entry.", buf, (const char*) token->data);
break;
}
(void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated);
if (truncated) {
log_rule_warning(dev, rules, "The sysctl value '%s' is truncated while substituting into '%s', "
"refusing to set the sysctl entry '%s'", value, token->value, buf);
break;
}
(void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false);
(void) udev_event_apply_format(event, token->value, value, sizeof(value), false);
sysctl_normalize(buf);
log_rule_debug(dev, rules, "SYSCTL '%s' writing '%s'", buf, value);
r = sysctl_write(buf, value);
@ -2203,6 +2379,7 @@ static int udev_rule_apply_token_to_event(
case TK_A_RUN_PROGRAM: {
_cleanup_free_ char *cmd = NULL;
char buf[UDEV_PATH_SIZE];
bool truncated;
if (event->run_final)
break;
@ -2212,7 +2389,12 @@ static int udev_rule_apply_token_to_event(
if (IN_SET(token->op, OP_ASSIGN, OP_ASSIGN_FINAL))
ordered_hashmap_clear_free_key(event->run_list);
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
(void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated);
if (truncated) {
log_rule_warning(dev, rules, "The command '%s' is truncated while substituting into '%s', "
"refusing to invoke the command.", buf, token->value);
break;
}
cmd = strdup(buf);
if (!cmd)

View File

@ -87,6 +87,11 @@ int control_main(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to initialize udev control: %m");
/* See comments in on_post() in udevd.c. */
r = udev_ctrl_send_pid(uctrl);
if (r < 0)
return log_error_errno(r, "Failed to send pid of this process: %m");
while ((c = getopt_long(argc, argv, "el:sSRp:m:t:Vh", options, NULL)) >= 0)
switch (c) {
case 'e':

View File

@ -135,8 +135,11 @@ int test_main(int argc, char *argv[], void *userdata) {
ORDERED_HASHMAP_FOREACH_KEY(val, cmd, event->run_list) {
char program[UDEV_PATH_SIZE];
bool truncated;
(void) udev_event_apply_format(event, cmd, program, sizeof(program), false);
(void) udev_event_apply_format(event, cmd, program, sizeof(program), false, &truncated);
if (truncated)
log_warning("The command '%s' is truncated while substituting into '%s'.", program, cmd);
printf("run: '%s'\n", program);
}

View File

@ -108,6 +108,8 @@ typedef struct Manager {
bool stop_exec_queue;
bool exit;
pid_t pid_udevadm; /* pid of 'udevadm control' */
} Manager;
typedef enum EventState {
@ -1215,6 +1217,10 @@ static int on_ctrl_msg(UdevCtrl *uctrl, UdevCtrlMessageType type, const UdevCtrl
log_debug("Received udev control message (EXIT)");
manager_exit(manager);
break;
case UDEV_CTRL_SENDER_PID:
log_debug("Received udev control message (SENDER_PID)");
manager->pid_udevadm = value->pid;
break;
default:
log_debug("Received unknown udev control message, ignoring");
}
@ -1490,9 +1496,35 @@ static int on_post(sd_event_source *s, void *userdata) {
if (manager->exit)
return sd_event_exit(manager->event, 0);
if (manager->cgroup)
/* cleanup possible left-over processes in our cgroup */
(void) cg_kill(SYSTEMD_CGROUP_CONTROLLER, manager->cgroup, SIGKILL, CGROUP_IGNORE_SELF, NULL, NULL, NULL);
if (!manager->cgroup)
return 1;
/* Cleanup possible left-over processes in our cgroup. But do not kill udevadm called by
* ExecReload= in systemd-udevd.service. See #16867. To protect it, the following two ways are
* introduced:
*
* 1. After the connection is accepted, but the PID of udevadm is not received, do not call
* cg_kill(). So, in this period, unwanted process or threads may exist in our cgroup.
* But, it is expected that the PID of the udevadm will be received soon. So, this time
* period should be short enough.
* 2. After the PID of udevadm is received, check the process is active or not, and if it is
* still active, set the PID to the deny list for cg_kill(). Why udev_ctrl_is_connected() is
* not enough? Because the udevadm may be still active after the control socket is
* disconnected. If the process is not active, then clear the PID for later connections.
*/
if (udev_ctrl_is_connected(manager->ctrl) >= 0 && !pid_is_valid(manager->pid_udevadm))
return 1;
_cleanup_set_free_ Set *pids = NULL;
if (pid_is_valid(manager->pid_udevadm)) {
if (pid_is_alive(manager->pid_udevadm))
(void) set_ensure_put(&pids, NULL, PID_TO_PTR(manager->pid_udevadm));
else
manager->pid_udevadm = 0;
}
(void) cg_kill(SYSTEMD_CGROUP_CONTROLLER, manager->cgroup, SIGKILL, CGROUP_IGNORE_SELF, pids, NULL, NULL);
return 1;
}

View File

@ -0,0 +1 @@
../TEST-01-BASIC/Makefile

View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -e
TEST_DESCRIPTION="Test propagation of exit status to On{Failure,Success}= dependencies"
TEST_NO_QEMU=1
# shellcheck source=test/test-functions
. "$TEST_BASE_DIR/test-functions"
do_test "$@"

View File

@ -1932,11 +1932,9 @@ inst_libs() {
while read -r line; do
[[ "$line" = 'not a dynamic executable' ]] && break
# Skip a harmless error when running the tests on a system with a significantly
# older systemd version (ldd tries to resolve the unprefixed RPATH for libsystemd.so.0,
# which is in this case older than the already installed libsystemd.so.0 in $initdir).
# The issue is triggered by installing test dependencies in install_missing_libraries().
[[ "$line" =~ libsystemd.so.*:\ version\ .*\ not\ found ]] && continue
# Ignore errors about our own stuff missing. This is most likely caused
# by ldd attempting to use the unprefixed RPATH.
[[ "$line" =~ libsystemd.*\ not\ found ]] && continue
if [[ "$line" =~ $so_regex ]]; then
file="${BASH_REMATCH[1]}"
@ -2309,7 +2307,7 @@ inst_binary() {
# In certain cases we might attempt to install a binary which is already
# present in the test image, yet it's missing from the host system.
# In such cases, let's check if the binary indeed exists in the image
# before doing any other chcecks. If it does, immediately return with
# before doing any other checks. If it does, immediately return with
# success.
if [[ $# -eq 1 ]]; then
for path in "" bin sbin usr/bin usr/sbin; do
@ -2328,6 +2326,10 @@ inst_binary() {
while read -r line; do
[[ "$line" = 'not a dynamic executable' ]] && break
# Ignore errors about our own stuff missing. This is most likely caused
# by ldd attempting to use the unprefixed RPATH.
[[ "$line" =~ libsystemd.*\ not\ found ]] && continue
if [[ "$line" =~ $so_regex ]]; then
file="${BASH_REMATCH[1]}"
[[ -e "${initdir}/$file" ]] && continue

View File

@ -0,0 +1,9 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[NetDev]
Name=ip6tnl-slaac
Kind=ip6tnl
[Tunnel]
Mode=ip6ip6
Local=slaac
Remote=2001:473:fece:cafe::5179

View File

@ -0,0 +1,10 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=ip6tnl-slaac
[Network]
IPv6AcceptRA=no
LinkLocalAddressing=yes
[Route]
Destination=0.0.0.0/0

View File

@ -0,0 +1,10 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Match]
Name=veth99
[Network]
IPv6AcceptRA=yes
Tunnel=ip6tnl-slaac
[IPv6AcceptRA]
PrefixAllowList=2002:da8:1::/64

View File

@ -900,6 +900,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'ip6gretun97',
'ip6gretun98',
'ip6gretun99',
'ip6tnl-slaac',
'ip6tnl97',
'ip6tnl98',
'ip6tnl99',
@ -986,6 +987,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-ip6gre-tunnel.netdev',
'25-ip6tnl-tunnel-any-any.netdev',
'25-ip6tnl-tunnel-local-any.netdev',
'25-ip6tnl-tunnel-local-slaac.netdev',
'25-ip6tnl-tunnel-local-slaac.network',
'25-ip6tnl-tunnel-remote-any.netdev',
'25-ip6tnl-tunnel.netdev',
'25-ipip-tunnel-any-any.netdev',
@ -1044,8 +1047,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'gretun.network',
'ip6gretap.network',
'ip6gretun.network',
'ip6tnl-slaac.network',
'ip6tnl.network',
'ipip.network',
'ipv6-prefix.network',
'ipvlan.network',
'ipvtap.network',
'isatap.network',
@ -1664,19 +1669,33 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ip6tnl.network',
'25-ip6tnl-tunnel.netdev', '25-tunnel.network',
'25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
'25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
'25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
'25-veth.netdev', 'ip6tnl-slaac.network', 'ipv6-prefix.network',
'25-ip6tnl-tunnel-local-slaac.netdev', '25-ip6tnl-tunnel-local-slaac.network')
start_networkd()
self.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable', 'dummy98:degraded'])
self.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable', 'ip6tnl-slaac:degraded',
'dummy98:degraded', 'veth99:routable', 'veth-peer:degraded'])
output = check_output('ip -d link show ip6tnl99')
print(output)
self.assertRegex(output, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
self.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98', output)
output = check_output('ip -d link show ip6tnl98')
print(output)
self.assertRegex(output, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (any|::) dev dummy98')
output = check_output('ip -d link show ip6tnl97')
print(output)
self.assertRegex(output, 'ip6tnl ip6ip6 remote (any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
output = check_output('ip -d link show ip6tnl-slaac')
print(output)
self.assertIn('ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2002:da8:1:0:1034:56ff:fe78:9abc dev veth99', output)
output = check_output('ip -6 address show veth99')
print(output)
self.assertIn('inet6 2002:da8:1:0:1034:56ff:fe78:9abc/64 scope global dynamic', output)
output = check_output('ip -4 route show default')
print(output)
self.assertIn('default dev ip6tnl-slaac proto static', output)
def test_sit_tunnel(self):
copy_unit_to_networkd_unit_path('12-dummy.netdev', 'sit.network',

View File

@ -0,0 +1,7 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=TEST-68-PROPAGATE-EXIT-STATUS
[Service]
Type=oneshot
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh

230
test/units/testsuite-68.sh Executable file
View File

@ -0,0 +1,230 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -ex
set -o pipefail
# Wait for a service to enter a state within a timeout period, if it doesn't
# enter the desired state within the timeout period then this function will
# exit the test case with a non zero exit code.
wait_on_state_or_fail () {
service=$1
expected_state=$2
timeout=$3
state=$(systemctl show "$service" --property=ActiveState --value)
while [ "$state" != "$expected_state" ]; do
if [ "$timeout" = "0" ]; then
systemd-analyze log-level info
exit 1
fi
timeout=$((timeout - 1))
sleep 1
state=$(systemctl show "$service" --property=ActiveState --value)
done
}
systemd-analyze log-level debug
systemd-analyze log-target console
# Trigger testservice-failure-exit-handler-68.service
cat >/run/systemd/system/testservice-failure-68.service <<EOF
[Unit]
Description=TEST-68-PROPAGATE-EXIT-STATUS with OnFailure= trigger
OnFailure=testservice-failure-exit-handler-68.service
[Service]
ExecStart=/bin/bash -c "exit 1"
EOF
# Another service which triggers testservice-failure-exit-handler-68.service
cat >/run/systemd/system/testservice-failure-68-additional.service <<EOF
[Unit]
Description=TEST-68-PROPAGATE-EXIT-STATUS Additional service with OnFailure= trigger
OnFailure=testservice-failure-exit-handler-68.service
[Service]
ExecStart=/bin/bash -c "exit 1"
EOF
# Trigger testservice-success-exit-handler-68.service
cat >/run/systemd/system/testservice-success-68.service <<EOF
[Unit]
Description=TEST-68-PROPAGATE-EXIT-STATUS with OnSuccess= trigger
OnSuccess=testservice-success-exit-handler-68.service
[Service]
ExecStart=/bin/bash -c "exit 0"
EOF
# Trigger testservice-success-exit-handler-68.service
cat >/run/systemd/system/testservice-success-68-additional.service <<EOF
[Unit]
Description=TEST-68-PROPAGATE-EXIT-STATUS Addition service with OnSuccess= trigger
OnSuccess=testservice-success-exit-handler-68.service
[Service]
ExecStart=/bin/bash -c "exit 0"
EOF
# Script to check that when an OnSuccess= dependency fires, the correct
# MONITOR* env variables are passed. This script handles the case where
# multiple services triggered the unit that calls this script. In this
# case we need to check the MONITOR_METADATA variable for >= 1 service
# details since jobs may merge.
cat >/tmp/check_on_success.sh <<EOF
#!/usr/bin/env bash
set -ex
echo "MONITOR_METADATA=\$MONITOR_METADATA"
IFS=';' read -ra ALL_SERVICE_MD <<< "\$MONITOR_METADATA"
for SERVICE_MD in "\${ALL_SERVICE_MD[@]}"; do
IFS=',' read -ra METADATA <<< "\$SERVICE_MD"
IFS='=' read -ra SERVICE_RESULT <<< "\${METADATA[0]}"
SERVICE_RESULT=\${SERVICE_RESULT[1]}
IFS='=' read -ra EXIT_CODE <<< "\${METADATA[1]}"
EXIT_CODE=\${EXIT_CODE[1]}
IFS='=' read -ra EXIT_STATUS <<< "\${METADATA[2]}"
EXIT_STATUS=\${EXIT_STATUS[1]}
IFS='=' read -ra INVOCATION_ID <<< "\${METADATA[3]}"
INVOCATION_ID=\${INVOCATION_ID[1]}
IFS='=' read -ra UNIT <<< "\${METADATA[4]}"
UNIT=\${UNIT[1]}
if [ "\$SERVICE_RESULT" != "success" ]; then
echo 'SERVICE_RESULT was "\$SERVICE_RESULT", expected "success"';
exit 1;
fi
if [ "\$EXIT_CODE" != "exited" ]; then
echo 'EXIT_CODE was "\$EXIT_CODE", expected "exited"';
exit 1;
fi
if [ "\$EXIT_STATUS" != "0" ]; then
echo 'EXIT_STATUS was "\$EXIT_STATUS", expected "0"';
exit 1;
fi
if [ -z "\$INVOCATION_ID" ]; then
echo 'INVOCATION_ID unset';
exit 1;
fi
if [[ "\$UNIT" != "testservice-success-68.service" && "\$UNIT" != "testservice-success-68-additional.service" && "\$UNIT" != "testservice-transient-success-68.service" ]]; then
echo 'UNIT was "\$UNIT", expected "testservice-success-68{-additional,-transient}.service"';
exit 1;
fi
done
exit 0;
EOF
chmod +x /tmp/check_on_success.sh
# Handle testservice-failure-exit-handler-68.service exiting with success.
cat >/run/systemd/system/testservice-success-exit-handler-68.service <<EOF
[Unit]
Description=TEST-68-PROPAGATE-EXIT-STATUS handle service exiting in success
[Service]
ExecStartPre=/tmp/check_on_success.sh
ExecStart=/tmp/check_on_success.sh
EOF
# Script to check that when an OnFailure= dependency fires, the correct
# MONITOR* env variables are passed. This script handles the case where
# multiple services triggered the unit that calls this script. In this
# case we need to check the MONITOR_METADATA variable for >=1 service
# details since jobs may merge.
cat >/tmp/check_on_failure.sh <<EOF
#!/usr/bin/env bash
set -ex
echo "MONITOR_METADATA=\$MONITOR_METADATA"
IFS=';' read -ra ALL_SERVICE_MD <<< "\$MONITOR_METADATA"
for SERVICE_MD in "\${ALL_SERVICE_MD[@]}"; do
IFS=',' read -ra METADATA <<< "\$SERVICE_MD"
IFS='=' read -ra SERVICE_RESULT <<< "\${METADATA[0]}"
SERVICE_RESULT=\${SERVICE_RESULT[1]}
IFS='=' read -ra EXIT_CODE <<< "\${METADATA[1]}"
EXIT_CODE=\${EXIT_CODE[1]}
IFS='=' read -ra EXIT_STATUS <<< "\${METADATA[2]}"
EXIT_STATUS=\${EXIT_STATUS[1]}
IFS='=' read -ra INVOCATION_ID <<< "\${METADATA[3]}"
INVOCATION_ID=\${INVOCATION_ID[1]}
IFS='=' read -ra UNIT <<< "\${METADATA[4]}"
UNIT=\${UNIT[1]}
if [ "\$SERVICE_RESULT" != "exit-code" ]; then
echo 'SERVICE_RESULT was "\$SERVICE_RESULT", expected "exit-code"';
exit 1;
fi
if [ "\$EXIT_CODE" != "exited" ]; then
echo 'EXIT_CODE was "\$EXIT_CODE", expected "exited"';
exit 1;
fi
if [ "\$EXIT_STATUS" != "1" ]; then
echo 'EXIT_STATUS was "\$EXIT_STATUS", expected "1"';
exit 1;
fi
if [ -z "\$INVOCATION_ID" ]; then
echo 'INVOCATION_ID unset';
exit 1;
fi
if [[ "\$UNIT" != "testservice-failure-68.service" && "\$UNIT" != "testservice-failure-68-additional.service" && "\$UNIT" != "testservice-transient-failure-68.service" ]]; then
echo 'UNIT was "\$UNIT", expected "testservice-failure-68{-additional,-transient}.service"';
exit 1;
fi
done
exit 0;
EOF
chmod +x /tmp/check_on_failure.sh
# Handle testservice-failure-exit-handler-68.service exiting with failure.
cat >/run/systemd/system/testservice-failure-exit-handler-68.service <<EOF
[Unit]
Description=TEST-68-PROPAGATE-EXIT-STATUS handle service exiting in failure
[Service]
ExecStartPre=/tmp/check_on_failure.sh
ExecStart=/tmp/check_on_failure.sh
EOF
systemctl daemon-reload
# The running of the OnFailure= and OnSuccess= jobs for all of these services
# may result in jobs being merged.
systemctl start testservice-failure-68.service
wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
systemctl start testservice-failure-68-additional.service
wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
systemctl start testservice-success-68.service
wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
systemctl start testservice-success-68-additional.service
wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
# Test some transient units since these exit very quickly.
systemd-run --unit=testservice-transient-success-68 --property=OnSuccess=testservice-success-exit-handler-68.service /bin/bash -c "exit 0;"
wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
systemd-run --unit=testservice-transient-failure-68 --property=OnFailure=testservice-failure-exit-handler-68.service /bin/bash -c "exit 1;"
wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
# These yield a higher chance of resulting in jobs merging.
systemctl start testservice-failure-68.service testservice-failure-68-additional.service --no-block
wait_on_state_or_fail "testservice-failure-exit-handler-68.service" "inactive" "10"
systemctl start testservice-success-68.service testservice-success-68-additional.service --no-block
wait_on_state_or_fail "testservice-success-exit-handler-68.service" "inactive" "10"
systemd-analyze log-level info
echo OK >/testok
exit 0

View File

@ -12,6 +12,7 @@ L+ /etc/mtab - - - - ../proc/self/mounts
{% if HAVE_SMACK_RUN_LABEL %}
t /etc/mtab - - - - security.SMACK64=_
{% endif %}
C! /etc/locale.conf - - - -
C! /etc/nsswitch.conf - - - -
{% if HAVE_PAM %}
C! /etc/pam.d - - - -

View File

@ -67,7 +67,9 @@ df="$build/dns-fuzzing"
git clone --depth 1 https://github.com/CZ-NIC/dns-fuzzing "$df"
zip -jqr "$OUT/fuzz-dns-packet_seed_corpus.zip" "$df/packet"
install -Dt "$OUT/src/shared/" "$build"/src/shared/libsystemd-shared-*.so
install -Dt "$OUT/src/shared/" \
"$build"/src/shared/libsystemd-shared-*.so \
"$build"/src/core/libsystemd-core-*.so
wget -O "$OUT/fuzz-json.dict" https://raw.githubusercontent.com/rc0r/afl-fuzz/master/dictionaries/json.dict