Compare commits

...

66 Commits

Author SHA1 Message Date
Guilhem Lettron 2e22a54f4e Implement SNI when using DNS-over-TLS
Some DNS providers need SNI to identify client.

This can be used by adding #name to a DNS.
Example:
[Resolve]
DNS=192.168.1.1#example.com
2019-12-04 23:24:06 +09:00
Lennart Poettering b7aa08ca15
Merge pull request #14111 from keszybz/unknown-section-warning
Warn about unknown sections
2019-12-04 14:12:24 +01:00
Zbigniew Jędrzejewski-Szmek 41ab3b7a85
Merge pull request #13953 from SpencerMichaels/systemd-boot-efistub-id-fix
boot: Fix default/oneshot selection for EFISTUB entries
2019-12-04 13:30:52 +01:00
Lennart Poettering f9f8268ac6
Merge pull request #14218 from poettering/homed-preparatory-small-stuff
Assorted smaller stuff split out from homed PR
2019-12-04 13:13:38 +01:00
Lennart Poettering ef560d8b06
Merge pull request #13886 from poettering/sd-event-pidfd
add pidfd support to sd-event (but not yet PID 1)
2019-12-04 13:13:18 +01:00
Lennart Poettering 6b636c2d27 main-func: send main exit code to parent via sd_notify() on exit
So far we silently convert negative return values from run() as
EXIT_FAILURE, which is how UNIX expects it. In many cases it would be
very useful for the caller to retrieve the actual error number we exit
with. Let's generically return that via sd_notify()'s ERRNO= attribute.
This means callers can set $NOTIFY_SOCKET and get the actual error
number delivered at their doorstep just like that.
2019-12-04 10:59:59 +01:00
Lennart Poettering 8987afc4d1 process-util: add new safe_fork() flag for connecting stdout to stderr
This adds a new safe_fork() flag. If set the child process' fd 1 becomes
fd 2 of the caller. This is useful for invoking tools (such as various
mkfs/fsck implementations) that output status messages to stdout, but
which we invoke and don't want to pollute stdout with their output.
2019-12-04 10:59:42 +01:00
Lennart Poettering 7a509acc29 tmpfile-util: modernize mkostemp_safe() a bit 2019-12-04 10:59:30 +01:00
Lennart Poettering e5ea9ed030 tmpfile-util: if no path is passed to fopen_temporary() make one up
Let's beef up functionality a bit, and modernize the whole function.
2019-12-04 10:59:17 +01:00
Lennart Poettering a3292ec8d7 user-util: add uid_is_container() for checking whether UID is in container range
We have similar calls for the dynamic user and system range, let's add
this too here.
2019-12-04 10:59:04 +01:00
Lennart Poettering 6093b2bb05 user-util: export is_nologin_shell() so that we can use it elsewhere 2019-12-04 10:58:46 +01:00
Charles (Chas) Williams c0dd326953 man: document journal rate limit burst multiplier
The actual burst limit is modified by the remaining disk space. This
isn't mentioned anywhere in the available documentation and might be a
source of surprise for an end user expecting certain behaviors.
2019-12-04 10:58:15 +01:00
Lennart Poettering 53caaffdf4 string-util: readd string_erase()
This was dropped in 8e27167cc9, but is
actually useful for some usecases still.
2019-12-04 10:58:12 +01:00
Lennart Poettering 282bde1066 memory-util: introduce erase_and_free() helper 2019-12-04 10:57:59 +01:00
Lennart Poettering 9933a47808 errno-util: add new ERRNO_IS_DISK_SPACE() helper 2019-12-04 10:57:44 +01:00
Lennart Poettering b64cea6027 ordered-set: add ordered_set_first() helper 2019-12-04 10:57:37 +01:00
Lennart Poettering 22810041c2 parse-util: sometimes it is useful to check if a string is a valid integer, but not actually parse it 2019-12-04 10:56:50 +01:00
Leonid Bloch 26601a2a17 sd-boot: Add a 0.1 second delay before key-probing for showing menu
If there is no boot menu timeout, pressing a key during boot should get
the boot menu displayed. However, on some systems the keyboard is not
initialized right away, which causes the menu to be inaccessible if no
timeout is specified.

To resolve this, if the error is "not ready" after the initial attempt of
detection, wait for 0.1 second and retry. This solves the problem
described above on all the tested systems.

The reason for just a single retry, and not retrying while "not ready",
is that some firmwares continue to return the "not ready" error on
every probe attempt if no key is pressed.

Signed-off-by: Leonid Bloch <lb.workbox@gmail.com>
2019-12-04 10:52:28 +01:00
Lennart Poettering e544601536 sd-event: refuse running default event loops in any other thread than the one they are default for 2019-12-04 10:36:28 +01:00
Lennart Poettering 8089643328 man: document the new sd-event pidfd magic 2019-12-04 10:36:10 +01:00
Lennart Poettering b350807200 man: mention that SIGCHLD has to be blocked before using sd_event_add_child() 2019-12-04 10:35:56 +01:00
Lennart Poettering 68765d94fe man: don't claim we'd unblock the specified signal in sd_event_add_signal()
We don't, the signal remains blocked. We use signalfd() to be able to
read the signal events without unblocking the signal.

While we are at it, mention that pthread_sigmask() is fine too.
2019-12-04 10:35:42 +01:00
Lennart Poettering 3ecb3bdc93 test: add test for pidfd support in sd-event 2019-12-04 10:35:39 +01:00
Lennart Poettering ee880b37c1 sd-event: refuse sd_event_add_child() if SIGCHLD is not blocked
We already refuse sd_event_add_signal() if the specified signal is not
blocked, let's do this also for sd_event_add_child(), since we might
need signalfd() to implement this, and this means the signal needs to be
blocked.
2019-12-04 10:35:27 +01:00
Lennart Poettering d1b75241ba sd-event: make use of new signal_is_blocked() helper 2019-12-04 10:35:16 +01:00
Lennart Poettering 90b15e18ee signal-util: add new helper signal_is_blocked() 2019-12-04 10:35:01 +01:00
Lennart Poettering f8f3f9263e sd-event: add pidfd support
This adds support for watching for process exits via Linux new pidfd
concept. This makes watching processes and killing them race-free if
properly used, fixing a long-standing UNIX misdesign.

This patch adds implicit and explicit pidfd support to sd-event: if a
process shall be watched and is specified by PID we will now internally
create a pidfd for it and use that, if available. Alternatively a new
constructor for child process event sources is added that takes pidfds
as input.

Besides mere watching of child processes via pidfd two additional
features are added:

→ sd_event_source_send_child_signal() allows sending a signal to the
  process being watched in the safest way possible (wrapping
  the new pidfd_send_signal() syscall).

→ sd_event_source_set_child_process_own() allows marking a process
  watched for destruction as soon as the event source is freed. This is
  currently implemented in userspace, but hopefully will become a kernel
  feature eventually.

Altogether this means an sd_event_source object is now a safe and stable
concept for referencing processes in race-free way, with automatic
fallback to pre-pidfd kernels.

Note that this patch adds support for this only to sd-event, not to PID
1. That's because PID 1 needs to use waitid(P_ALL) for reaping any
process that might get reparented to it. This currently semantically
conflicts with pidfd use for watching processes since we P_ALL is
undirected and thus might reap process earlier than the pidfd notifies
process end, which is hard to handle. The kernel will likely gain a
concept for excluding specific pidfds from P_ALL watching, as soon as
that is around we can start making use of this in PID 1 too.
2019-12-04 10:34:41 +01:00
Lennart Poettering 298f466f15 process-util: add helper pidfd_get_pid()
It returns the pid_t a pidfd refers to.
2019-12-04 10:34:26 +01:00
Lennart Poettering 5ead4e85f6 missing: add rt_sigqueueinfo() syscall definition
This is not a new system call at all (since kernel 2.2), however it's
not exposed in glibc (a wrapper is exposed however in sigqueue(), but it
substantially simplifies the system call). Since we want a nice fallback
for sending signals on non-pidfd systems for pidfd_send_signal() let's
wrap rt_sigqueueinfo() since it takes the same siginfo_t parameter.
2019-12-04 10:34:03 +01:00
Lennart Poettering 5f152f43d0 missing: define new pidfd syscalls 2019-12-04 10:33:41 +01:00
Lennart Poettering 5a795bff38 sd-event: (void)ify some epoll_ctl() syscall invocations 2019-12-04 10:33:38 +01:00
Lennart Poettering d1cf202374 sd-event: drop unnecessary local variable 2019-12-04 10:32:59 +01:00
Paul Davey 9f537ae310 udev: Ensure udev_event_spawn reads stdout
When running the program with udev_event_spawn it is possible to miss
output in stdout when the program exits causing the result to be empty
which can cause rules using the result to not function correctly.

This is due to the on_spawn_sigchld callback being processed while IO is
still pending and causing the event loop to exit.

To correct this the sigchld event source is made a lower priority than
the other event sources to ensure it is processed after IO.  This
requires changing the IO event source to oneshot and re-enabling it when
valid data is read but not for EOF, this prevents the empty pipes
constantly generating IO events.
2019-12-04 10:31:37 +01:00
Lennart Poettering eaadc03d61
Merge pull request #14133 from keur/clear_ambient_inherited
Clear ambient inherited
2019-12-04 10:30:58 +01:00
Lennart Poettering b51d61fec6
Merge pull request #14177 from keszybz/use-initrd.target
Use initrd.target in the initramfs
2019-12-04 10:30:32 +01:00
Christian Göttsche a9dfac21ec core: reload SELinux label cache on daemon-reload
Reloading the SELinux label cache here enables a light-wight follow-up of a SELinux policy change, e.g. adding a label for a RuntimeDirectory.

Closes: #13363
2019-12-04 10:29:46 +01:00
Lennart Poettering 97a3e8d582
Merge pull request #14189 from cgzones/selinux_tmpfiles
Selinux tmpfiles
2019-12-04 10:29:22 +01:00
Lennart Poettering 68d58f3869 pid1: add new kernel cmdline arg systemd.cpu_affinity=
Let's allow configuration of the CPU affinity via the kernel cmdline,
overriding CPUAffinity= in /etc/systemd/system.conf

Prompted by:

https://lists.freedesktop.org/archives/systemd-devel/2019-November/043754.html
2019-12-04 10:28:43 +01:00
Raphael 6355715e5b Fix DPI for MX Master 2s bluetooth mouse
Mouse behaviour is unusably slow disregardless of mouse speed settings.
2019-12-04 09:23:57 +01:00
Jérémy Rosen a652f050a7 Create parent directories when creating systemd-private subdirs
This is needed when systemd is compiled without systemd-tmpfiles
2019-12-04 09:22:52 +01:00
Zbigniew Jędrzejewski-Szmek 206056cf01
Merge pull request #14211 from yuwata/support-nlmsgerr_attr_msg
Support NLMSGERR_ATTR_MSG
2019-12-04 09:15:23 +01:00
Yu Watanabe e813de549b network: do not return error but return UINT64_MAX if speed meter is disabled
Fixes #14222.
2019-12-04 09:11:34 +01:00
lothrond cfd54b6a2e Alienware M17xR3 ejectcd button fix 2019-12-04 09:10:45 +01:00
Zbigniew Jędrzejewski-Szmek 23e5e79a51 initrd: fix systemd.debug-shell & friends
They would get assigned to an inactive target in the initramfs.
2019-12-03 14:48:27 +01:00
Yu Watanabe 5ecb131d94 network: include NLMSGERR_ATTR_MSG attribute in error message 2019-11-30 16:14:02 +09:00
Yu Watanabe e4a1e68d7a sd-netlink: support NLMSGERR_ATTR_MSG
From v4.12 the kernel appends some attributes to netlink acks
containing a textual description of the error and other fields.
This makes sd-netlink parse the attributes.
2019-11-30 16:13:51 +09:00
Zbigniew Jędrzejewski-Szmek 2b1daf24dc man: document initrd.target 2019-11-28 19:59:33 +01:00
Zbigniew Jędrzejewski-Szmek 8755dbad5b pid1: use initrd.target in the initramfs by default
This makes the code do what the documentation says. The code had no inkling
about initrd.target, so I think this change is fairly risky. As a fallback,
default.target will be loaded, so initramfses which relied on current behaviour
will still work, as along as they don't have a different initrd.target.

In an initramfs created with recent dracut:
$ ls -l usr/lib/systemd/system/{default.target,initrd.target}
lrwxrwxrwx. usr/lib/systemd/system/default.target -> initrd.target
-rw-r--r--. usr/lib/systemd/system/initrd.target
So at least for dracut, there should be no difference.

Also avoid a pointless allocation.
2019-11-28 19:59:33 +01:00
Christian Göttsche 80e7c84081 tmpfiles: create with correct MAC label on option C
Closes: #10855
2019-11-28 12:18:04 +01:00
Christian Göttsche aeec5efab5 copy: add flag COPY_MAC_CREATE to create with correct label
Useful for MAC aware file creation like in systemd-tmpfiles.
2019-11-28 12:17:56 +01:00
Zbigniew Jędrzejewski-Szmek 6e86b24db3 tree-wide: normalize includes of public headers
They are supposed to go into a sectinon of their own.
2019-11-28 09:14:22 +01:00
Zbigniew Jędrzejewski-Szmek fe7a6da8c5 core: use SPECIAL_DEFAULT_TARGET more 2019-11-28 09:13:45 +01:00
Kevin Kuehler 943800f4e7 execute: Call capability_ambient_set_apply even if ambient set is 0
The function capability_ambient_set_apply() now drops capabilities not
in the capability_ambient_set(), so it is necessary to call it when
the ambient set is empty.

Fixes #13163
2019-11-27 10:57:23 -08:00
Kevin Kuehler 155a6234ea test-capability: Modify ambient capability tests to test clearing caps
Change test_set_ambient_caps() to test_apply_ambient_caps(), since the
function capability_ambient_set_apply() not only sets ambient
capabilities, but clears inherited capabilities that are not explicitly
requested by the caller.
2019-11-27 10:57:21 -08:00
Kevin Kuehler 82d832b435 basic: Drop ambient inherited capabilities by default
Modify the functions capability_update_inherited_set() and
capability_ambient_set_apply() to drop capabilities not explicitly
requested by the user.
2019-11-26 11:21:40 -08:00
Zbigniew Jędrzejewski-Szmek f4331d0db2 shared/install: warn about unkown sections in unit files
As in the previous commit, ignoring unkown sections means users may be confused
easily. It is better to warn about misspellt section names.

In this case, we are using a separate item table, so we'd ignore all those
sections anyway, so we could list them with out the minus prefixes and the
effect would be the same. But I think it's clearer to prefix them.
2019-11-25 16:45:29 +01:00
Zbigniew Jędrzejewski-Szmek 130b812f9d network: warn about unknown sections when parsing .netdev files
Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1774242.
Now we'll emit the warning about unknown section [Netdev], making the issue
much easier to diagnose.
2019-11-25 16:45:29 +01:00
Zbigniew Jędrzejewski-Szmek ddeb3f5d4b shared/conf-parser: allow sections to be silently ignored with new -Section syntax
If we ignore any uknown section, we will not be able to show any
warning if a typo in a section name is made. Let's reverse our
approach, and explicitly list sections to ignore instead.

I opted to make use the same section list for this, instead of adding a second
list, because this list is passed through to many functions and adding yet
another parameter to the long signature would be very noisy.
2019-11-22 15:27:22 +01:00
Zbigniew Jędrzejewski-Szmek 94a404cb03 shared/conf-parser: document what the flags do 2019-11-22 13:11:35 +01:00
Zbigniew Jędrzejewski-Szmek f9761a89a8 shared/conf-parser: turn CONFIG_PARSE_REFUSE_BOM flag into a local variable
This is an internal implementation detail.
2019-11-22 13:11:35 +01:00
Spencer Michaels 15b82eecb6 boot: Deduplicate old-style loader entries.
In cases where systemd (and thus bootctl) is updated to a version
including the earlier unique-ID fix, but the corresponding new version
of systemd-boot is not installed to the ESP and run at least once,
the bootloader will report old-style entry IDs cached in the
LoaderEntries EFI variable, while bootctl will report new-style IDs for
the same entries, producing duplicate entries. This commit makes bootctl
compute and retain old-style IDs for non-auto entries so that it can
properly deduplicate entries even if the cache contains old-style IDs.
2019-11-21 15:50:03 -08:00
Spencer Michaels ae474efc3f boot: Update bootspec.c to match previous changes.
bootspec.c, which is used by bootctl and systemctl, computes bootloaders
entry IDs independently from systemd-boot. This commit updates
the ID computation in bootspec.c to be in line with the previous few
commits altering boot.c.
2019-11-18 22:59:51 -08:00
Spencer Michaels 10d0024a07 boot: Improve EFISTUB name and version detection.
Make two minor cosmetic changes to how config_entry_add_linux() extracts
OS information from the embedded /etc/os-release in EFISTUB binaries.
This increases the detail of information available to the user for
EFISTUB entries.

* entry->version, which was previously always null, is now set to the
value of VERSION, or VERSION_ID, or BUILD_ID (preferred in that order).
* entry->title, which was previously set to the value of PRETTY_NAME, can
now fall back to NAME or ID if PRETTY_NAME is not present. In that case,
NAME is preferred over ID.
2019-11-18 22:59:51 -08:00
Spencer Michaels 6cd12ebcfe boot: Retain ".conf" suffix for loader config IDs.
Change config_entry_add_from_file() so that it does not remove the
`.conf` ending from the filename that it uses for entry->id. This is
necessary because otherwise an EFISTUB binary and a loader config entry
might end up with the same ID, as the loader config IDs previously
didn't include the config filename's .conf extension (see boot.c:1435).
Consider, for instance, an EFISTUB /EFI/Linux/linux.efi and a loader
config /loader/entries/linux.efi.conf; both would have the ID linux.efi.

In addition, update a comment that previously stated that IDs are
non-unique (which is no longer the case, as of this commit).
2019-11-18 22:59:51 -08:00
Spencer Michaels 65901c0fd1 boot: Ignore EFISTUB binaries starting with "auto-".
To further increase similarity with loader configs and provide global
uniqueness, ignore filenames starting with auto- (see boot.c:1512).
2019-11-18 22:59:51 -08:00
Spencer Michaels 7fa23ab646 boot: Make EFISTUB IDs use binaries' filenames.
Change config_entry_add_linux() so that Linux EFISTUB entries' ids are
simply the binaries' filenames, as is already the case with loader
configs. This guarantees that EFISTUB binaries' IDs are mutually unique.
2019-11-18 22:59:44 -08:00
117 changed files with 1716 additions and 323 deletions

1
TODO
View File

@ -701,7 +701,6 @@ Features:
- allow multiple signal handlers per signal? - allow multiple signal handlers per signal?
- document chaining of signal handler for SIGCHLD and child handlers - document chaining of signal handler for SIGCHLD and child handlers
- define more intervals where we will shift wakeup intervals around in, 1h, 6h, 24h, ... - define more intervals where we will shift wakeup intervals around in, 1h, 6h, 24h, ...
- generate a failure of a default event loop is executed out-of-thread
* investigate endianness issues of UUID vs. GUID * investigate endianness issues of UUID vs. GUID

View File

@ -207,6 +207,10 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAlienware*:pn*:pvr*
KEYBOARD_KEY_c1=!prog2 #graphics amplifier, undock-button event KEYBOARD_KEY_c1=!prog2 #graphics amplifier, undock-button event
KEYBOARD_KEY_c2=!power #graphics amplifier, surprise undock event KEYBOARD_KEY_c2=!power #graphics amplifier, surprise undock event
# Alienware M17xR3 laptops
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAlienware*:pnM17xR3:*
KEYBOARD_KEY_89=ejectcd
########################################################### ###########################################################
# Asus # Asus
########################################################### ###########################################################

View File

@ -471,7 +471,7 @@ mouse:usb:v046dp4069:name:Logitech MX Master 2s:
# Logitech MX Master 2S (via Bluetooth) # Logitech MX Master 2S (via Bluetooth)
# Horiz wheel has 14 stops, angle is rounded up # Horiz wheel has 14 stops, angle is rounded up
mouse:bluetooth:v046dpb019:name:MX Master 2S Mouse: mouse:bluetooth:v046dpb019:name:MX Master 2S Mouse:
MOUSE_DPI=2000@2000 MOUSE_DPI=1000@2000
MOUSE_WHEEL_CLICK_ANGLE=15 MOUSE_WHEEL_CLICK_ANGLE=15
MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL=26 MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL=26
MOUSE_WHEEL_CLICK_COUNT=24 MOUSE_WHEEL_CLICK_COUNT=24

View File

@ -139,6 +139,51 @@
<literal>us</literal>. To turn off any kind of rate limiting, <literal>us</literal>. To turn off any kind of rate limiting,
set either value to 0.</para> set either value to 0.</para>
<para>Note that the effective rate limit is multiplied with a
factor derived from the available free disk space for the journal.
Currently, this factor is calculated using the base 2 logarithm.</para>
<table>
<title>Example <varname>RateLimitBurst=</varname> rate
modifications by the available disk space</title>
<tgroup cols='2'>
<colspec colname='freespace' />
<colspec colname='multiplier' />
<thead>
<row>
<entry>Available Disk Space</entry>
<entry>Burst Multiplier</entry>
</row>
</thead>
<tbody>
<row>
<entry>&lt;= 1MB</entry>
<entry>1</entry>
</row>
<row>
<entry>&lt;= 16MB</entry>
<entry>2</entry>
</row>
<row>
<entry>&lt;= 256MB</entry>
<entry>3</entry>
</row>
<row>
<entry>&lt;= 4GB</entry>
<entry>4</entry>
</row>
<row>
<entry>&lt;= 64GB</entry>
<entry>5</entry>
</row>
<row>
<entry>&lt;= 1TB</entry>
<entry>6</entry>
</row>
</tbody>
</tgroup>
</table>
<para>If a service provides rate limits for itself through <para>If a service provides rate limits for itself through
<varname>LogRateLimitIntervalSec=</varname> and/or <varname>LogRateLimitBurst=</varname> <varname>LogRateLimitIntervalSec=</varname> and/or <varname>LogRateLimitBurst=</varname>
in <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>, in <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,

View File

@ -393,6 +393,17 @@
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>systemd.cpu_affinity=</varname></term>
<listitem>
<para>Overrides the CPU affinity mask for the service manager and the default for all child
processes it forks. This takes precedence over <varname>CPUAffinity=</varname>, see
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details.</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><varname>modules_load=</varname></term> <term><varname>modules_load=</varname></term>
<term><varname>rd.modules_load=</varname></term> <term><varname>rd.modules_load=</varname></term>

View File

@ -214,6 +214,9 @@
resolver is not capable of authenticating the server, so it is resolver is not capable of authenticating the server, so it is
vulnerable to "man-in-the-middle" attacks.</para> vulnerable to "man-in-the-middle" attacks.</para>
<para>Server Name Indication (SNI) can be used when opening a TLS connection.
Entries in <varname>DNS=</varname> should be in format <literal>address#server_name</literal>.</para>
<para>In addition to this global DNSOverTLS setting <para>In addition to this global DNSOverTLS setting
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
also maintains per-link DNSOverTLS settings. For system DNS also maintains per-link DNSOverTLS settings. For system DNS

View File

@ -371,7 +371,15 @@ manpages = [
['sd_bus_wait', '3', [], ''], ['sd_bus_wait', '3', [], ''],
['sd_event_add_child', ['sd_event_add_child',
'3', '3',
['sd_event_child_handler_t', 'sd_event_source_get_child_pid'], ['sd_event_add_child_pidfd',
'sd_event_child_handler_t',
'sd_event_source_get_child_pid',
'sd_event_source_get_child_pidfd',
'sd_event_source_get_child_pidfd_own',
'sd_event_source_get_child_process_own',
'sd_event_source_send_child_signal',
'sd_event_source_set_child_pidfd_own',
'sd_event_source_set_child_process_own'],
''], ''],
['sd_event_add_defer', ['sd_event_add_defer',
'3', '3',

View File

@ -17,7 +17,14 @@
<refnamediv> <refnamediv>
<refname>sd_event_add_child</refname> <refname>sd_event_add_child</refname>
<refname>sd_event_add_child_pidfd</refname>
<refname>sd_event_source_get_child_pid</refname> <refname>sd_event_source_get_child_pid</refname>
<refname>sd_event_source_get_child_pidfd</refname>
<refname>sd_event_source_get_child_pidfd_own</refname>
<refname>sd_event_source_set_child_pidfd_own</refname>
<refname>sd_event_source_get_child_process_own</refname>
<refname>sd_event_source_set_child_process_own</refname>
<refname>sd_event_source_send_child_signal</refname>
<refname>sd_event_child_handler_t</refname> <refname>sd_event_child_handler_t</refname>
<refpurpose>Add a child process state change event source to an event loop</refpurpose> <refpurpose>Add a child process state change event source to an event loop</refpurpose>
@ -46,40 +53,77 @@
<paramdef>void *<parameter>userdata</parameter></paramdef> <paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype> </funcprototype>
<funcprototype>
<funcdef>int <function>sd_event_add_child_pidfd</function></funcdef>
<paramdef>sd_event *<parameter>event</parameter></paramdef>
<paramdef>sd_event_source **<parameter>source</parameter></paramdef>
<paramdef>int <parameter>pidfd</parameter></paramdef>
<paramdef>int <parameter>options</parameter></paramdef>
<paramdef>sd_event_child_handler_t <parameter>handler</parameter></paramdef>
<paramdef>void *<parameter>userdata</parameter></paramdef>
</funcprototype>
<funcprototype> <funcprototype>
<funcdef>int <function>sd_event_source_get_child_pid</function></funcdef> <funcdef>int <function>sd_event_source_get_child_pid</function></funcdef>
<paramdef>sd_event_source *<parameter>source</parameter></paramdef> <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
<paramdef>pid_t *<parameter>pid</parameter></paramdef> <paramdef>pid_t *<parameter>pid</parameter></paramdef>
</funcprototype> </funcprototype>
<funcprototype>
<funcdef>int <function>sd_event_source_get_child_pidfd</function></funcdef>
<paramdef>sd_event_source *<parameter>source</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_event_source_get_child_pidfd_own</function></funcdef>
<paramdef>sd_event_source *<parameter>source</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_event_source_set_child_pidfd_own</function></funcdef>
<paramdef>sd_event_source *<parameter>source</parameter></paramdef>
<paramdef>int <parameter>own</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_event_source_get_child_process_own</function></funcdef>
<paramdef>sd_event_source *<parameter>source</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_event_source_set_child_process_own</function></funcdef>
<paramdef>sd_event_source *<parameter>source</parameter></paramdef>
<paramdef>int <parameter>own</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_event_source_send_child_signal</function></funcdef>
<paramdef>sd_event_source *<parameter>source</parameter></paramdef>
<paramdef>int <parameter>sig</parameter></paramdef>
<paramdef>const siginfo_t *<parameter>info</parameter></paramdef>
<paramdef>unsigned <parameter>flags</parameter></paramdef>
</funcprototype>
</funcsynopsis> </funcsynopsis>
</refsynopsisdiv> </refsynopsisdiv>
<refsect1> <refsect1>
<title>Description</title> <title>Description</title>
<para><function>sd_event_add_child()</function> adds a new child <para><function>sd_event_add_child()</function> adds a new child process state change event source to an
process state change event source to an event loop. The event loop event loop. The event loop object is specified in the <parameter>event</parameter> parameter, the event
object is specified in the <parameter>event</parameter> parameter, source object is returned in the <parameter>source</parameter> parameter. The <parameter>pid</parameter>
the event source object is returned in the parameter specifies the PID of the process to watch, which must be a direct child process of the invoking
<parameter>source</parameter> parameter. The process. The <parameter>handler</parameter> must reference a function to call when the process changes
<parameter>pid</parameter> parameter specifies the PID of the state. The handler function will be passed the <parameter>userdata</parameter> pointer, which may be
process to watch. The <parameter>handler</parameter> must chosen freely by the caller. The handler also receives a pointer to a <structname>siginfo_t</structname>
reference a function to call when the process changes state. The structure containing information about the child process event. The <parameter>options</parameter>
handler function will be passed the parameter determines which state changes will be watched for. It must contain an OR-ed mask of
<parameter>userdata</parameter> pointer, which may be chosen <constant>WEXITED</constant> (watch for the child process terminating), <constant>WSTOPPED</constant>
freely by the caller. The handler also receives a pointer to a (watch for the child process being stopped by a signal), and <constant>WCONTINUED</constant> (watch for
<structname>siginfo_t</structname> structure containing the child process being resumed by a signal). See <citerefentry
information about the child process event. The project='man-pages'><refentrytitle>waitid</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
<parameter>options</parameter> parameter determines which state further information.</para>
changes will be watched for. It must contain an OR-ed mask of
<constant>WEXITED</constant> (watch for the child process
terminating), <constant>WSTOPPED</constant> (watch for the child
process being stopped by a signal), and
<constant>WCONTINUED</constant> (watch for the child process being
resumed by a signal). See <citerefentry
project='man-pages'><refentrytitle>waitid</refentrytitle><manvolnum>2</manvolnum></citerefentry>
for further information.</para>
<para>Only a single handler may be installed for a specific <para>Only a single handler may be installed for a specific
child process. The handler is enabled for a single event child process. The handler is enabled for a single event
@ -100,6 +144,12 @@
<constant>SD_EVENT_OFF</constant> with <constant>SD_EVENT_OFF</constant> with
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para> <citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
<para>The <constant>SIGCHLD</constant> signal must be blocked in all threads before this function is
called (using <citerefentry
project='man-pages'><refentrytitle>sigprocmask</refentrytitle><manvolnum>2</manvolnum></citerefentry> or
<citerefentry
project='man-pages'><refentrytitle>pthread_sigmask</refentrytitle><manvolnum>3</manvolnum></citerefentry>).</para>
<para>If the second parameter of <para>If the second parameter of
<function>sd_event_add_child()</function> is passed as NULL no <function>sd_event_add_child()</function> is passed as NULL no
reference to the event source object is returned. In this case the reference to the event source object is returned. In this case the
@ -121,6 +171,17 @@
processed first, it should leave the child processes for which processed first, it should leave the child processes for which
child process state change event sources are installed unreaped.</para> child process state change event sources are installed unreaped.</para>
<para><function>sd_event_add_child_pidfd()</function> is similar to
<function>sd_event_add_child()</function> but takes a file descriptor referencing the process ("pidfd")
instead of the numeric PID. A suitable file descriptor may be acquired via <citerefentry
project='man-pages'><refentrytitle>pidfd_open</refentrytitle><manvolnum>2</manvolnum></citerefentry> and
related calls. The passed file descriptor is not closed when the event source is freed again, unless
<function>sd_event_source_set_child_pidfd_own()</function> is used to turn this behaviour on. Note that
regardless which of <function>sd_event_add_child()</function> and
<function>sd_event_add_child_pidfd()</function> is used for allocating an event source, the watched
process has to be a direct child process of the invoking process. Also in both cases
<constant>SIGCHLD</constant> has to be blocked in the invoking process.</para>
<para><function>sd_event_source_get_child_pid()</function> <para><function>sd_event_source_get_child_pid()</function>
retrieves the configured PID of a child process state change event retrieves the configured PID of a child process state change event
source created previously with source created previously with
@ -129,6 +190,45 @@
pointer to a <type>pid_t</type> variable to return the process ID pointer to a <type>pid_t</type> variable to return the process ID
in. in.
</para> </para>
<para><function>sd_event_source_get_child_pidfd()</function> retrieves the file descriptor referencing
the watched process ("pidfd") if this functionality is available. On kernels that support the concept the
event loop will make use of pidfds to watch child processes, regardless if the individual event sources
are allocated via <function>sd_event_add_child()</function> or
<function>sd_event_add_child_pidfd()</function>. If the latter call was used to allocate the event
source, this function returns the file descriptor used for allocation. On kernels that do not support the
pidfd concept this function will fail with <constant>EOPNOTSUPP</constant>. This call takes the event
source object as the <parameter>source</parameter> parameter and returns the numeric file descriptor.
</para>
<para><function>sd_event_source_get_child_pidfd_own()</function> may be used to query whether the pidfd
the event source encapsulates shall be closed when the event source is freed. This function returns zero
if the pidfd shall be left open, and positive if it shall be closed automatically. By default this
setting defaults to on if the event source was allocated via <function>sd_event_add_child()</function>
and off if it was allocated via <function>sd_event_add_child_pidfd()</function>. The
<function>sd_event_source_set_child_pidfd_own()</function> function may be used to change the setting and
takes a boolean parameter with the new setting.</para>
<para><function>sd_event_source_get_child_process_own()</function> may be used to query whether the
process the event source watches shall be killed (with <constant>SIGKILL</constant>) and reaped when the
event source is freed. This function returns zero if the process shell be left running, and positive if
it shall be killed and reaped automatically. By default this setting defaults to off. The
<function>sd_event_source_set_child_process_own()</function> function may be used to change the setting
and takes a boolean parameter with the new setting. Note that currently if the calling process is
terminated abnormally the watched process might survive even thought the event source ceases to
exist. This behaviour might change eventually.</para>
<para><function>sd_event_source_send_child_signal()</function> may be used to send a UNIX signal to the
watched process. If the pidfd concept is supported in the kernel, this is implemented via <citerefentry
project='man-pages'><refentrytitle>pidfd_send_signal</refentrytitle><manvolnum>2</manvolnum></citerefentry>
and otherwise via <citerefentry
project='man-pages'><refentrytitle>rt_sigqueueinfo</refentrytitle><manvolnum>2</manvolnum></citerefentry>
(or via <citerefentry
project='man-pages'><refentrytitle>kill</refentrytitle><manvolnum>2</manvolnum></citerefentry> in case
<parameter>info</parameter> is <constant>NULL</constant>). The specified parameters match those of these
underlying system calls, except that the <parameter>info</parameter> is never modified (and is thus
declared constant). Like for the underlying system calls, the <parameter>flags</parameter> parameter
currently must be zero.</para>
</refsect1> </refsect1>
<refsect1> <refsect1>
@ -165,8 +265,8 @@
<varlistentry> <varlistentry>
<term><constant>-EBUSY</constant></term> <term><constant>-EBUSY</constant></term>
<listitem><para>A handler is already installed for this <listitem><para>A handler is already installed for this child process, or
child process.</para></listitem> <constant>SIGCHLD</constant> is not blocked.</para></listitem>
</varlistentry> </varlistentry>
@ -190,6 +290,12 @@
<listitem><para>The passed event source is not a child process event source.</para></listitem> <listitem><para>The passed event source is not a child process event source.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><constant>-EOPNOTSUPP</constant></term>
<listitem><para>A pidfd was requested but the kernel does not support this concept.</para></listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect2> </refsect2>
</refsect1> </refsect1>
@ -214,7 +320,13 @@
<citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_source_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>waitid</refentrytitle><manvolnum>2</manvolnum></citerefentry> <citerefentry project='man-pages'><refentrytitle>waitid</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>sigprocmask</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>pthread_sigmask</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>pidfd_open</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>pidfd_send_signal</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>rt_sigqueueinfo</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>kill</refentrytitle><manvolnum>2</manvolnum></citerefentry>
</para> </para>
</refsect1> </refsect1>

View File

@ -75,14 +75,13 @@
project='man-pages'><refentrytitle>signalfd</refentrytitle><manvolnum>2</manvolnum></citerefentry> project='man-pages'><refentrytitle>signalfd</refentrytitle><manvolnum>2</manvolnum></citerefentry>
for further information.</para> for further information.</para>
<para>Only a single handler may be installed for a specific <para>Only a single handler may be installed for a specific signal. The signal must be blocked in all
signal. The signal will be unblocked by this call, and must be threads before this function is called (using <citerefentry
blocked before this function is called in all threads (using project='man-pages'><refentrytitle>sigprocmask</refentrytitle><manvolnum>2</manvolnum></citerefentry> or
<citerefentry <citerefentry
project='man-pages'><refentrytitle>sigprocmask</refentrytitle><manvolnum>2</manvolnum></citerefentry>). If project='man-pages'><refentrytitle>pthread_sigmask</refentrytitle><manvolnum>3</manvolnum></citerefentry>). If
the handler is not specified (<parameter>handler</parameter> is the handler is not specified (<parameter>handler</parameter> is <constant>NULL</constant>), a default
<constant>NULL</constant>), a default handler which causes the handler which causes the program to exit cleanly will be used.</para>
program to exit cleanly will be used.</para>
<para>By default, the event source is enabled permanently <para>By default, the event source is enabled permanently
(<constant>SD_EVENT_ON</constant>), but this may be changed with (<constant>SD_EVENT_ON</constant>), but this may be changed with
@ -189,7 +188,9 @@
<citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_source_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_source_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>signalfd</refentrytitle><manvolnum>2</manvolnum></citerefentry> <citerefentry project='man-pages'><refentrytitle>signalfd</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>sigprocmask</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>pthread_sigmask</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para> </para>
</refsect1> </refsect1>

View File

@ -38,6 +38,7 @@
<filename>hibernate.target</filename>, <filename>hibernate.target</filename>,
<filename>hybrid-sleep.target</filename>, <filename>hybrid-sleep.target</filename>,
<filename>suspend-then-hibernate.target</filename>, <filename>suspend-then-hibernate.target</filename>,
<filename>initrd.target</filename>,
<filename>initrd-fs.target</filename>, <filename>initrd-fs.target</filename>,
<filename>initrd-root-device.target</filename>, <filename>initrd-root-device.target</filename>,
<filename>initrd-root-fs.target</filename>, <filename>initrd-root-fs.target</filename>,
@ -202,14 +203,16 @@
<varlistentry> <varlistentry>
<term><filename>default.target</filename></term> <term><filename>default.target</filename></term>
<listitem> <listitem>
<para>The default unit systemd starts at bootup. Usually, <para>The default unit systemd starts at bootup. Usually, this should be aliased (symlinked) to
this should be aliased (symlinked) to <filename>multi-user.target</filename> or <filename>graphical.target</filename>. See
<filename>multi-user.target</filename> or <citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
<filename>graphical.target</filename>.</para> more discussion.</para>
<para>The default unit systemd starts at bootup can be <para>The default unit systemd starts at bootup can be overridden with the
overridden with the <varname>systemd.unit=</varname> kernel <varname>systemd.unit=</varname> kernel command line option, or more conveniently, with the short
command line option.</para> names like <varname>single</varname>, <varname>rescue</varname>, <varname>1</varname>,
<varname>3</varname>, <varname>5</varname>, …; see
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
@ -341,6 +344,15 @@
is active as long as the system is running.</para> is active as long as the system is running.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><filename>initrd.target</filename></term>
<listitem>
<para>This is the default target in the initramfs, similar to <filename>default.target</filename>
in the main system. It is used to mount the real root and transition to it. See
<citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
more discussion.</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><filename>initrd-fs.target</filename></term> <term><filename>initrd-fs.target</filename></term>
<listitem> <listitem>

View File

@ -517,6 +517,18 @@ foreach ident : [
#include <unistd.h>'''], #include <unistd.h>'''],
['get_mempolicy', '''#include <stdlib.h> ['get_mempolicy', '''#include <stdlib.h>
#include <unistd.h>'''], #include <unistd.h>'''],
['pidfd_send_signal', '''#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>'''],
['pidfd_open', '''#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>'''],
['rt_sigqueueinfo', '''#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>'''],
] ]
have = cc.has_function(ident[0], prefix : ident[1], args : '-D_GNU_SOURCE') have = cc.has_function(ident[0], prefix : ident[1], args : '-D_GNU_SOURCE')

View File

@ -86,21 +86,18 @@ unsigned long cap_last_cap(void) {
int capability_update_inherited_set(cap_t caps, uint64_t set) { int capability_update_inherited_set(cap_t caps, uint64_t set) {
unsigned long i; unsigned long i;
/* Add capabilities in the set to the inherited caps. Do not apply /* Add capabilities in the set to the inherited caps, drops capabilities not in the set.
* them yet. */ * Do not apply them yet. */
for (i = 0; i <= cap_last_cap(); i++) { for (i = 0; i <= cap_last_cap(); i++) {
cap_flag_value_t flag = set & (UINT64_C(1) << i) ? CAP_SET : CAP_CLEAR;
if (set & (UINT64_C(1) << i)) {
cap_value_t v; cap_value_t v;
v = (cap_value_t) i; v = (cap_value_t) i;
/* Make the capability inheritable. */ if (cap_set_flag(caps, CAP_INHERITABLE, 1, &v, flag) < 0)
if (cap_set_flag(caps, CAP_INHERITABLE, 1, &v, CAP_SET) < 0)
return -errno; return -errno;
} }
}
return 0; return 0;
} }
@ -132,6 +129,17 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
/* Add the capability to the ambient set. */ /* Add the capability to the ambient set. */
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0) if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0)
return -errno; return -errno;
} else {
/* Drop the capability so we don't inherit capabilities we didn't ask for. */
r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, i, 0, 0);
if (r < 0)
return -errno;
if (r)
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, i, 0, 0) < 0)
return -errno;
} }
} }

View File

@ -21,6 +21,7 @@
#include "missing_syscall.h" #include "missing_syscall.h"
#include "mountpoint-util.h" #include "mountpoint-util.h"
#include "nulstr-util.h" #include "nulstr-util.h"
#include "selinux-util.h"
#include "stat-util.h" #include "stat-util.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
@ -372,7 +373,15 @@ static int fd_copy_symlink(
if (r < 0) if (r < 0)
return r; return r;
if (symlinkat(target, dt, to) < 0) if (copy_flags & COPY_MAC_CREATE) {
r = mac_selinux_create_file_prepare_at(dt, to, S_IFLNK);
if (r < 0)
return r;
}
r = symlinkat(target, dt, to);
if (copy_flags & COPY_MAC_CREATE)
mac_selinux_create_file_clear();
if (r < 0)
return -errno; return -errno;
if (fchownat(dt, to, if (fchownat(dt, to,
@ -408,7 +417,14 @@ static int fd_copy_regular(
if (fdf < 0) if (fdf < 0)
return -errno; return -errno;
if (copy_flags & COPY_MAC_CREATE) {
r = mac_selinux_create_file_prepare_at(dt, to, S_IFREG);
if (r < 0)
return r;
}
fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777); fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777);
if (copy_flags & COPY_MAC_CREATE)
mac_selinux_create_file_clear();
if (fdt < 0) if (fdt < 0)
return -errno; return -errno;
@ -457,7 +473,14 @@ static int fd_copy_fifo(
assert(st); assert(st);
assert(to); assert(to);
if (copy_flags & COPY_MAC_CREATE) {
r = mac_selinux_create_file_prepare_at(dt, to, S_IFIFO);
if (r < 0)
return r;
}
r = mkfifoat(dt, to, st->st_mode & 07777); r = mkfifoat(dt, to, st->st_mode & 07777);
if (copy_flags & COPY_MAC_CREATE)
mac_selinux_create_file_clear();
if (r < 0) if (r < 0)
return -errno; return -errno;
@ -488,7 +511,14 @@ static int fd_copy_node(
assert(st); assert(st);
assert(to); assert(to);
if (copy_flags & COPY_MAC_CREATE) {
r = mac_selinux_create_file_prepare_at(dt, to, st->st_mode & S_IFMT);
if (r < 0)
return r;
}
r = mknodat(dt, to, st->st_mode, st->st_rdev); r = mknodat(dt, to, st->st_mode, st->st_rdev);
if (copy_flags & COPY_MAC_CREATE)
mac_selinux_create_file_clear();
if (r < 0) if (r < 0)
return -errno; return -errno;
@ -556,6 +586,9 @@ static int fd_copy_directory(
if (exists) if (exists)
created = false; created = false;
else { else {
if (copy_flags & COPY_MAC_CREATE)
r = mkdirat_label(dt, to, st->st_mode & 07777);
else
r = mkdirat(dt, to, st->st_mode & 07777); r = mkdirat(dt, to, st->st_mode & 07777);
if (r >= 0) if (r >= 0)
created = true; created = true;
@ -796,7 +829,14 @@ int copy_file_full(
assert(to); assert(to);
RUN_WITH_UMASK(0000) { RUN_WITH_UMASK(0000) {
if (copy_flags & COPY_MAC_CREATE) {
r = mac_selinux_create_file_prepare(to, S_IFREG);
if (r < 0)
return r;
}
fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode); fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
if (copy_flags & COPY_MAC_CREATE)
mac_selinux_create_file_clear();
if (fdt < 0) if (fdt < 0)
return -errno; return -errno;
} }
@ -850,13 +890,29 @@ int copy_file_atomic_full(
if (r < 0) if (r < 0)
return r; return r;
if (copy_flags & COPY_MAC_CREATE) {
r = mac_selinux_create_file_prepare(to, S_IFREG);
if (r < 0) {
t = mfree(t);
return r;
}
}
fdt = open(t, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|O_WRONLY|O_CLOEXEC, 0600); fdt = open(t, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|O_WRONLY|O_CLOEXEC, 0600);
if (copy_flags & COPY_MAC_CREATE)
mac_selinux_create_file_clear();
if (fdt < 0) { if (fdt < 0) {
t = mfree(t); t = mfree(t);
return -errno; return -errno;
} }
} else { } else {
if (copy_flags & COPY_MAC_CREATE) {
r = mac_selinux_create_file_prepare(to, S_IFREG);
if (r < 0)
return r;
}
fdt = open_tmpfile_linkable(to, O_WRONLY|O_CLOEXEC, &t); fdt = open_tmpfile_linkable(to, O_WRONLY|O_CLOEXEC, &t);
if (copy_flags & COPY_MAC_CREATE)
mac_selinux_create_file_clear();
if (fdt < 0) if (fdt < 0)
return fdt; return fdt;
} }

View File

@ -16,6 +16,7 @@ typedef enum CopyFlags {
COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */ COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */
COPY_CRTIME = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */ COPY_CRTIME = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */
COPY_SIGINT = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */ COPY_SIGINT = 1 << 6, /* Check for SIGINT regularly and return EINTR if seen (caller needs to block SIGINT) */
COPY_MAC_CREATE = 1 << 7, /* Create files with the correct MAC label (currently SELinux only) */
} CopyFlags; } CopyFlags;
typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata); typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata);

View File

@ -101,3 +101,11 @@ static inline bool ERRNO_IS_PRIVILEGE(int r) {
EACCES, EACCES,
EPERM); EPERM);
} }
/* Three difference errors for "not enough disk space" */
static inline bool ERRNO_IS_DISK_SPACE(int r) {
return IN_SET(abs(r),
ENOSPC,
EDQUOT,
EFBIG);
}

View File

@ -80,14 +80,21 @@ static inline void* explicit_bzero_safe(void *p, size_t l) {
void *explicit_bzero_safe(void *p, size_t l); void *explicit_bzero_safe(void *p, size_t l);
#endif #endif
static inline void erase_and_freep(void *p) { static inline void* erase_and_free(void *p) {
void *ptr = *(void**) p; size_t l;
if (ptr) { if (!p)
size_t l = malloc_usable_size(ptr); return NULL;
explicit_bzero_safe(ptr, l);
free(ptr); l = malloc_usable_size(p);
explicit_bzero_safe(p, l);
free(p);
return NULL;
} }
static inline void erase_and_freep(void *p) {
erase_and_free(*(void**) p);
} }
/* Use with _cleanup_ to erase a single 'char' when leaving scope */ /* Use with _cleanup_ to erase a single 'char' when leaving scope */

View File

@ -5,8 +5,10 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <signal.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#ifdef ARCH_MIPS #ifdef ARCH_MIPS
@ -524,3 +526,45 @@ static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask,
#define get_mempolicy missing_get_mempolicy #define get_mempolicy missing_get_mempolicy
#endif #endif
#if !HAVE_PIDFD_OPEN
/* may be (invalid) negative number due to libseccomp, see PR 13319 */
# if ! (defined __NR_pidfd_open && __NR_pidfd_open > 0)
# if defined __NR_pidfd_open
# undef __NR_pidfd_open
# endif
# define __NR_pidfd_open 434
#endif
static inline int pidfd_open(pid_t pid, unsigned flags) {
#ifdef __NR_pidfd_open
return syscall(__NR_pidfd_open, pid, flags);
#else
errno = ENOSYS;
return -1;
#endif
}
#endif
#if !HAVE_PIDFD_SEND_SIGNAL
/* may be (invalid) negative number due to libseccomp, see PR 13319 */
# if ! (defined __NR_pidfd_send_signal && __NR_pidfd_send_signal > 0)
# if defined __NR_pidfd_send_signal
# undef __NR_pidfd_send_signal
# endif
# define __NR_pidfd_send_signal 424
#endif
static inline int pidfd_send_signal(int fd, int sig, siginfo_t *info, unsigned flags) {
#ifdef __NR_pidfd_open
return syscall(__NR_pidfd_send_signal, fd, sig, info, flags);
#else
errno = ENOSYS;
return -1;
#endif
}
#endif
#if !HAVE_RT_SIGQUEUEINFO
static inline int rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *info) {
return syscall(__NR_rt_sigqueueinfo, tgid, sig, info);
}
#endif

View File

@ -50,6 +50,10 @@ static inline void* ordered_set_remove(OrderedSet *s, void *p) {
return ordered_hashmap_remove((OrderedHashmap*) s, p); return ordered_hashmap_remove((OrderedHashmap*) s, p);
} }
static inline void* ordered_set_first(OrderedSet *s) {
return ordered_hashmap_first((OrderedHashmap*) s);
}
static inline void* ordered_set_steal_first(OrderedSet *s) { static inline void* ordered_set_steal_first(OrderedSet *s) {
return ordered_hashmap_steal_first((OrderedHashmap*) s); return ordered_hashmap_steal_first((OrderedHashmap*) s);
} }

View File

@ -365,7 +365,6 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
unsigned long l; unsigned long l;
assert(s); assert(s);
assert(ret_u);
assert(base <= 16); assert(base <= 16);
/* strtoul() is happy to parse negative values, and silently /* strtoul() is happy to parse negative values, and silently
@ -389,7 +388,9 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
if ((unsigned long) (unsigned) l != l) if ((unsigned long) (unsigned) l != l)
return -ERANGE; return -ERANGE;
if (ret_u)
*ret_u = (unsigned) l; *ret_u = (unsigned) l;
return 0; return 0;
} }
@ -398,7 +399,6 @@ int safe_atoi(const char *s, int *ret_i) {
long l; long l;
assert(s); assert(s);
assert(ret_i);
errno = 0; errno = 0;
l = strtol(s, &x, 0); l = strtol(s, &x, 0);
@ -409,7 +409,9 @@ int safe_atoi(const char *s, int *ret_i) {
if ((long) (int) l != l) if ((long) (int) l != l)
return -ERANGE; return -ERANGE;
if (ret_i)
*ret_i = (int) l; *ret_i = (int) l;
return 0; return 0;
} }
@ -418,7 +420,6 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) {
unsigned long long l; unsigned long long l;
assert(s); assert(s);
assert(ret_llu);
s += strspn(s, WHITESPACE); s += strspn(s, WHITESPACE);
@ -431,7 +432,9 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) {
if (*s == '-') if (*s == '-')
return -ERANGE; return -ERANGE;
if (ret_llu)
*ret_llu = l; *ret_llu = l;
return 0; return 0;
} }
@ -440,7 +443,6 @@ int safe_atolli(const char *s, long long int *ret_lli) {
long long l; long long l;
assert(s); assert(s);
assert(ret_lli);
errno = 0; errno = 0;
l = strtoll(s, &x, 0); l = strtoll(s, &x, 0);
@ -449,7 +451,9 @@ int safe_atolli(const char *s, long long int *ret_lli) {
if (!x || x == s || *x != 0) if (!x || x == s || *x != 0)
return -EINVAL; return -EINVAL;
if (ret_lli)
*ret_lli = l; *ret_lli = l;
return 0; return 0;
} }
@ -458,7 +462,6 @@ int safe_atou8(const char *s, uint8_t *ret) {
unsigned long l; unsigned long l;
assert(s); assert(s);
assert(ret);
s += strspn(s, WHITESPACE); s += strspn(s, WHITESPACE);
@ -473,6 +476,7 @@ int safe_atou8(const char *s, uint8_t *ret) {
if ((unsigned long) (uint8_t) l != l) if ((unsigned long) (uint8_t) l != l)
return -ERANGE; return -ERANGE;
if (ret)
*ret = (uint8_t) l; *ret = (uint8_t) l;
return 0; return 0;
} }
@ -507,7 +511,6 @@ int safe_atoi16(const char *s, int16_t *ret) {
long l; long l;
assert(s); assert(s);
assert(ret);
errno = 0; errno = 0;
l = strtol(s, &x, 0); l = strtol(s, &x, 0);
@ -518,7 +521,9 @@ int safe_atoi16(const char *s, int16_t *ret) {
if ((long) (int16_t) l != l) if ((long) (int16_t) l != l)
return -ERANGE; return -ERANGE;
if (ret)
*ret = (int16_t) l; *ret = (int16_t) l;
return 0; return 0;
} }
@ -528,7 +533,6 @@ int safe_atod(const char *s, double *ret_d) {
double d = 0; double d = 0;
assert(s); assert(s);
assert(ret_d);
loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0); loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
if (loc == (locale_t) 0) if (loc == (locale_t) 0)
@ -541,7 +545,9 @@ int safe_atod(const char *s, double *ret_d) {
if (!x || x == s || *x != 0) if (!x || x == s || *x != 0)
return -EINVAL; return -EINVAL;
if (ret_d)
*ret_d = (double) d; *ret_d = (double) d;
return 0; return 0;
} }

View File

@ -40,6 +40,7 @@
#include "rlimit-util.h" #include "rlimit-util.h"
#include "signal-util.h" #include "signal-util.h"
#include "stat-util.h" #include "stat-util.h"
#include "stdio-util.h"
#include "string-table.h" #include "string-table.h"
#include "string-util.h" #include "string-util.h"
#include "terminal-util.h" #include "terminal-util.h"
@ -1337,6 +1338,13 @@ int safe_fork_full(
log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m"); log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m");
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
} else if (flags & FORK_STDOUT_TO_STDERR) {
if (dup2(STDERR_FILENO, STDOUT_FILENO) < 0) {
log_full_errno(prio, r, "Failed to connect stdout to stderr: %m");
_exit(EXIT_FAILURE);
}
} }
if (flags & FORK_RLIMIT_NOFILE_SAFE) { if (flags & FORK_RLIMIT_NOFILE_SAFE) {
@ -1488,6 +1496,38 @@ int set_oom_score_adjust(int value) {
WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER); WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
} }
int pidfd_get_pid(int fd, pid_t *ret) {
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *fdinfo = NULL;
char *p;
int r;
if (fd < 0)
return -EBADF;
xsprintf(path, "/proc/self/fdinfo/%i", fd);
r = read_full_file(path, &fdinfo, NULL);
if (r == -ENOENT) /* if fdinfo doesn't exist we assume the process does not exist */
return -ESRCH;
if (r < 0)
return r;
p = startswith(fdinfo, "Pid:");
if (!p) {
p = strstr(fdinfo, "\nPid:");
if (!p)
return -ENOTTY; /* not a pidfd? */
p += 5;
}
p += strspn(p, WHITESPACE);
p[strcspn(p, WHITESPACE)] = 0;
return parse_pid(p, ret);
}
static const char *const ioprio_class_table[] = { static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime", [IOPRIO_CLASS_RT] = "realtime",

View File

@ -157,6 +157,7 @@ typedef enum ForkFlags {
FORK_NEW_MOUNTNS = 1 << 7, /* Run child in its own mount namespace */ FORK_NEW_MOUNTNS = 1 << 7, /* Run child in its own mount namespace */
FORK_MOUNTNS_SLAVE = 1 << 8, /* Make child's mount namespace MS_SLAVE */ FORK_MOUNTNS_SLAVE = 1 << 8, /* Make child's mount namespace MS_SLAVE */
FORK_RLIMIT_NOFILE_SAFE = 1 << 9, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */ FORK_RLIMIT_NOFILE_SAFE = 1 << 9, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
FORK_STDOUT_TO_STDERR = 1 << 10, /* Make stdout a copy of stderr */
} ForkFlags; } ForkFlags;
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
@ -197,3 +198,5 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX);
(pid) = 0; \ (pid) = 0; \
_pid_; \ _pid_; \
}) })
int pidfd_get_pid(int fd, pid_t *ret);

View File

@ -107,6 +107,26 @@ void mac_selinux_finish(void) {
#endif #endif
} }
void mac_selinux_reload(void) {
#if HAVE_SELINUX
struct selabel_handle *backup_label_hnd;
if (!label_hnd)
return;
backup_label_hnd = TAKE_PTR(label_hnd);
/* try to initialize new handle
* on success close backup
* on failure restore backup */
if (mac_selinux_init() == 0)
selabel_close(backup_label_hnd);
else
label_hnd = backup_label_hnd;
#endif
}
int mac_selinux_fix(const char *path, LabelFixFlags flags) { int mac_selinux_fix(const char *path, LabelFixFlags flags) {
#if HAVE_SELINUX #if HAVE_SELINUX

View File

@ -13,6 +13,7 @@ void mac_selinux_retest(void);
int mac_selinux_init(void); int mac_selinux_init(void);
void mac_selinux_finish(void); void mac_selinux_finish(void);
void mac_selinux_reload(void);
int mac_selinux_fix(const char *path, LabelFixFlags flags); int mac_selinux_fix(const char *path, LabelFixFlags flags);
int mac_selinux_apply(const char *path, const char *label); int mac_selinux_apply(const char *path, const char *label);

View File

@ -287,3 +287,18 @@ int signal_from_string(const char *s) {
void nop_signal_handler(int sig) { void nop_signal_handler(int sig) {
/* nothing here */ /* nothing here */
} }
int signal_is_blocked(int sig) {
sigset_t ss;
int r;
r = pthread_sigmask(SIG_SETMASK, NULL, &ss);
if (r != 0)
return -r;
r = sigismember(&ss, sig);
if (r < 0)
return -errno;
return r;
}

View File

@ -41,3 +41,5 @@ static inline const char* signal_to_string_with_check(int n) {
return signal_to_string(n); return signal_to_string(n);
} }
int signal_is_blocked(int sig);

View File

@ -2,6 +2,7 @@
#pragma once #pragma once
#define SPECIAL_DEFAULT_TARGET "default.target" #define SPECIAL_DEFAULT_TARGET "default.target"
#define SPECIAL_INITRD_TARGET "initrd.target"
/* Shutdown targets */ /* Shutdown targets */
#define SPECIAL_UMOUNT_TARGET "umount.target" #define SPECIAL_UMOUNT_TARGET "umount.target"

View File

@ -1064,3 +1064,13 @@ bool string_is_safe(const char *p) {
return true; return true;
} }
char* string_erase(char *x) {
if (!x)
return NULL;
/* A delicious drop of snake-oil! To be called on memory where we stored passphrases or so, after we
* used them. */
explicit_bzero_safe(x, strlen(x));
return x;
}

View File

@ -278,3 +278,5 @@ static inline char* str_realloc(char **p) {
return (*p = t); return (*p = t);
} }
char* string_erase(char *x);

View File

@ -19,49 +19,59 @@
#include "tmpfile-util.h" #include "tmpfile-util.h"
#include "umask-util.h" #include "umask-util.h"
int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) {
FILE *f; _cleanup_fclose_ FILE *f = NULL;
char *t; _cleanup_free_ char *t = NULL;
int r, fd; _cleanup_close_ int fd = -1;
int r;
assert(path);
assert(_f);
assert(_temp_path);
if (path) {
r = tempfn_xxxxxx(path, NULL, &t); r = tempfn_xxxxxx(path, NULL, &t);
if (r < 0) if (r < 0)
return r; return r;
} else {
const char *d;
r = tmp_dir(&d);
if (r < 0)
return r;
t = path_join(d, "XXXXXX");
if (!t)
return -ENOMEM;
}
fd = mkostemp_safe(t); fd = mkostemp_safe(t);
if (fd < 0) { if (fd < 0)
free(t);
return -errno; return -errno;
}
/* This assumes that returned FILE object is short-lived and used within the same single-threaded /* This assumes that returned FILE object is short-lived and used within the same single-threaded
* context and never shared externally, hence locking is not necessary. */ * context and never shared externally, hence locking is not necessary. */
r = fdopen_unlocked(fd, "w", &f); r = fdopen_unlocked(fd, "w", &f);
if (r < 0) { if (r < 0) {
unlink(t); (void) unlink(t);
free(t);
safe_close(fd);
return r; return r;
} }
*_f = f; TAKE_FD(fd);
*_temp_path = t;
if (ret_f)
*ret_f = TAKE_PTR(f);
if (ret_temp_path)
*ret_temp_path = TAKE_PTR(t);
return 0; return 0;
} }
/* This is much like mkostemp() but is subject to umask(). */ /* This is much like mkostemp() but is subject to umask(). */
int mkostemp_safe(char *pattern) { int mkostemp_safe(char *pattern) {
_unused_ _cleanup_umask_ mode_t u = umask(0077);
int fd; int fd;
assert(pattern); assert(pattern);
RUN_WITH_UMASK(0077)
fd = mkostemp(pattern, O_CLOEXEC); fd = mkostemp(pattern, O_CLOEXEC);
if (fd < 0) if (fd < 0)
return -errno; return -errno;

View File

@ -84,7 +84,7 @@ char *getusername_malloc(void) {
return uid_to_name(getuid()); return uid_to_name(getuid());
} }
static bool is_nologin_shell(const char *shell) { bool is_nologin_shell(const char *shell) {
return PATH_IN_SET(shell, return PATH_IN_SET(shell,
/* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice

View File

@ -57,6 +57,14 @@ int take_etc_passwd_lock(const char *root);
#define ETC_PASSWD_LOCK_PATH "/etc/.pwd.lock" #define ETC_PASSWD_LOCK_PATH "/etc/.pwd.lock"
static inline bool uid_is_system(uid_t uid) {
return uid <= SYSTEM_UID_MAX;
}
static inline bool gid_is_system(gid_t gid) {
return gid <= SYSTEM_GID_MAX;
}
static inline bool uid_is_dynamic(uid_t uid) { static inline bool uid_is_dynamic(uid_t uid) {
return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX; return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX;
} }
@ -65,12 +73,12 @@ static inline bool gid_is_dynamic(gid_t gid) {
return uid_is_dynamic((uid_t) gid); return uid_is_dynamic((uid_t) gid);
} }
static inline bool uid_is_system(uid_t uid) { static inline bool uid_is_container(uid_t uid) {
return uid <= SYSTEM_UID_MAX; return CONTAINER_UID_BASE_MIN <= uid && uid <= CONTAINER_UID_BASE_MAX;
} }
static inline bool gid_is_system(gid_t gid) { static inline bool gid_is_container(gid_t gid) {
return gid <= SYSTEM_GID_MAX; return uid_is_container((uid_t) gid);
} }
/* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer /* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer
@ -127,3 +135,5 @@ int putsgent_sane(const struct sgrp *sg, FILE *stream);
#endif #endif
int make_salt(char **ret); int make_salt(char **ret);
bool is_nologin_shell(const char *shell);

View File

@ -32,7 +32,7 @@ enum loader_type {
}; };
typedef struct { typedef struct {
CHAR16 *id; /* The identifier for this entry (note that this id is not necessarily unique though!) */ CHAR16 *id; /* The unique identifier for this entry */
CHAR16 *title_show; CHAR16 *title_show;
CHAR16 *title; CHAR16 *title;
CHAR16 *version; CHAR16 *version;
@ -1310,7 +1310,6 @@ static VOID config_entry_add_from_file(
CHAR8 *line; CHAR8 *line;
UINTN pos = 0; UINTN pos = 0;
CHAR8 *key, *value; CHAR8 *key, *value;
UINTN len;
EFI_STATUS err; EFI_STATUS err;
EFI_FILE_HANDLE handle; EFI_FILE_HANDLE handle;
_cleanup_freepool_ CHAR16 *initrd = NULL; _cleanup_freepool_ CHAR16 *initrd = NULL;
@ -1431,10 +1430,6 @@ static VOID config_entry_add_from_file(
entry->device = device; entry->device = device;
entry->id = StrDuplicate(file); entry->id = StrDuplicate(file);
len = StrLen(entry->id);
/* remove ".conf" */
if (len > 5)
entry->id[len - 5] = '\0';
StrLwr(entry->id); StrLwr(entry->id);
config_add_entry(config, entry); config_add_entry(config, entry);
@ -1775,7 +1770,8 @@ static ConfigEntry *config_entry_add_loader(
CHAR16 *id, CHAR16 *id,
CHAR16 key, CHAR16 key,
CHAR16 *title, CHAR16 *title,
CHAR16 *loader) { CHAR16 *loader,
CHAR16 *version) {
ConfigEntry *entry; ConfigEntry *entry;
@ -1783,6 +1779,7 @@ static ConfigEntry *config_entry_add_loader(
*entry = (ConfigEntry) { *entry = (ConfigEntry) {
.type = type, .type = type,
.title = StrDuplicate(title), .title = StrDuplicate(title),
.version = StrDuplicate(version),
.device = device, .device = device,
.loader = StrDuplicate(loader), .loader = StrDuplicate(loader),
.id = StrDuplicate(id), .id = StrDuplicate(id),
@ -1840,7 +1837,7 @@ static BOOLEAN config_entry_add_loader_auto(
return FALSE; return FALSE;
uefi_call_wrapper(handle->Close, 1, handle); uefi_call_wrapper(handle->Close, 1, handle);
entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, id, key, title, loader); entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, id, key, title, loader, NULL);
if (!entry) if (!entry)
return FALSE; return FALSE;
@ -1908,10 +1905,12 @@ static VOID config_entry_add_linux(
CHAR8 *line; CHAR8 *line;
UINTN pos = 0; UINTN pos = 0;
CHAR8 *key, *value; CHAR8 *key, *value;
CHAR16 *os_name_pretty = NULL;
CHAR16 *os_name = NULL; CHAR16 *os_name = NULL;
CHAR16 *os_id = NULL; CHAR16 *os_id = NULL;
CHAR16 *os_version = NULL; CHAR16 *os_version = NULL;
CHAR16 *os_build = NULL; CHAR16 *os_version_id = NULL;
CHAR16 *os_build_id = NULL;
err = uefi_call_wrapper(linux_dir->Read, 3, linux_dir, &bufsize, buf); err = uefi_call_wrapper(linux_dir->Read, 3, linux_dir, &bufsize, buf);
if (bufsize == 0 || EFI_ERROR(err)) if (bufsize == 0 || EFI_ERROR(err))
@ -1927,6 +1926,8 @@ static VOID config_entry_add_linux(
continue; continue;
if (StriCmp(f->FileName + len - 4, L".efi") != 0) if (StriCmp(f->FileName + len - 4, L".efi") != 0)
continue; continue;
if (StrnCmp(f->FileName, L"auto-", 5) == 0)
continue;
/* look for .osrel and .cmdline sections in the .efi binary */ /* look for .osrel and .cmdline sections in the .efi binary */
err = pe_file_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs); err = pe_file_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs);
@ -1940,6 +1941,12 @@ static VOID config_entry_add_linux(
/* read properties from the embedded os-release file */ /* read properties from the embedded os-release file */
while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) { while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) {
if (strcmpa((CHAR8 *)"PRETTY_NAME", key) == 0) { if (strcmpa((CHAR8 *)"PRETTY_NAME", key) == 0) {
FreePool(os_name_pretty);
os_name_pretty = stra_to_str(value);
continue;
}
if (strcmpa((CHAR8 *)"NAME", key) == 0) {
FreePool(os_name); FreePool(os_name);
os_name = stra_to_str(value); os_name = stra_to_str(value);
continue; continue;
@ -1957,20 +1964,27 @@ static VOID config_entry_add_linux(
continue; continue;
} }
if (strcmpa((CHAR8 *)"VERSION_ID", key) == 0) {
FreePool(os_version_id);
os_version_id = stra_to_str(value);
continue;
}
if (strcmpa((CHAR8 *)"BUILD_ID", key) == 0) { if (strcmpa((CHAR8 *)"BUILD_ID", key) == 0) {
FreePool(os_build); FreePool(os_build_id);
os_build = stra_to_str(value); os_build_id = stra_to_str(value);
continue; continue;
} }
} }
if (os_name && os_id && (os_version || os_build)) { if ((os_name_pretty || os_name) && os_id && (os_version || os_version_id || os_build_id)) {
_cleanup_freepool_ CHAR16 *conf = NULL, *path = NULL; _cleanup_freepool_ CHAR16 *path = NULL;
conf = PoolPrint(L"%s-%s", os_id, os_version ? : os_build);
path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName); path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName);
entry = config_entry_add_loader(config, device, LOADER_LINUX, conf, 'l', os_name, path); entry = config_entry_add_loader(config, device, LOADER_LINUX, f->FileName, 'l',
os_name_pretty ? : (os_name ? : os_id), path,
os_version ? : (os_version_id ? : os_build_id));
FreePool(content); FreePool(content);
content = NULL; content = NULL;
@ -1989,10 +2003,12 @@ static VOID config_entry_add_linux(
config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi"); config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi");
} }
FreePool(os_name_pretty);
FreePool(os_name); FreePool(os_name);
FreePool(os_id); FreePool(os_id);
FreePool(os_version); FreePool(os_version);
FreePool(os_build); FreePool(os_version_id);
FreePool(os_build_id);
FreePool(content); FreePool(content);
} }
@ -2453,6 +2469,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
UINT64 key; UINT64 key;
err = console_key_read(&key, FALSE); err = console_key_read(&key, FALSE);
if (err == EFI_NOT_READY) {
uefi_call_wrapper(BS->Stall, 1, 100 * 1000);
err = console_key_read(&key, FALSE);
}
if (!EFI_ERROR(err)) { if (!EFI_ERROR(err)) {
INT16 idx; INT16 idx;

View File

@ -3595,8 +3595,7 @@ static int exec_child(
/* This is done before enforce_user, but ambient set /* This is done before enforce_user, but ambient set
* does not survive over setresuid() if keep_caps is not set. */ * does not survive over setresuid() if keep_caps is not set. */
if (!needs_ambient_hack && if (!needs_ambient_hack) {
context->capability_ambient_set != 0) {
r = capability_ambient_set_apply(context->capability_ambient_set, true); r = capability_ambient_set_apply(context->capability_ambient_set, true);
if (r < 0) { if (r < 0) {
*exit_status = EXIT_CAPABILITIES; *exit_status = EXIT_CAPABILITIES;

View File

@ -475,6 +475,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (arg_default_timeout_start_usec <= 0) if (arg_default_timeout_start_usec <= 0)
arg_default_timeout_start_usec = USEC_INFINITY; arg_default_timeout_start_usec = USEC_INFINITY;
} else if (proc_cmdline_key_streq(key, "systemd.cpu_affinity")) {
if (proc_cmdline_value_missing(key, value))
return 0;
r = parse_cpu_set(value, &arg_cpu_affinity);
if (r < 0)
log_warning_errno(r, "Faile to parse CPU affinity mask '%s', ignoring: %m", value);
} else if (proc_cmdline_key_streq(key, "systemd.watchdog_device")) { } else if (proc_cmdline_key_streq(key, "systemd.watchdog_device")) {
if (proc_cmdline_value_missing(key, value)) if (proc_cmdline_value_missing(key, value))
@ -1738,6 +1747,8 @@ static int invoke_main_loop(
saved_log_level = m->log_level_overridden ? log_get_max_level() : -1; saved_log_level = m->log_level_overridden ? log_get_max_level() : -1;
saved_log_target = m->log_target_overridden ? log_get_target() : _LOG_TARGET_INVALID; saved_log_target = m->log_target_overridden ? log_get_target() : _LOG_TARGET_INVALID;
mac_selinux_reload();
(void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock); (void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock);
set_manager_defaults(m); set_manager_defaults(m);
@ -1986,20 +1997,36 @@ static int do_queue_default_job(
const char **ret_error_message) { const char **ret_error_message) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
const char* default_unit;
Job *default_unit_job; Job *default_unit_job;
Unit *target = NULL; Unit *target = NULL;
int r; int r;
log_debug("Activating default unit: %s", arg_default_unit); if (arg_default_unit)
default_unit = arg_default_unit;
else if (in_initrd())
default_unit = SPECIAL_INITRD_TARGET;
else
default_unit = SPECIAL_DEFAULT_TARGET;
r = manager_load_startable_unit_or_warn(m, arg_default_unit, NULL, &target); log_debug("Activating default unit: %s", default_unit);
r = manager_load_startable_unit_or_warn(m, default_unit, NULL, &target);
if (r < 0 && in_initrd() && !arg_default_unit) {
/* Fall back to default.target, which we used to always use by default. Only do this if no
* explicit configuration was given. */
log_info("Falling back to " SPECIAL_DEFAULT_TARGET ".");
r = manager_load_startable_unit_or_warn(m, SPECIAL_DEFAULT_TARGET, NULL, &target);
}
if (r < 0) { if (r < 0) {
log_info("Falling back to rescue target: " SPECIAL_RESCUE_TARGET); log_info("Falling back to " SPECIAL_RESCUE_TARGET ".");
r = manager_load_startable_unit_or_warn(m, SPECIAL_RESCUE_TARGET, NULL, &target); r = manager_load_startable_unit_or_warn(m, SPECIAL_RESCUE_TARGET, NULL, &target);
if (r < 0) { if (r < 0) {
*ret_error_message = r == -ERFKILL ? "Rescue target masked" *ret_error_message = r == -ERFKILL ? SPECIAL_RESCUE_TARGET " masked"
: "Failed to load rescue target"; : "Failed to load " SPECIAL_RESCUE_TARGET;
return r; return r;
} }
} }
@ -2211,15 +2238,6 @@ static int load_configuration(
return r; return r;
} }
/* Initialize default unit */
if (!arg_default_unit) {
arg_default_unit = strdup(SPECIAL_DEFAULT_TARGET);
if (!arg_default_unit) {
*ret_error_message = "Failed to set default unit";
return log_oom();
}
}
/* Initialize the show status setting if it hasn't been set explicitly yet */ /* Initialize the show status setting if it hasn't been set explicitly yet */
if (arg_show_status == _SHOW_STATUS_INVALID) if (arg_show_status == _SHOW_STATUS_INVALID)
arg_show_status = SHOW_STATUS_YES; arg_show_status = SHOW_STATUS_YES;

View File

@ -29,6 +29,7 @@
#include "string-table.h" #include "string-table.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
#include "tmpfile-util.h"
#include "umask-util.h" #include "umask-util.h"
#include "user-util.h" #include "user-util.h"
@ -1640,6 +1641,44 @@ int temporary_filesystem_add(
return 0; return 0;
} }
static int make_tmp_prefix(const char *prefix) {
_cleanup_free_ char *t = NULL;
int r;
/* Don't do anything unless we know the dir is actually missing */
r = access(prefix, F_OK);
if (r >= 0)
return 0;
if (errno != ENOENT)
return -errno;
r = mkdir_parents(prefix, 0755);
if (r < 0)
return r;
r = tempfn_random(prefix, NULL, &t);
if (r < 0)
return r;
if (mkdir(t, 0777) < 0)
return -errno;
if (chmod(t, 01777) < 0) {
r = -errno;
(void) rmdir(t);
return r;
}
if (rename(t, prefix) < 0) {
r = -errno;
(void) rmdir(t);
return r == -EEXIST ? 0 : r; /* it's fine if someone else created the dir by now */
}
return 0;
}
static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) { static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) {
_cleanup_free_ char *x = NULL; _cleanup_free_ char *x = NULL;
char bid[SD_ID128_STRING_MAX]; char bid[SD_ID128_STRING_MAX];
@ -1661,6 +1700,10 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) {
if (!x) if (!x)
return -ENOMEM; return -ENOMEM;
r = make_tmp_prefix(prefix);
if (r < 0)
return r;
RUN_WITH_UMASK(0077) RUN_WITH_UMASK(0077)
if (!mkdtemp(x)) if (!mkdtemp(x))
return -errno; return -errno;

View File

@ -126,7 +126,15 @@ static int generate_wants_symlinks(void) {
STRV_FOREACH(u, arg_wants) { STRV_FOREACH(u, arg_wants) {
_cleanup_free_ char *p = NULL, *f = NULL; _cleanup_free_ char *p = NULL, *f = NULL;
const char *target = arg_default_unit ?: SPECIAL_DEFAULT_TARGET; const char *target;
/* This should match what do_queue_default_job() in core/main.c does. */
if (arg_default_unit)
target = arg_default_unit;
else if (in_initrd())
target = SPECIAL_INITRD_TARGET;
else
target = SPECIAL_DEFAULT_TARGET;
p = strjoin(arg_dest, "/", target, ".wants/", *u); p = strjoin(arg_dest, "/", target, ".wants/", *u);
if (!p) if (!p)

View File

@ -4,6 +4,7 @@
#include "sd-event.h" #include "sd-event.h"
#include "sd-journal.h" #include "sd-journal.h"
#include "time-util.h" #include "time-util.h"
typedef enum { typedef enum {

View File

@ -6,6 +6,7 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include "sd-dhcp-server.h" #include "sd-dhcp-server.h"
#include "sd-id128.h"
#include "alloc-util.h" #include "alloc-util.h"
#include "dhcp-internal.h" #include "dhcp-internal.h"
@ -13,7 +14,6 @@
#include "fd-util.h" #include "fd-util.h"
#include "in-addr-util.h" #include "in-addr-util.h"
#include "io-util.h" #include "io-util.h"
#include "sd-id128.h"
#include "siphash24.h" #include "siphash24.h"
#include "string-util.h" #include "string-util.h"
#include "unaligned.h" #include "unaligned.h"

View File

@ -682,3 +682,14 @@ global:
sd_bus_object_vtable_format; sd_bus_object_vtable_format;
sd_event_source_disable_unref; sd_event_source_disable_unref;
} LIBSYSTEMD_241; } LIBSYSTEMD_241;
LIBSYSTEMD_245 {
global:
sd_event_add_child_pidfd;
sd_event_source_get_child_pidfd;
sd_event_source_get_child_pidfd_own;
sd_event_source_set_child_pidfd_own;
sd_event_source_get_child_process_own;
sd_event_source_set_child_process_own;
sd_event_source_send_child_signal;
} LIBSYSTEMD_243;

View File

@ -102,7 +102,6 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRODUCT_UUID, EOPNOTSUPP), SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRODUCT_UUID, EOPNOTSUPP),
SD_BUS_ERROR_MAP(BUS_ERROR_SPEED_METER_INACTIVE, EOPNOTSUPP),
SD_BUS_ERROR_MAP(BUS_ERROR_UNMANAGED_INTERFACE, EOPNOTSUPP), SD_BUS_ERROR_MAP(BUS_ERROR_UNMANAGED_INTERFACE, EOPNOTSUPP),
SD_BUS_ERROR_MAP_END SD_BUS_ERROR_MAP_END

View File

@ -81,7 +81,6 @@
#define BUS_ERROR_NO_PRODUCT_UUID "org.freedesktop.hostname1.NoProductUUID" #define BUS_ERROR_NO_PRODUCT_UUID "org.freedesktop.hostname1.NoProductUUID"
#define BUS_ERROR_SPEED_METER_INACTIVE "org.freedesktop.network1.SpeedMeterInactive"
#define BUS_ERROR_UNMANAGED_INTERFACE "org.freedesktop.network1.UnmanagedInterface" #define BUS_ERROR_UNMANAGED_INTERFACE "org.freedesktop.network1.UnmanagedInterface"
BUS_ERROR_MAP_ELF_USE(bus_common_errors); BUS_ERROR_MAP_ELF_USE(bus_common_errors);

View File

@ -34,7 +34,7 @@ typedef enum EventSourceType {
* we know how to dispatch it */ * we know how to dispatch it */
typedef enum WakeupType { typedef enum WakeupType {
WAKEUP_NONE, WAKEUP_NONE,
WAKEUP_EVENT_SOURCE, WAKEUP_EVENT_SOURCE, /* either I/O or pidfd wakeup */
WAKEUP_CLOCK_DATA, WAKEUP_CLOCK_DATA,
WAKEUP_SIGNAL_DATA, WAKEUP_SIGNAL_DATA,
WAKEUP_INOTIFY_DATA, WAKEUP_INOTIFY_DATA,
@ -96,6 +96,12 @@ struct sd_event_source {
siginfo_t siginfo; siginfo_t siginfo;
pid_t pid; pid_t pid;
int options; int options;
int pidfd;
bool registered:1; /* whether the pidfd is registered in the epoll */
bool pidfd_owned:1; /* close pidfd when event source is freed */
bool process_owned:1; /* kill+reap process when event source is freed */
bool exited:1; /* true if process exited (i.e. if there's value in SIGKILLing it if we want to get rid of it) */
bool waited:1; /* true if process was waited for (i.e. if there's value in waitid(P_PID)'ing it if we want to get rid of it) */
} child; } child;
struct { struct {
sd_event_handler_t callback; sd_event_handler_t callback;

View File

@ -9,6 +9,7 @@
#include "sd-id128.h" #include "sd-id128.h"
#include "alloc-util.h" #include "alloc-util.h"
#include "env-util.h"
#include "event-source.h" #include "event-source.h"
#include "fd-util.h" #include "fd-util.h"
#include "fs-util.h" #include "fs-util.h"
@ -28,6 +29,14 @@
#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC) #define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC)
static bool EVENT_SOURCE_WATCH_PIDFD(sd_event_source *s) {
/* Returns true if this is a PID event source and can be implemented by watching EPOLLIN */
return s &&
s->type == SOURCE_CHILD &&
s->child.pidfd >= 0 &&
s->child.options == WEXITED;
}
static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = { static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = {
[SOURCE_IO] = "io", [SOURCE_IO] = "io",
[SOURCE_TIME_REALTIME] = "realtime", [SOURCE_TIME_REALTIME] = "realtime",
@ -356,8 +365,6 @@ static bool event_pid_changed(sd_event *e) {
} }
static void source_io_unregister(sd_event_source *s) { static void source_io_unregister(sd_event_source *s) {
int r;
assert(s); assert(s);
assert(s->type == SOURCE_IO); assert(s->type == SOURCE_IO);
@ -367,8 +374,7 @@ static void source_io_unregister(sd_event_source *s) {
if (!s->io.registered) if (!s->io.registered)
return; return;
r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL); if (epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL) < 0)
if (r < 0)
log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll: %m", log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll: %m",
strna(s->description), event_source_type_to_string(s->type)); strna(s->description), event_source_type_to_string(s->type));
@ -404,6 +410,51 @@ static int source_io_register(
return 0; return 0;
} }
static void source_child_pidfd_unregister(sd_event_source *s) {
assert(s);
assert(s->type == SOURCE_CHILD);
if (event_pid_changed(s->event))
return;
if (!s->child.registered)
return;
if (EVENT_SOURCE_WATCH_PIDFD(s))
if (epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->child.pidfd, NULL) < 0)
log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll: %m",
strna(s->description), event_source_type_to_string(s->type));
s->child.registered = false;
}
static int source_child_pidfd_register(sd_event_source *s, int enabled) {
int r;
assert(s);
assert(s->type == SOURCE_CHILD);
assert(enabled != SD_EVENT_OFF);
if (EVENT_SOURCE_WATCH_PIDFD(s)) {
struct epoll_event ev;
ev = (struct epoll_event) {
.events = EPOLLIN | (enabled == SD_EVENT_ONESHOT ? EPOLLONESHOT : 0),
.data.ptr = s,
};
if (s->child.registered)
r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_MOD, s->child.pidfd, &ev);
else
r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_ADD, s->child.pidfd, &ev);
if (r < 0)
return -errno;
}
s->child.registered = true;
return 0;
}
static clockid_t event_source_type_to_clock(EventSourceType t) { static clockid_t event_source_type_to_clock(EventSourceType t) {
switch (t) { switch (t) {
@ -614,9 +665,8 @@ static void event_gc_signal_data(sd_event *e, const int64_t *priority, int sig)
assert(e); assert(e);
/* Rechecks if the specified signal is still something we are /* Rechecks if the specified signal is still something we are interested in. If not, we'll unmask it,
* interested in. If not, we'll unmask it, and possibly drop * and possibly drop the signalfd for it. */
* the signalfd for it. */
if (sig == SIGCHLD && if (sig == SIGCHLD &&
e->n_enabled_child_sources > 0) e->n_enabled_child_sources > 0)
@ -707,9 +757,13 @@ static void source_disconnect(sd_event_source *s) {
} }
(void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid)); (void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid));
event_gc_signal_data(s->event, &s->priority, SIGCHLD);
} }
if (EVENT_SOURCE_WATCH_PIDFD(s))
source_child_pidfd_unregister(s);
else
event_gc_signal_data(s->event, &s->priority, SIGCHLD);
break; break;
case SOURCE_DEFER: case SOURCE_DEFER:
@ -790,6 +844,44 @@ static void source_free(sd_event_source *s) {
if (s->type == SOURCE_IO && s->io.owned) if (s->type == SOURCE_IO && s->io.owned)
s->io.fd = safe_close(s->io.fd); s->io.fd = safe_close(s->io.fd);
if (s->type == SOURCE_CHILD) {
/* Eventually the kernel will do this automatically for us, but for now let's emulate this (unreliably) in userspace. */
if (s->child.process_owned) {
if (!s->child.exited) {
bool sent = false;
if (s->child.pidfd >= 0) {
if (pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0) < 0) {
if (errno == ESRCH) /* Already dead */
sent = true;
else if (!ERRNO_IS_NOT_SUPPORTED(errno))
log_debug_errno(errno, "Failed to kill process " PID_FMT " via pidfd_send_signal(), re-trying via kill(): %m",
s->child.pid);
} else
sent = true;
}
if (!sent)
if (kill(s->child.pid, SIGKILL) < 0)
if (errno != ESRCH) /* Already dead */
log_debug_errno(errno, "Failed to kill process " PID_FMT " via kill(), ignoring: %m",
s->child.pid);
}
if (!s->child.waited) {
siginfo_t si = {};
/* Reap the child if we can */
(void) waitid(P_PID, s->child.pid, &si, WEXITED);
}
}
if (s->child.pidfd_owned)
s->child.pidfd = safe_close(s->child.pidfd);
}
if (s->destroy_callback) if (s->destroy_callback)
s->destroy_callback(s->userdata); s->destroy_callback(s->userdata);
@ -1073,7 +1165,6 @@ _public_ int sd_event_add_signal(
_cleanup_(source_freep) sd_event_source *s = NULL; _cleanup_(source_freep) sd_event_source *s = NULL;
struct signal_data *d; struct signal_data *d;
sigset_t ss;
int r; int r;
assert_return(e, -EINVAL); assert_return(e, -EINVAL);
@ -1085,11 +1176,10 @@ _public_ int sd_event_add_signal(
if (!callback) if (!callback)
callback = signal_exit_callback; callback = signal_exit_callback;
r = pthread_sigmask(SIG_SETMASK, NULL, &ss); r = signal_is_blocked(sig);
if (r != 0) if (r < 0)
return -r; return r;
if (r == 0)
if (!sigismember(&ss, sig))
return -EBUSY; return -EBUSY;
if (!e->signal_sources) { if (!e->signal_sources) {
@ -1124,6 +1214,11 @@ _public_ int sd_event_add_signal(
return 0; return 0;
} }
static bool shall_use_pidfd(void) {
/* Mostly relevant for debugging, i.e. this is used in test-event.c to test the event loop once with and once without pidfd */
return getenv_bool_secure("SYSTEMD_PIDFD") != 0;
}
_public_ int sd_event_add_child( _public_ int sd_event_add_child(
sd_event *e, sd_event *e,
sd_event_source **ret, sd_event_source **ret,
@ -1144,6 +1239,20 @@ _public_ int sd_event_add_child(
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD); assert_return(!event_pid_changed(e), -ECHILD);
if (e->n_enabled_child_sources == 0) {
/* Caller must block SIGCHLD before using us to watch children, even if pidfd is available,
* for compatibility with pre-pidfd and because we don't want the reap the child processes
* ourselves, i.e. call waitid(), and don't want Linux' default internal logic for that to
* take effect.
*
* (As an optimization we only do this check on the first child event source created.) */
r = signal_is_blocked(SIGCHLD);
if (r < 0)
return r;
if (r == 0)
return -EBUSY;
}
r = hashmap_ensure_allocated(&e->child_sources, NULL); r = hashmap_ensure_allocated(&e->child_sources, NULL);
if (r < 0) if (r < 0)
return r; return r;
@ -1155,18 +1264,131 @@ _public_ int sd_event_add_child(
if (!s) if (!s)
return -ENOMEM; return -ENOMEM;
s->wakeup = WAKEUP_EVENT_SOURCE;
s->child.pid = pid; s->child.pid = pid;
s->child.options = options; s->child.options = options;
s->child.callback = callback; s->child.callback = callback;
s->userdata = userdata; s->userdata = userdata;
s->enabled = SD_EVENT_ONESHOT; s->enabled = SD_EVENT_ONESHOT;
/* We always take a pidfd here if we can, even if we wait for anything else than WEXITED, so that we
* pin the PID, and make regular waitid() handling race-free. */
if (shall_use_pidfd()) {
s->child.pidfd = pidfd_open(s->child.pid, 0);
if (s->child.pidfd < 0) {
/* Propagate errors unless the syscall is not supported or blocked */
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
} else
s->child.pidfd_owned = true; /* If we allocate the pidfd we own it by default */
} else
s->child.pidfd = -1;
r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s);
if (r < 0)
return r;
e->n_enabled_child_sources++;
if (EVENT_SOURCE_WATCH_PIDFD(s)) {
/* We have a pidfd and we only want to watch for exit */
r = source_child_pidfd_register(s, s->enabled);
if (r < 0) {
e->n_enabled_child_sources--;
return r;
}
} else {
/* We have no pidfd or we shall wait for some other event than WEXITED */
r = event_make_signal_data(e, SIGCHLD, NULL);
if (r < 0) {
e->n_enabled_child_sources--;
return r;
}
e->need_process_child = true;
}
if (ret)
*ret = s;
TAKE_PTR(s);
return 0;
}
_public_ int sd_event_add_child_pidfd(
sd_event *e,
sd_event_source **ret,
int pidfd,
int options,
sd_event_child_handler_t callback,
void *userdata) {
_cleanup_(source_freep) sd_event_source *s = NULL;
pid_t pid;
int r;
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
assert_return(pidfd >= 0, -EBADF);
assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL);
assert_return(options != 0, -EINVAL);
assert_return(callback, -EINVAL);
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(!event_pid_changed(e), -ECHILD);
if (e->n_enabled_child_sources == 0) {
r = signal_is_blocked(SIGCHLD);
if (r < 0)
return r;
if (r == 0)
return -EBUSY;
}
r = hashmap_ensure_allocated(&e->child_sources, NULL);
if (r < 0)
return r;
r = pidfd_get_pid(pidfd, &pid);
if (r < 0)
return r;
if (hashmap_contains(e->child_sources, PID_TO_PTR(pid)))
return -EBUSY;
s = source_new(e, !ret, SOURCE_CHILD);
if (!s)
return -ENOMEM;
s->wakeup = WAKEUP_EVENT_SOURCE;
s->child.pidfd = pidfd;
s->child.pid = pid;
s->child.options = options;
s->child.callback = callback;
s->child.pidfd_owned = false; /* If we got the pidfd passed in we don't own it by default (similar to the IO fd case) */
s->userdata = userdata;
s->enabled = SD_EVENT_ONESHOT;
r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s); r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s);
if (r < 0) if (r < 0)
return r; return r;
e->n_enabled_child_sources++; e->n_enabled_child_sources++;
if (EVENT_SOURCE_WATCH_PIDFD(s)) {
/* We only want to watch for WEXITED */
r = source_child_pidfd_register(s, s->enabled);
if (r < 0) {
e->n_enabled_child_sources--;
return r;
}
} else {
/* We shall wait for some other event than WEXITED */
r = event_make_signal_data(e, SIGCHLD, NULL); r = event_make_signal_data(e, SIGCHLD, NULL);
if (r < 0) { if (r < 0) {
e->n_enabled_child_sources--; e->n_enabled_child_sources--;
@ -1174,11 +1396,12 @@ _public_ int sd_event_add_child(
} }
e->need_process_child = true; e->need_process_child = true;
}
if (ret) if (ret)
*ret = s; *ret = s;
TAKE_PTR(s);
TAKE_PTR(s);
return 0; return 0;
} }
@ -1765,7 +1988,7 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
return r; return r;
} }
epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, saved_fd, NULL); (void) epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, saved_fd, NULL);
} }
return 0; return 0;
@ -2026,7 +2249,11 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
assert(s->event->n_enabled_child_sources > 0); assert(s->event->n_enabled_child_sources > 0);
s->event->n_enabled_child_sources--; s->event->n_enabled_child_sources--;
if (EVENT_SOURCE_WATCH_PIDFD(s))
source_child_pidfd_unregister(s);
else
event_gc_signal_data(s->event, &s->priority, SIGCHLD); event_gc_signal_data(s->event, &s->priority, SIGCHLD);
break; break;
case SOURCE_EXIT: case SOURCE_EXIT:
@ -2100,6 +2327,18 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
s->enabled = m; s->enabled = m;
if (EVENT_SOURCE_WATCH_PIDFD(s)) {
/* yes, we have pidfd */
r = source_child_pidfd_register(s, s->enabled);
if (r < 0) {
s->enabled = SD_EVENT_OFF;
s->event->n_enabled_child_sources--;
return r;
}
} else {
/* no pidfd, or something other to watch for than WEXITED */
r = event_make_signal_data(s->event, SIGCHLD, NULL); r = event_make_signal_data(s->event, SIGCHLD, NULL);
if (r < 0) { if (r < 0) {
s->enabled = SD_EVENT_OFF; s->enabled = SD_EVENT_OFF;
@ -2107,6 +2346,7 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
event_gc_signal_data(s->event, &s->priority, SIGCHLD); event_gc_signal_data(s->event, &s->priority, SIGCHLD);
return r; return r;
} }
}
break; break;
@ -2228,6 +2468,98 @@ _public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) {
return 0; return 0;
} }
_public_ int sd_event_source_get_child_pidfd(sd_event_source *s) {
assert_return(s, -EINVAL);
assert_return(s->type == SOURCE_CHILD, -EDOM);
assert_return(!event_pid_changed(s->event), -ECHILD);
if (s->child.pidfd < 0)
return -EOPNOTSUPP;
return s->child.pidfd;
}
_public_ int sd_event_source_send_child_signal(sd_event_source *s, int sig, const siginfo_t *si, unsigned flags) {
assert_return(s, -EINVAL);
assert_return(s->type == SOURCE_CHILD, -EDOM);
assert_return(!event_pid_changed(s->event), -ECHILD);
assert_return(SIGNAL_VALID(sig), -EINVAL);
/* If we already have seen indication the process exited refuse sending a signal early. This way we
* can be sure we don't accidentally kill the wrong process on PID reuse when pidfds are not
* available. */
if (s->child.exited)
return -ESRCH;
if (s->child.pidfd >= 0) {
siginfo_t copy;
/* pidfd_send_signal() changes the siginfo_t argument. This is weird, let's hence copy the
* structure here */
if (si)
copy = *si;
if (pidfd_send_signal(s->child.pidfd, sig, si ? &copy : NULL, 0) < 0) {
/* Let's propagate the error only if the system call is not implemented or prohibited */
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
} else
return 0;
}
/* Flags are only supported for pidfd_send_signal(), not for rt_sigqueueinfo(), hence let's refuse
* this here. */
if (flags != 0)
return -EOPNOTSUPP;
if (si) {
/* We use rt_sigqueueinfo() only if siginfo_t is specified. */
siginfo_t copy = *si;
if (rt_sigqueueinfo(s->child.pid, sig, &copy) < 0)
return -errno;
} else if (kill(s->child.pid, sig) < 0)
return -errno;
return 0;
}
_public_ int sd_event_source_get_child_pidfd_own(sd_event_source *s) {
assert_return(s, -EINVAL);
assert_return(s->type == SOURCE_CHILD, -EDOM);
if (s->child.pidfd < 0)
return -EOPNOTSUPP;
return s->child.pidfd_owned;
}
_public_ int sd_event_source_set_child_pidfd_own(sd_event_source *s, int own) {
assert_return(s, -EINVAL);
assert_return(s->type == SOURCE_CHILD, -EDOM);
if (s->child.pidfd < 0)
return -EOPNOTSUPP;
s->child.pidfd_owned = own;
return 0;
}
_public_ int sd_event_source_get_child_process_own(sd_event_source *s) {
assert_return(s, -EINVAL);
assert_return(s->type == SOURCE_CHILD, -EDOM);
return s->child.process_owned;
}
_public_ int sd_event_source_set_child_process_own(sd_event_source *s, int own) {
assert_return(s, -EINVAL);
assert_return(s->type == SOURCE_CHILD, -EDOM);
s->child.process_owned = own;
return 0;
}
_public_ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *mask) { _public_ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *mask) {
assert_return(s, -EINVAL); assert_return(s, -EINVAL);
assert_return(mask, -EINVAL); assert_return(mask, -EINVAL);
@ -2538,6 +2870,12 @@ static int process_child(sd_event *e) {
if (s->enabled == SD_EVENT_OFF) if (s->enabled == SD_EVENT_OFF)
continue; continue;
if (s->child.exited)
continue;
if (EVENT_SOURCE_WATCH_PIDFD(s)) /* There's a usable pidfd known for this event source? then don't waitid() for it here */
continue;
zero(s->child.siginfo); zero(s->child.siginfo);
r = waitid(P_PID, s->child.pid, &s->child.siginfo, r = waitid(P_PID, s->child.pid, &s->child.siginfo,
WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options); WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | s->child.options);
@ -2547,6 +2885,9 @@ static int process_child(sd_event *e) {
if (s->child.siginfo.si_pid != 0) { if (s->child.siginfo.si_pid != 0) {
bool zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED); bool zombie = IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED);
if (zombie)
s->child.exited = true;
if (!zombie && (s->child.options & WEXITED)) { if (!zombie && (s->child.options & WEXITED)) {
/* If the child isn't dead then let's /* If the child isn't dead then let's
* immediately remove the state change * immediately remove the state change
@ -2566,6 +2907,33 @@ static int process_child(sd_event *e) {
return 0; return 0;
} }
static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) {
assert(e);
assert(s);
assert(s->type == SOURCE_CHILD);
if (s->pending)
return 0;
if (s->enabled == SD_EVENT_OFF)
return 0;
if (!EVENT_SOURCE_WATCH_PIDFD(s))
return 0;
zero(s->child.siginfo);
if (waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG | WNOWAIT | s->child.options) < 0)
return -errno;
if (s->child.siginfo.si_pid == 0)
return 0;
if (IN_SET(s->child.siginfo.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED))
s->child.exited = true;
return source_set_pending(s, true);
}
static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) {
bool read_one = false; bool read_one = false;
int r; int r;
@ -2850,8 +3218,10 @@ static int source_dispatch(sd_event_source *s) {
r = s->child.callback(s, &s->child.siginfo, s->userdata); r = s->child.callback(s, &s->child.siginfo, s->userdata);
/* Now, reap the PID for good. */ /* Now, reap the PID for good. */
if (zombie) if (zombie) {
(void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED); (void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED);
s->child.waited = true;
}
break; break;
} }
@ -3052,6 +3422,11 @@ _public_ int sd_event_prepare(sd_event *e) {
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
/* Let's check that if we are a default event loop we are executed in the correct thread. We only do
* this check here once, since gettid() is typically not cached, and thus want to minimize
* syscalls */
assert_return(!e->default_event_ptr || e->tid == gettid(), -EREMOTEIO);
if (e->exit_requested) if (e->exit_requested)
goto pending; goto pending;
@ -3147,12 +3522,33 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
switch (*t) { switch (*t) {
case WAKEUP_EVENT_SOURCE: case WAKEUP_EVENT_SOURCE: {
r = process_io(e, ev_queue[i].data.ptr, ev_queue[i].events); sd_event_source *s = ev_queue[i].data.ptr;
assert(s);
switch (s->type) {
case SOURCE_IO:
r = process_io(e, s, ev_queue[i].events);
break; break;
case SOURCE_CHILD:
r = process_pidfd(e, s, ev_queue[i].events);
break;
default:
assert_not_reached("Unexpected event source type");
}
break;
}
case WAKEUP_CLOCK_DATA: { case WAKEUP_CLOCK_DATA: {
struct clock_data *d = ev_queue[i].data.ptr; struct clock_data *d = ev_queue[i].data.ptr;
assert(d);
r = flush_timer(e, d->fd, ev_queue[i].events, &d->next); r = flush_timer(e, d->fd, ev_queue[i].events, &d->next);
break; break;
} }
@ -3476,7 +3872,7 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) {
} else { } else {
if (e->watchdog_fd >= 0) { if (e->watchdog_fd >= 0) {
epoll_ctl(e->epoll_fd, EPOLL_CTL_DEL, e->watchdog_fd, NULL); (void) epoll_ctl(e->epoll_fd, EPOLL_CTL_DEL, e->watchdog_fd, NULL);
e->watchdog_fd = safe_close(e->watchdog_fd); e->watchdog_fd = safe_close(e->watchdog_fd);
} }
} }

View File

@ -9,6 +9,7 @@
#include "fs-util.h" #include "fs-util.h"
#include "log.h" #include "log.h"
#include "macro.h" #include "macro.h"
#include "missing_syscall.h"
#include "parse-util.h" #include "parse-util.h"
#include "path-util.h" #include "path-util.h"
#include "process-util.h" #include "process-util.h"
@ -62,6 +63,11 @@ static int child_handler(sd_event_source *s, const siginfo_t *si, void *userdata
assert_se(s); assert_se(s);
assert_se(si); assert_se(si);
assert_se(si->si_uid == getuid());
assert_se(si->si_signo == SIGCHLD);
assert_se(si->si_code == CLD_EXITED);
assert_se(si->si_status == 78);
log_info("got child on %c", PTR_TO_INT(userdata)); log_info("got child on %c", PTR_TO_INT(userdata));
assert_se(userdata == INT_TO_PTR('f')); assert_se(userdata == INT_TO_PTR('f'));
@ -75,6 +81,7 @@ static int child_handler(sd_event_source *s, const siginfo_t *si, void *userdata
static int signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { static int signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
sd_event_source *p = NULL; sd_event_source *p = NULL;
pid_t pid; pid_t pid;
siginfo_t plain_si;
assert_se(s); assert_se(s);
assert_se(si); assert_se(si);
@ -83,16 +90,41 @@ static int signal_handler(sd_event_source *s, const struct signalfd_siginfo *si,
assert_se(userdata == INT_TO_PTR('e')); assert_se(userdata == INT_TO_PTR('e'));
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGUSR2, -1) >= 0);
pid = fork(); pid = fork();
assert_se(pid >= 0); assert_se(pid >= 0);
if (pid == 0) if (pid == 0) {
_exit(EXIT_SUCCESS); sigset_t ss;
assert_se(sigemptyset(&ss) >= 0);
assert_se(sigaddset(&ss, SIGUSR2) >= 0);
zero(plain_si);
assert_se(sigwaitinfo(&ss, &plain_si) >= 0);
assert_se(plain_si.si_signo == SIGUSR2);
assert_se(plain_si.si_value.sival_int == 4711);
_exit(78);
}
assert_se(sd_event_add_child(sd_event_source_get_event(s), &p, pid, WEXITED, child_handler, INT_TO_PTR('f')) >= 0); assert_se(sd_event_add_child(sd_event_source_get_event(s), &p, pid, WEXITED, child_handler, INT_TO_PTR('f')) >= 0);
assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0); assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0);
assert_se(sd_event_source_set_child_process_own(p, true) >= 0);
/* We can't use structured initialization here, since the structure contains various unions and these
* fields lie in overlapping (carefully aligned) unions that LLVM is allergic to allow assignments
* to */
zero(plain_si);
plain_si.si_signo = SIGUSR2;
plain_si.si_code = SI_QUEUE;
plain_si.si_pid = getpid();
plain_si.si_uid = getuid();
plain_si.si_value.sival_int = 4711;
assert_se(sd_event_source_send_child_signal(p, SIGUSR2, &plain_si, 0) >= 0);
sd_event_source_unref(s); sd_event_source_unref(s);
@ -119,7 +151,7 @@ static int defer_handler(sd_event_source *s, void *userdata) {
return 1; return 1;
} }
static bool do_quit = false; static bool do_quit;
static int time_handler(sd_event_source *s, uint64_t usec, void *userdata) { static int time_handler(sd_event_source *s, uint64_t usec, void *userdata) {
log_info("got timer on %c", PTR_TO_INT(userdata)); log_info("got timer on %c", PTR_TO_INT(userdata));
@ -161,7 +193,7 @@ static int post_handler(sd_event_source *s, void *userdata) {
return 2; return 2;
} }
static void test_basic(void) { static void test_basic(bool with_pidfd) {
sd_event *e = NULL; sd_event *e = NULL;
sd_event_source *w = NULL, *x = NULL, *y = NULL, *z = NULL, *q = NULL, *t = NULL; sd_event_source *w = NULL, *x = NULL, *y = NULL, *z = NULL, *q = NULL, *t = NULL;
static const char ch = 'x'; static const char ch = 'x';
@ -169,6 +201,8 @@ static void test_basic(void) {
uint64_t event_now; uint64_t event_now;
int64_t priority; int64_t priority;
assert_se(setenv("SYSTEMD_PIDFD", yes_no(with_pidfd), 1) >= 0);
assert_se(pipe(a) >= 0); assert_se(pipe(a) >= 0);
assert_se(pipe(b) >= 0); assert_se(pipe(b) >= 0);
assert_se(pipe(d) >= 0); assert_se(pipe(d) >= 0);
@ -201,6 +235,8 @@ static void test_basic(void) {
assert_se(sd_event_add_io(e, &x, a[0], EPOLLIN, io_handler, INT_TO_PTR('a')) >= 0); assert_se(sd_event_add_io(e, &x, a[0], EPOLLIN, io_handler, INT_TO_PTR('a')) >= 0);
assert_se(sd_event_add_io(e, &y, b[0], EPOLLIN, io_handler, INT_TO_PTR('b')) >= 0); assert_se(sd_event_add_io(e, &y, b[0], EPOLLIN, io_handler, INT_TO_PTR('b')) >= 0);
do_quit = false;
assert_se(sd_event_add_time(e, &z, CLOCK_MONOTONIC, 0, 0, time_handler, INT_TO_PTR('c')) >= 0); assert_se(sd_event_add_time(e, &z, CLOCK_MONOTONIC, 0, 0, time_handler, INT_TO_PTR('c')) >= 0);
assert_se(sd_event_add_exit(e, &q, exit_handler, INT_TO_PTR('g')) >= 0); assert_se(sd_event_add_exit(e, &q, exit_handler, INT_TO_PTR('g')) >= 0);
@ -258,6 +294,8 @@ static void test_basic(void) {
safe_close_pair(b); safe_close_pair(b);
safe_close_pair(d); safe_close_pair(d);
safe_close_pair(k); safe_close_pair(k);
assert_se(unsetenv("SYSTEMD_PIDFD") >= 0);
} }
static void test_sd_event_now(void) { static void test_sd_event_now(void) {
@ -482,15 +520,89 @@ static void test_inotify(unsigned n_create_events) {
sd_event_unref(e); sd_event_unref(e);
} }
static int pidfd_handler(sd_event_source *s, const siginfo_t *si, void *userdata) {
assert_se(s);
assert_se(si);
assert_se(si->si_uid == getuid());
assert_se(si->si_signo == SIGCHLD);
assert_se(si->si_code == CLD_EXITED);
assert_se(si->si_status == 66);
log_info("got pidfd on %c", PTR_TO_INT(userdata));
assert_se(userdata == INT_TO_PTR('p'));
assert_se(sd_event_exit(sd_event_source_get_event(s), 0) >= 0);
sd_event_source_unref(s);
return 0;
}
static void test_pidfd(void) {
sd_event_source *s = NULL, *t = NULL;
sd_event *e = NULL;
int pidfd;
pid_t pid, pid2;
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
pid = fork();
if (pid == 0) {
/* child */
_exit(66);
}
assert_se(pid > 1);
pidfd = pidfd_open(pid, 0);
if (pidfd < 0) {
/* No pidfd_open() supported or blocked? */
assert_se(ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno));
(void) wait_for_terminate(pid, NULL);
return;
}
pid2 = fork();
if (pid2 == 0)
freeze();
assert_se(pid > 2);
assert_se(sd_event_default(&e) >= 0);
assert_se(sd_event_add_child_pidfd(e, &s, pidfd, WEXITED, pidfd_handler, INT_TO_PTR('p')) >= 0);
assert_se(sd_event_source_set_child_pidfd_own(s, true) >= 0);
/* This one should never trigger, since our second child lives forever */
assert_se(sd_event_add_child(e, &t, pid2, WEXITED, pidfd_handler, INT_TO_PTR('q')) >= 0);
assert_se(sd_event_source_set_child_process_own(t, true) >= 0);
assert_se(sd_event_loop(e) >= 0);
/* Child should still be alive */
assert_se(kill(pid2, 0) >= 0);
t = sd_event_source_unref(t);
/* Child should now be dead, since we dropped the ref */
assert_se(kill(pid2, 0) < 0 && errno == ESRCH);
sd_event_unref(e);
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
test_setup_logging(LOG_INFO); test_setup_logging(LOG_INFO);
test_basic(); test_basic(true); /* test with pidfd */
test_basic(false); /* test without pidfd */
test_sd_event_now(); test_sd_event_now();
test_rtqueue(); test_rtqueue();
test_inotify(100); /* should work without overflow */ test_inotify(100); /* should work without overflow */
test_inotify(33000); /* should trigger a q overflow */ test_inotify(33000); /* should trigger a q overflow */
test_pidfd();
return 0; return 0;
} }

View File

@ -575,7 +575,9 @@ static int netlink_message_read_internal(sd_netlink_message *m, unsigned short t
assert_return(data, -EINVAL); assert_return(data, -EINVAL);
assert(m->n_containers < RTNL_CONTAINER_DEPTH); assert(m->n_containers < RTNL_CONTAINER_DEPTH);
assert(m->containers[m->n_containers].attributes);
if (!m->containers[m->n_containers].attributes)
return -ENODATA;
if (type >= m->containers[m->n_containers].n_attributes) if (type >= m->containers[m->n_containers].n_attributes)
return -ENODATA; return -ENODATA;
@ -1021,6 +1023,27 @@ int sd_netlink_message_get_errno(const sd_netlink_message *m) {
return err->error; return err->error;
} }
static int netlink_message_parse_error(sd_netlink_message *m) {
struct nlmsgerr *err = NLMSG_DATA(m->hdr);
size_t hlen = sizeof(struct nlmsgerr);
/* no TLVs, nothing to do here */
if (!(m->hdr->nlmsg_flags & NLM_F_ACK_TLVS))
return 0;
/* if NLM_F_CAPPED is set then the inner err msg was capped */
if (!(m->hdr->nlmsg_flags & NLM_F_CAPPED))
hlen += err->msg.nlmsg_len - sizeof(struct nlmsghdr);
if (m->hdr->nlmsg_len <= NLMSG_SPACE(hlen))
return 0;
return netlink_container_parse(m,
&m->containers[m->n_containers],
(struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + hlen),
NLMSG_PAYLOAD(m->hdr, hlen));
}
int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *genl) { int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *genl) {
const NLType *nl_type; const NLType *nl_type;
uint16_t type; uint16_t type;
@ -1060,6 +1083,9 @@ int sd_netlink_message_rewind(sd_netlink_message *m, sd_netlink *genl) {
m->containers[0].type_system = type_system; m->containers[0].type_system = type_system;
if (sd_netlink_message_is_error(m))
r = netlink_message_parse_error(m);
else
r = netlink_container_parse(m, r = netlink_container_parse(m,
&m->containers[m->n_containers], &m->containers[m->n_containers],
(struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)), (struct rtattr*)((uint8_t*) NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)),

View File

@ -745,9 +745,19 @@ static const NLTypeSystem rtnl_qdisc_type_system = {
.types = rtnl_qdisc_types, .types = rtnl_qdisc_types,
}; };
static const NLType error_types[] = {
[NLMSGERR_ATTR_MSG] = { .type = NETLINK_TYPE_STRING },
[NLMSGERR_ATTR_OFFS] = { .type = NETLINK_TYPE_U32 },
};
static const NLTypeSystem error_type_system = {
.count = ELEMENTSOF(error_types),
.types = error_types,
};
static const NLType rtnl_types[] = { static const NLType rtnl_types[] = {
[NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 }, [NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 },
[NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) }, [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &error_type_system, .size = sizeof(struct nlmsgerr) },
[RTM_NEWLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, [RTM_NEWLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
[RTM_DELLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, [RTM_DELLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
[RTM_GETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, [RTM_GETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) },
@ -1052,7 +1062,7 @@ const NLTypeSystem genl_family_type_system_root = {
}; };
static const NLType genl_types[] = { static const NLType genl_types[] = {
[SD_GENL_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) }, [SD_GENL_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &error_type_system, .size = sizeof(struct nlmsgerr) },
[SD_GENL_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system }, [SD_GENL_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system },
[SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_get_family_type_system, .size = sizeof(struct genlmsghdr) }, [SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_get_family_type_system, .size = sizeof(struct genlmsghdr) },
[SD_GENL_NL80211] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system, .size = sizeof(struct genlmsghdr) }, [SD_GENL_NL80211] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_type_system, .size = sizeof(struct genlmsghdr) },

View File

@ -107,6 +107,10 @@ int sd_netlink_open_fd(sd_netlink **ret, int fd) {
rtnl->fd = fd; rtnl->fd = fd;
rtnl->protocol = protocol; rtnl->protocol = protocol;
r = setsockopt_int(fd, SOL_NETLINK, NETLINK_EXT_ACK, 1);
if (r < 0)
log_debug_errno(r, "sd-netlink: Failed to enable NETLINK_EXT_ACK option, ignoring: %m");
r = socket_bind(rtnl); r = socket_bind(rtnl);
if (r < 0) { if (r < 0) {
rtnl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */ rtnl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */

View File

@ -587,7 +587,7 @@ const NetDevVTable bond_vtable = {
.object_size = sizeof(Bond), .object_size = sizeof(Bond),
.init = bond_init, .init = bond_init,
.done = bond_done, .done = bond_done,
.sections = "Match\0NetDev\0Bond\0", .sections = NETDEV_COMMON_SECTIONS "Bond\0",
.fill_message_create = netdev_bond_fill_message_create, .fill_message_create = netdev_bond_fill_message_create,
.create_type = NETDEV_CREATE_MASTER, .create_type = NETDEV_CREATE_MASTER,
.generate_mac = true, .generate_mac = true,

View File

@ -355,7 +355,7 @@ static void bridge_init(NetDev *n) {
const NetDevVTable bridge_vtable = { const NetDevVTable bridge_vtable = {
.object_size = sizeof(Bridge), .object_size = sizeof(Bridge),
.init = bridge_init, .init = bridge_init,
.sections = "Match\0NetDev\0Bridge\0", .sections = NETDEV_COMMON_SECTIONS "Bridge\0",
.post_create = netdev_bridge_post_create, .post_create = netdev_bridge_post_create,
.create_type = NETDEV_CREATE_MASTER, .create_type = NETDEV_CREATE_MASTER,
}; };

View File

@ -4,7 +4,7 @@
const NetDevVTable dummy_vtable = { const NetDevVTable dummy_vtable = {
.object_size = sizeof(Dummy), .object_size = sizeof(Dummy),
.sections = "Match\0NetDev\0", .sections = NETDEV_COMMON_SECTIONS,
.create_type = NETDEV_CREATE_INDEPENDENT, .create_type = NETDEV_CREATE_INDEPENDENT,
.generate_mac = true, .generate_mac = true,
}; };

View File

@ -262,7 +262,7 @@ static void fou_tunnel_init(NetDev *netdev) {
const NetDevVTable foutnl_vtable = { const NetDevVTable foutnl_vtable = {
.object_size = sizeof(FouTunnel), .object_size = sizeof(FouTunnel),
.init = fou_tunnel_init, .init = fou_tunnel_init,
.sections = "Match\0NetDev\0FooOverUDP\0", .sections = NETDEV_COMMON_SECTIONS "FooOverUDP\0",
.create = netdev_fou_tunnel_create, .create = netdev_fou_tunnel_create,
.create_type = NETDEV_CREATE_INDEPENDENT, .create_type = NETDEV_CREATE_INDEPENDENT,
.config_verify = netdev_fou_tunnel_verify, .config_verify = netdev_fou_tunnel_verify,

View File

@ -348,7 +348,7 @@ static void geneve_init(NetDev *netdev) {
const NetDevVTable geneve_vtable = { const NetDevVTable geneve_vtable = {
.object_size = sizeof(Geneve), .object_size = sizeof(Geneve),
.init = geneve_init, .init = geneve_init,
.sections = "Match\0NetDev\0GENEVE\0", .sections = NETDEV_COMMON_SECTIONS "GENEVE\0",
.create = netdev_geneve_create, .create = netdev_geneve_create,
.create_type = NETDEV_CREATE_INDEPENDENT, .create_type = NETDEV_CREATE_INDEPENDENT,
.config_verify = netdev_geneve_verify, .config_verify = netdev_geneve_verify,

View File

@ -74,7 +74,7 @@ static void ipvlan_init(NetDev *n) {
const NetDevVTable ipvlan_vtable = { const NetDevVTable ipvlan_vtable = {
.object_size = sizeof(IPVlan), .object_size = sizeof(IPVlan),
.init = ipvlan_init, .init = ipvlan_init,
.sections = "Match\0NetDev\0IPVLAN\0", .sections = NETDEV_COMMON_SECTIONS "IPVLAN\0",
.fill_message_create = netdev_ipvlan_fill_message_create, .fill_message_create = netdev_ipvlan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.generate_mac = true, .generate_mac = true,
@ -83,7 +83,7 @@ const NetDevVTable ipvlan_vtable = {
const NetDevVTable ipvtap_vtable = { const NetDevVTable ipvtap_vtable = {
.object_size = sizeof(IPVlan), .object_size = sizeof(IPVlan),
.init = ipvlan_init, .init = ipvlan_init,
.sections = "Match\0NetDev\0IPVTAP\0", .sections = NETDEV_COMMON_SECTIONS "IPVTAP\0",
.fill_message_create = netdev_ipvlan_fill_message_create, .fill_message_create = netdev_ipvlan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.generate_mac = true, .generate_mac = true,

View File

@ -723,7 +723,7 @@ static void l2tp_tunnel_done(NetDev *netdev) {
const NetDevVTable l2tptnl_vtable = { const NetDevVTable l2tptnl_vtable = {
.object_size = sizeof(L2tpTunnel), .object_size = sizeof(L2tpTunnel),
.init = l2tp_tunnel_init, .init = l2tp_tunnel_init,
.sections = "Match\0NetDev\0L2TP\0L2TPSession\0", .sections = NETDEV_COMMON_SECTIONS "L2TP\0L2TPSession\0",
.create_after_configured = l2tp_create_tunnel, .create_after_configured = l2tp_create_tunnel,
.done = l2tp_tunnel_done, .done = l2tp_tunnel_done,
.create_type = NETDEV_CREATE_AFTER_CONFIGURED, .create_type = NETDEV_CREATE_AFTER_CONFIGURED,

View File

@ -1235,7 +1235,7 @@ static void macsec_done(NetDev *netdev) {
const NetDevVTable macsec_vtable = { const NetDevVTable macsec_vtable = {
.object_size = sizeof(MACsec), .object_size = sizeof(MACsec),
.init = macsec_init, .init = macsec_init,
.sections = "Match\0NetDev\0MACsec\0MACsecReceiveChannel\0MACsecTransmitAssociation\0MACsecReceiveAssociation\0", .sections = NETDEV_COMMON_SECTIONS "MACsec\0MACsecReceiveChannel\0MACsecTransmitAssociation\0MACsecReceiveAssociation\0",
.fill_message_create = netdev_macsec_fill_message_create, .fill_message_create = netdev_macsec_fill_message_create,
.post_create = netdev_macsec_configure, .post_create = netdev_macsec_configure,
.done = macsec_done, .done = macsec_done,

View File

@ -58,7 +58,7 @@ static void macvlan_init(NetDev *n) {
const NetDevVTable macvtap_vtable = { const NetDevVTable macvtap_vtable = {
.object_size = sizeof(MacVlan), .object_size = sizeof(MacVlan),
.init = macvlan_init, .init = macvlan_init,
.sections = "Match\0NetDev\0MACVTAP\0", .sections = NETDEV_COMMON_SECTIONS "MACVTAP\0",
.fill_message_create = netdev_macvlan_fill_message_create, .fill_message_create = netdev_macvlan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.generate_mac = true, .generate_mac = true,
@ -67,7 +67,7 @@ const NetDevVTable macvtap_vtable = {
const NetDevVTable macvlan_vtable = { const NetDevVTable macvlan_vtable = {
.object_size = sizeof(MacVlan), .object_size = sizeof(MacVlan),
.init = macvlan_init, .init = macvlan_init,
.sections = "Match\0NetDev\0MACVLAN\0", .sections = NETDEV_COMMON_SECTIONS "MACVLAN\0",
.fill_message_create = netdev_macvlan_fill_message_create, .fill_message_create = netdev_macvlan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.generate_mac = true, .generate_mac = true,

View File

@ -682,9 +682,9 @@ int netdev_load_one(Manager *manager, const char *filename) {
dropin_dirname = strjoina(basename(filename), ".d"); dropin_dirname = strjoina(basename(filename), ".d");
r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname, r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname,
"Match\0NetDev\0", NETDEV_COMMON_SECTIONS NETDEV_OTHER_SECTIONS,
config_item_perf_lookup, network_netdev_gperf_lookup, config_item_perf_lookup, network_netdev_gperf_lookup,
CONFIG_PARSE_WARN|CONFIG_PARSE_RELAXED, netdev_raw); CONFIG_PARSE_WARN, netdev_raw);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -8,6 +8,35 @@
#include "networkd-link.h" #include "networkd-link.h"
#include "time-util.h" #include "time-util.h"
#define NETDEV_COMMON_SECTIONS "Match\0NetDev\0"
/* This is the list of known sections. We need to ignore them in the initial parsing phase. */
#define NETDEV_OTHER_SECTIONS \
"-Bond\0" \
"-Bridge\0" \
"-FooOverUDP\0" \
"-GENEVE\0" \
"-IPVLAN\0" \
"-IPVTAP\0" \
"-L2TP\0" \
"-L2TPSession\0" \
"-MACsec\0" \
"-MACsecReceiveChannel\0" \
"-MACsecTransmitAssociation\0" \
"-MACsecReceiveAssociation\0" \
"-MACVTAP\0" \
"-MACVLAN\0" \
"-Tunnel\0" \
"-Tun\0" \
"-Tap\0" \
"-Peer\0" \
"-VLAN\0" \
"-VRF\0" \
"-VXCAN\0" \
"-VXLAN\0" \
"-WireGuard\0" \
"-WireGuardPeer\0" \
"-Xfrm\0"
typedef struct netdev_join_callback netdev_join_callback; typedef struct netdev_join_callback netdev_join_callback;
struct netdev_join_callback { struct netdev_join_callback {

View File

@ -4,7 +4,7 @@
const NetDevVTable netdevsim_vtable = { const NetDevVTable netdevsim_vtable = {
.object_size = sizeof(NetDevSim), .object_size = sizeof(NetDevSim),
.sections = "Match\0NetDev\0", .sections = NETDEV_COMMON_SECTIONS,
.create_type = NETDEV_CREATE_INDEPENDENT, .create_type = NETDEV_CREATE_INDEPENDENT,
.generate_mac = true, .generate_mac = true,
}; };

View File

@ -16,7 +16,7 @@ static int netdev_nlmon_verify(NetDev *netdev, const char *filename) {
const NetDevVTable nlmon_vtable = { const NetDevVTable nlmon_vtable = {
.object_size = sizeof(NLMon), .object_size = sizeof(NLMon),
.sections = "Match\0NetDev\0", .sections = NETDEV_COMMON_SECTIONS,
.create_type = NETDEV_CREATE_INDEPENDENT, .create_type = NETDEV_CREATE_INDEPENDENT,
.config_verify = netdev_nlmon_verify, .config_verify = netdev_nlmon_verify,
}; };

View File

@ -805,7 +805,7 @@ static void ip6tnl_init(NetDev *n) {
const NetDevVTable ipip_vtable = { const NetDevVTable ipip_vtable = {
.object_size = sizeof(Tunnel), .object_size = sizeof(Tunnel),
.init = ipip_sit_init, .init = ipip_sit_init,
.sections = "Match\0NetDev\0Tunnel\0", .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_ipip_sit_fill_message_create, .fill_message_create = netdev_ipip_sit_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify, .config_verify = netdev_tunnel_verify,
@ -815,7 +815,7 @@ const NetDevVTable ipip_vtable = {
const NetDevVTable sit_vtable = { const NetDevVTable sit_vtable = {
.object_size = sizeof(Tunnel), .object_size = sizeof(Tunnel),
.init = ipip_sit_init, .init = ipip_sit_init,
.sections = "Match\0NetDev\0Tunnel\0", .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_ipip_sit_fill_message_create, .fill_message_create = netdev_ipip_sit_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify, .config_verify = netdev_tunnel_verify,
@ -825,7 +825,7 @@ const NetDevVTable sit_vtable = {
const NetDevVTable vti_vtable = { const NetDevVTable vti_vtable = {
.object_size = sizeof(Tunnel), .object_size = sizeof(Tunnel),
.init = vti_init, .init = vti_init,
.sections = "Match\0NetDev\0Tunnel\0", .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_vti_fill_message_create, .fill_message_create = netdev_vti_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify, .config_verify = netdev_tunnel_verify,
@ -835,7 +835,7 @@ const NetDevVTable vti_vtable = {
const NetDevVTable vti6_vtable = { const NetDevVTable vti6_vtable = {
.object_size = sizeof(Tunnel), .object_size = sizeof(Tunnel),
.init = vti_init, .init = vti_init,
.sections = "Match\0NetDev\0Tunnel\0", .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_vti_fill_message_create, .fill_message_create = netdev_vti_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify, .config_verify = netdev_tunnel_verify,
@ -845,7 +845,7 @@ const NetDevVTable vti6_vtable = {
const NetDevVTable gre_vtable = { const NetDevVTable gre_vtable = {
.object_size = sizeof(Tunnel), .object_size = sizeof(Tunnel),
.init = gre_erspan_init, .init = gre_erspan_init,
.sections = "Match\0NetDev\0Tunnel\0", .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_gre_erspan_fill_message_create, .fill_message_create = netdev_gre_erspan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify, .config_verify = netdev_tunnel_verify,
@ -855,7 +855,7 @@ const NetDevVTable gre_vtable = {
const NetDevVTable gretap_vtable = { const NetDevVTable gretap_vtable = {
.object_size = sizeof(Tunnel), .object_size = sizeof(Tunnel),
.init = gre_erspan_init, .init = gre_erspan_init,
.sections = "Match\0NetDev\0Tunnel\0", .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_gre_erspan_fill_message_create, .fill_message_create = netdev_gre_erspan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify, .config_verify = netdev_tunnel_verify,
@ -865,7 +865,7 @@ const NetDevVTable gretap_vtable = {
const NetDevVTable ip6gre_vtable = { const NetDevVTable ip6gre_vtable = {
.object_size = sizeof(Tunnel), .object_size = sizeof(Tunnel),
.init = ip6gre_init, .init = ip6gre_init,
.sections = "Match\0NetDev\0Tunnel\0", .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_ip6gre_fill_message_create, .fill_message_create = netdev_ip6gre_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify, .config_verify = netdev_tunnel_verify,
@ -875,7 +875,7 @@ const NetDevVTable ip6gre_vtable = {
const NetDevVTable ip6gretap_vtable = { const NetDevVTable ip6gretap_vtable = {
.object_size = sizeof(Tunnel), .object_size = sizeof(Tunnel),
.init = ip6gre_init, .init = ip6gre_init,
.sections = "Match\0NetDev\0Tunnel\0", .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_ip6gre_fill_message_create, .fill_message_create = netdev_ip6gre_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify, .config_verify = netdev_tunnel_verify,
@ -885,7 +885,7 @@ const NetDevVTable ip6gretap_vtable = {
const NetDevVTable ip6tnl_vtable = { const NetDevVTable ip6tnl_vtable = {
.object_size = sizeof(Tunnel), .object_size = sizeof(Tunnel),
.init = ip6tnl_init, .init = ip6tnl_init,
.sections = "Match\0NetDev\0Tunnel\0", .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_ip6tnl_fill_message_create, .fill_message_create = netdev_ip6tnl_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify, .config_verify = netdev_tunnel_verify,
@ -895,7 +895,7 @@ const NetDevVTable ip6tnl_vtable = {
const NetDevVTable erspan_vtable = { const NetDevVTable erspan_vtable = {
.object_size = sizeof(Tunnel), .object_size = sizeof(Tunnel),
.init = gre_erspan_init, .init = gre_erspan_init,
.sections = "Match\0NetDev\0Tunnel\0", .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
.fill_message_create = netdev_gre_erspan_fill_message_create, .fill_message_create = netdev_gre_erspan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify, .config_verify = netdev_tunnel_verify,

View File

@ -147,7 +147,7 @@ static int tuntap_verify(NetDev *netdev, const char *filename) {
const NetDevVTable tun_vtable = { const NetDevVTable tun_vtable = {
.object_size = sizeof(TunTap), .object_size = sizeof(TunTap),
.sections = "Match\0NetDev\0Tun\0", .sections = NETDEV_COMMON_SECTIONS "Tun\0",
.config_verify = tuntap_verify, .config_verify = tuntap_verify,
.done = tuntap_done, .done = tuntap_done,
.create = netdev_create_tuntap, .create = netdev_create_tuntap,
@ -156,7 +156,7 @@ const NetDevVTable tun_vtable = {
const NetDevVTable tap_vtable = { const NetDevVTable tap_vtable = {
.object_size = sizeof(TunTap), .object_size = sizeof(TunTap),
.sections = "Match\0NetDev\0Tap\0", .sections = NETDEV_COMMON_SECTIONS "Tap\0",
.config_verify = tuntap_verify, .config_verify = tuntap_verify,
.done = tuntap_done, .done = tuntap_done,
.create = netdev_create_tuntap, .create = netdev_create_tuntap,

View File

@ -4,7 +4,7 @@
const NetDevVTable vcan_vtable = { const NetDevVTable vcan_vtable = {
.object_size = sizeof(VCan), .object_size = sizeof(VCan),
.sections = "Match\0NetDev\0", .sections = NETDEV_COMMON_SECTIONS,
.create_type = NETDEV_CREATE_INDEPENDENT, .create_type = NETDEV_CREATE_INDEPENDENT,
.generate_mac = true, .generate_mac = true,
}; };

View File

@ -85,7 +85,7 @@ static void veth_done(NetDev *n) {
const NetDevVTable veth_vtable = { const NetDevVTable veth_vtable = {
.object_size = sizeof(Veth), .object_size = sizeof(Veth),
.sections = "Match\0NetDev\0Peer\0", .sections = NETDEV_COMMON_SECTIONS "Peer\0",
.done = veth_done, .done = veth_done,
.fill_message_create = netdev_veth_fill_message_create, .fill_message_create = netdev_veth_fill_message_create,
.create_type = NETDEV_CREATE_INDEPENDENT, .create_type = NETDEV_CREATE_INDEPENDENT,

View File

@ -85,7 +85,7 @@ static void vlan_init(NetDev *netdev) {
const NetDevVTable vlan_vtable = { const NetDevVTable vlan_vtable = {
.object_size = sizeof(VLan), .object_size = sizeof(VLan),
.init = vlan_init, .init = vlan_init,
.sections = "Match\0NetDev\0VLAN\0", .sections = NETDEV_COMMON_SECTIONS "VLAN\0",
.fill_message_create = netdev_vlan_fill_message_create, .fill_message_create = netdev_vlan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_vlan_verify, .config_verify = netdev_vlan_verify,

View File

@ -25,7 +25,7 @@ static int netdev_vrf_fill_message_create(NetDev *netdev, Link *link, sd_netlink
const NetDevVTable vrf_vtable = { const NetDevVTable vrf_vtable = {
.object_size = sizeof(Vrf), .object_size = sizeof(Vrf),
.sections = "Match\0NetDev\0VRF\0", .sections = NETDEV_COMMON_SECTIONS "VRF\0",
.fill_message_create = netdev_vrf_fill_message_create, .fill_message_create = netdev_vrf_fill_message_create,
.create_type = NETDEV_CREATE_MASTER, .create_type = NETDEV_CREATE_MASTER,
.generate_mac = true, .generate_mac = true,

View File

@ -65,7 +65,7 @@ static void vxcan_done(NetDev *n) {
const NetDevVTable vxcan_vtable = { const NetDevVTable vxcan_vtable = {
.object_size = sizeof(VxCan), .object_size = sizeof(VxCan),
.sections = "Match\0NetDev\0VXCAN\0", .sections = NETDEV_COMMON_SECTIONS "VXCAN\0",
.done = vxcan_done, .done = vxcan_done,
.fill_message_create = netdev_vxcan_fill_message_create, .fill_message_create = netdev_vxcan_fill_message_create,
.create_type = NETDEV_CREATE_INDEPENDENT, .create_type = NETDEV_CREATE_INDEPENDENT,

View File

@ -371,7 +371,7 @@ static void vxlan_init(NetDev *netdev) {
const NetDevVTable vxlan_vtable = { const NetDevVTable vxlan_vtable = {
.object_size = sizeof(VxLan), .object_size = sizeof(VxLan),
.init = vxlan_init, .init = vxlan_init,
.sections = "Match\0NetDev\0VXLAN\0", .sections = NETDEV_COMMON_SECTIONS "VXLAN\0",
.fill_message_create = netdev_vxlan_fill_message_create, .fill_message_create = netdev_vxlan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED, .create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_vxlan_verify, .config_verify = netdev_vxlan_verify,

View File

@ -967,7 +967,7 @@ static int wireguard_verify(NetDev *netdev, const char *filename) {
const NetDevVTable wireguard_vtable = { const NetDevVTable wireguard_vtable = {
.object_size = sizeof(Wireguard), .object_size = sizeof(Wireguard),
.sections = "Match\0NetDev\0WireGuard\0WireGuardPeer\0", .sections = NETDEV_COMMON_SECTIONS "WireGuard\0WireGuardPeer\0",
.post_create = netdev_wireguard_post_create, .post_create = netdev_wireguard_post_create,
.init = wireguard_init, .init = wireguard_init,
.done = wireguard_done, .done = wireguard_done,

View File

@ -27,7 +27,7 @@ static int xfrm_fill_message_create(NetDev *netdev, Link *link, sd_netlink_messa
const NetDevVTable xfrm_vtable = { const NetDevVTable xfrm_vtable = {
.object_size = sizeof(Xfrm), .object_size = sizeof(Xfrm),
.sections = "Match\0NetDev\0Xfrm\0", .sections = NETDEV_COMMON_SECTIONS "Xfrm\0",
.fill_message_create = xfrm_fill_message_create, .fill_message_create = xfrm_fill_message_create,
.create_type = NETDEV_CREATE_STACKED .create_type = NETDEV_CREATE_STACKED
}; };

View File

@ -349,8 +349,7 @@ static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
"org.freedesktop.network1.Link", "org.freedesktop.network1.Link",
"BitRates"); "BitRates");
if (r < 0) { if (r < 0) {
bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY) || bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY);
sd_bus_error_has_name(&error, BUS_ERROR_SPEED_METER_INACTIVE);
return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING, return log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING,
r, "Failed to query link bit rates: %s", bus_error_message(&error, r)); r, "Failed to query link bit rates: %s", bus_error_message(&error, r));
@ -368,7 +367,7 @@ static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
if (r < 0) if (r < 0)
return bus_log_parse_error(r); return bus_log_parse_error(r);
link->has_bitrates = true; link->has_bitrates = link->tx_bitrate != UINT64_MAX && link->rx_bitrate != UINT64_MAX;
return 0; return 0;
} }

View File

@ -95,7 +95,7 @@ static int address_label_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
log_link_warning_errno(link, r, "could not set address label: %m"); log_link_message_warning_errno(link, m, r, "Could not set address label");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} else if (r >= 0) } else if (r >= 0)

View File

@ -431,7 +431,7 @@ static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EADDRNOTAVAIL) if (r < 0 && r != -EADDRNOTAVAIL)
log_link_warning_errno(link, r, "Could not drop address: %m"); log_link_message_warning_errno(link, m, r, "Could not drop address");
return 1; return 1;
} }

View File

@ -141,7 +141,7 @@ static int set_brvlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *lin
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) if (r < 0 && r != -EEXIST)
log_link_error_errno(link, r, "Could not add VLAN to bridge port: %m"); log_link_message_warning_errno(link, m, r, "Could not add VLAN to bridge port");
return 1; return 1;
} }

View File

@ -20,7 +20,7 @@ static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0) if (r < 0)
/* we warn but don't fail the link, as it may be brought up later */ /* we warn but don't fail the link, as it may be brought up later */
log_link_warning_errno(link, r, "Could not bring up interface: %m"); log_link_message_warning_errno(link, m, r, "Could not bring up interface");
return 1; return 1;
} }
@ -60,7 +60,7 @@ static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Failed to configure CAN link: %m"); log_link_message_warning_errno(link, m, r, "Failed to configure CAN link");
link_enter_failed(link); link_enter_failed(link);
} }
@ -183,7 +183,7 @@ static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0) { if (r < 0) {
log_link_warning_errno(link, r, "Could not bring down interface: %m"); log_link_message_warning_errno(link, m, r, "Could not bring down interface");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} }

View File

@ -65,11 +65,11 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
/* It seems kernel does not support that the prefix route cannot be configured with /* It seems kernel does not support that the prefix route cannot be configured with
* route table. Let's once drop the config and reconfigure them later. */ * route table. Let's once drop the config and reconfigure them later. */
log_link_debug_errno(link, r, "Could not set DHCPv4 route, retrying later: %m"); log_link_message_debug_errno(link, m, r, "Could not set DHCPv4 route, retrying later: %m");
link->dhcp4_route_failed = true; link->dhcp4_route_failed = true;
link->manager->dhcp4_prefix_root_cannot_set_table = true; link->manager->dhcp4_prefix_root_cannot_set_table = true;
} else if (r < 0 && r != -EEXIST) { } else if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Could not set DHCPv4 route: %m"); log_link_message_warning_errno(link, m, r, "Could not set DHCPv4 route: %m");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} }
@ -543,7 +543,7 @@ static int dhcp_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m,
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0) if (r < 0)
log_link_debug_errno(link, r, "Failed to remove DHCPv4 address, ignoring: %m"); log_link_message_warning_errno(link, m, r, "Failed to remove DHCPv4 address, ignoring");
else else
(void) manager_rtnl_process_address(rtnl, m, link->manager); (void) manager_rtnl_process_address(rtnl, m, link->manager);
@ -665,7 +665,7 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Could not set DHCPv4 address: %m"); log_link_message_warning_errno(link, m, r, "Could not set DHCPv4 address");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} else if (r >= 0) } else if (r >= 0)

View File

@ -112,7 +112,7 @@ static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Lin
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0) if (r < 0)
log_link_debug_errno(link, r, "Received error on unreachable route removal for DHCPv6 delegated subnet: %m"); log_link_message_warning_errno(link, m, r, "Received error on unreachable route removal for DHCPv6 delegated subnet");
return 1; return 1;
} }
@ -251,7 +251,7 @@ static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) if (r < 0 && r != -EEXIST)
log_link_debug_errno(link, r, "Received error when adding unreachable route for DHCPv6 delegated subnet: %m"); log_link_message_warning_errno(link, m, r, "Received error when adding unreachable route for DHCPv6 delegated subnet");
return 1; return 1;
} }
@ -402,7 +402,7 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Could not set DHCPv6 address: %m"); log_link_message_warning_errno(link, m, r, "Could not set DHCPv6 address");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} else if (r >= 0) } else if (r >= 0)
@ -714,7 +714,7 @@ static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, Link *
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
log_link_debug_errno(link, r, "Received error adding DHCPv6 Prefix Delegation route: %m"); log_link_message_warning_errno(link, m, r, "Received error adding DHCPv6 Prefix Delegation route");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} }
@ -780,7 +780,7 @@ static int dhcp6_prefix_remove_handler(sd_netlink *nl, sd_netlink_message *m, Li
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0) { if (r < 0) {
log_link_debug_errno(link, r, "Received error on DHCPv6 Prefix Delegation route removal: %m"); log_link_message_warning_errno(link, m, r, "Received error on DHCPv6 Prefix Delegation route removal");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} }

View File

@ -105,7 +105,7 @@ static int set_fdb_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Could not add FDB entry: %m"); log_link_message_warning_errno(link, m, r, "Could not add FDB entry");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} }

View File

@ -51,7 +51,7 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "could not set ipv4ll address: %m"); log_link_message_warning_errno(link, m, r, "could not set ipv4ll address");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} else if (r >= 0) } else if (r >= 0)

View File

@ -141,7 +141,7 @@ static int set_ipv6_proxy_ndp_address_handler(sd_netlink *rtnl, sd_netlink_messa
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) if (r < 0 && r != -EEXIST)
log_link_error_errno(link, r, "Could not add IPv6 proxy ndp address entry: %m"); log_link_message_warning_errno(link, m, r, "Could not add IPv6 proxy ndp address entry, ignoring");
return 1; return 1;
} }

View File

@ -41,14 +41,10 @@ static int property_get_bit_rates(
manager = link->manager; manager = link->manager;
if (!manager->use_speed_meter) if (!manager->use_speed_meter ||
return sd_bus_error_set(error, BUS_ERROR_SPEED_METER_INACTIVE, "Speed meter is disabled."); manager->speed_meter_usec_old == 0 ||
!link->stats_updated)
if (manager->speed_meter_usec_old == 0) return sd_bus_message_append(reply, "(tt)", UINT64_MAX, UINT64_MAX);
return sd_bus_error_set(error, BUS_ERROR_SPEED_METER_INACTIVE, "Speed meter is not active.");
if (!link->stats_updated)
return sd_bus_error_set(error, BUS_ERROR_SPEED_METER_INACTIVE, "Failed to measure bit-rates.");
assert(manager->speed_meter_usec_new > manager->speed_meter_usec_old); assert(manager->speed_meter_usec_new > manager->speed_meter_usec_old);
interval_sec = (manager->speed_meter_usec_new - manager->speed_meter_usec_old) / USEC_PER_SEC; interval_sec = (manager->speed_meter_usec_new - manager->speed_meter_usec_old) / USEC_PER_SEC;

View File

@ -929,7 +929,7 @@ static int nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
log_link_warning_errno(link, r, "Could not set nexthop: %m"); log_link_message_warning_errno(link, m, r, "Could not set nexthop");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} }
@ -981,7 +981,7 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
log_link_warning_errno(link, r, "Could not set route: %m"); log_link_message_warning_errno(link, m, r, "Could not set route");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} }
@ -1170,7 +1170,7 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
log_link_warning_errno(link, r, "could not set address: %m"); log_link_message_warning_errno(link, m, r, "Could not set address");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} else if (r >= 0) } else if (r >= 0)
@ -1303,7 +1303,7 @@ static int set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0) if (r < 0)
log_link_warning_errno(link, r, "Could not set MTU, ignoring: %m"); log_link_message_warning_errno(link, m, r, "Could not set MTU, ignoring");
else else
log_link_debug(link, "Setting MTU done."); log_link_debug(link, "Setting MTU done.");
@ -1415,7 +1415,7 @@ static int set_flags_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0) if (r < 0)
log_link_warning_errno(link, r, "Could not set link flags, ignoring: %m"); log_link_message_warning_errno(link, m, r, "Could not set link flags, ignoring");
return 1; return 1;
} }
@ -1582,7 +1582,7 @@ static int link_address_genmode_handler(sd_netlink *rtnl, sd_netlink_message *m,
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0) if (r < 0)
log_link_warning_errno(link, r, "Could not set address genmode for interface, ignoring: %m"); log_link_message_warning_errno(link, m, r, "Could not set address genmode for interface, ignoring");
return 1; return 1;
} }
@ -1656,7 +1656,7 @@ static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0) if (r < 0)
/* we warn but don't fail the link, as it may be brought up later */ /* we warn but don't fail the link, as it may be brought up later */
log_link_warning_errno(link, r, "Could not bring up interface: %m"); log_link_message_warning_errno(link, m, r, "Could not bring up interface");
return 1; return 1;
} }
@ -1713,7 +1713,7 @@ static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0) if (r < 0)
log_link_warning_errno(link, r, "Could not bring down interface: %m"); log_link_message_warning_errno(link, m, r, "Could not bring down interface");
return 1; return 1;
} }
@ -2118,7 +2118,7 @@ static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Could not join netdev: %m"); log_link_message_warning_errno(link, m, r, "Could not join netdev");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} }
@ -4053,3 +4053,10 @@ static const char* const link_state_table[_LINK_STATE_MAX] = {
}; };
DEFINE_STRING_TABLE_LOOKUP(link_state, LinkState); DEFINE_STRING_TABLE_LOOKUP(link_state, LinkState);
int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, int err, const char *msg) {
const char *err_msg = NULL;
(void) sd_netlink_message_read_string(m, NLMSGERR_ATTR_MSG, &err_msg);
return log_link_full(link, level, err, "%s: %s%s%m", msg, strempty(err_msg), err_msg ? " " : "");
}

View File

@ -213,6 +213,13 @@ int link_request_set_nexthop(Link *link);
int link_reconfigure(Link *link, bool force); int link_reconfigure(Link *link, bool force);
int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, int err, const char *msg);
#define log_link_message_error_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_ERR, err, msg)
#define log_link_message_warning_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_WARNING, err, msg)
#define log_link_message_notice_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_NOTICE, err, msg)
#define log_link_message_info_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_INFO, err, msg)
#define log_link_message_debug_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_DEBUG, err, msg)
#define ADDRESS_FMT_VAL(address) \ #define ADDRESS_FMT_VAL(address) \
be32toh((address).s_addr) >> 24, \ be32toh((address).s_addr) >> 24, \
(be32toh((address).s_addr) >> 16) & 0xFFu, \ (be32toh((address).s_addr) >> 16) & 0xFFu, \

View File

@ -41,6 +41,13 @@
/* use 8 MB for receive socket kernel queue. */ /* use 8 MB for receive socket kernel queue. */
#define RCVBUF_SIZE (8*1024*1024) #define RCVBUF_SIZE (8*1024*1024)
static int log_message_warning_errno(sd_netlink_message *m, int err, const char *msg) {
const char *err_msg = NULL;
(void) sd_netlink_message_read_string(m, NLMSGERR_ATTR_MSG, &err_msg);
return log_warning_errno(err, "%s: %s%s%m", msg, strempty(err_msg), err_msg ? " " : "");
}
static int setup_default_address_pool(Manager *m) { static int setup_default_address_pool(Manager *m) {
AddressPool *p; AddressPool *p;
int r; int r;
@ -283,7 +290,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
if (sd_netlink_message_is_error(message)) { if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message); r = sd_netlink_message_get_errno(message);
if (r < 0) if (r < 0)
log_warning_errno(r, "rtnl: failed to receive route message, ignoring: %m"); log_message_warning_errno(message, r, "rtnl: failed to receive route message, ignoring");
return 0; return 0;
} }
@ -576,7 +583,7 @@ int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message,
if (sd_netlink_message_is_error(message)) { if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message); r = sd_netlink_message_get_errno(message);
if (r < 0) if (r < 0)
log_warning_errno(r, "rtnl: failed to receive neighbor message, ignoring: %m"); log_message_warning_errno(message, r, "rtnl: failed to receive neighbor message, ignoring");
return 0; return 0;
} }
@ -714,7 +721,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
if (sd_netlink_message_is_error(message)) { if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message); r = sd_netlink_message_get_errno(message);
if (r < 0) if (r < 0)
log_warning_errno(r, "rtnl: failed to receive address message, ignoring: %m"); log_message_warning_errno(message, r, "rtnl: failed to receive address message, ignoring");
return 0; return 0;
} }
@ -867,7 +874,7 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
if (sd_netlink_message_is_error(message)) { if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message); r = sd_netlink_message_get_errno(message);
if (r < 0) if (r < 0)
log_warning_errno(r, "rtnl: Could not receive link message, ignoring: %m"); log_message_warning_errno(message, r, "rtnl: Could not receive link message, ignoring");
return 0; return 0;
} }
@ -957,7 +964,7 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
if (sd_netlink_message_is_error(message)) { if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message); r = sd_netlink_message_get_errno(message);
if (r < 0) if (r < 0)
log_warning_errno(r, "rtnl: failed to receive rule message, ignoring: %m"); log_message_warning_errno(message, r, "rtnl: failed to receive rule message, ignoring");
return 0; return 0;
} }
@ -1170,7 +1177,7 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
if (sd_netlink_message_is_error(message)) { if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message); r = sd_netlink_message_get_errno(message);
if (r < 0) if (r < 0)
log_warning_errno(r, "rtnl: failed to receive rule message, ignoring: %m"); log_message_warning_errno(message, r, "rtnl: failed to receive rule message, ignoring");
return 0; return 0;
} }

View File

@ -32,7 +32,7 @@ static int ndisc_netlink_route_message_handler(sd_netlink *rtnl, sd_netlink_mess
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Could not set NDisc route or address: %m"); log_link_message_error_errno(link, m, r, "Could not set NDisc route or address");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} }
@ -63,7 +63,7 @@ static int ndisc_netlink_address_message_handler(sd_netlink *rtnl, sd_netlink_me
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Could not set NDisc route or address: %m"); log_link_message_error_errno(link, m, r, "Could not set NDisc route or address");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} else if (r >= 0) } else if (r >= 0)

View File

@ -102,7 +102,7 @@ static int neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, L
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) if (r < 0 && r != -EEXIST)
/* Neighbor may not exist yet. So, do not enter failed state here. */ /* Neighbor may not exist yet. So, do not enter failed state here. */
log_link_warning_errno(link, r, "Could not set neighbor, ignoring: %m"); log_link_message_warning_errno(link, m, r, "Could not set neighbor, ignoring");
if (link->neighbor_messages == 0) { if (link->neighbor_messages == 0) {
log_link_debug(link, "Neighbors set"); log_link_debug(link, "Neighbors set");
@ -171,7 +171,7 @@ static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -ESRCH) if (r < 0 && r != -ESRCH)
/* Neighbor may not exist because it already got deleted, ignore that. */ /* Neighbor may not exist because it already got deleted, ignore that. */
log_link_warning_errno(link, r, "Could not remove neighbor: %m"); log_link_message_warning_errno(link, m, r, "Could not remove neighbor");
return 1; return 1;
} }

View File

@ -278,7 +278,7 @@ static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -ESRCH) if (r < 0 && r != -ESRCH)
log_link_warning_errno(link, r, "Could not drop nexthop: %m"); log_link_message_warning_errno(link, m, r, "Could not drop nexthop, ignoring");
return 1; return 1;
} }

View File

@ -398,7 +398,7 @@ static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *l
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -ESRCH) if (r < 0 && r != -ESRCH)
log_link_warning_errno(link, r, "Could not drop route: %m"); log_link_message_warning_errno(link, m, r, "Could not drop route, ignoring");
return 1; return 1;
} }

View File

@ -317,7 +317,7 @@ static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_messa
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0) if (r < 0)
log_link_warning_errno(link, r, "Could not drop routing policy rule: %m"); log_link_message_warning_errno(link, m, r, "Could not drop routing policy rule");
return 1; return 1;
} }
@ -431,7 +431,7 @@ static int routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m,
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
log_link_warning_errno(link, r, "Could not add routing policy rule: %m"); log_link_message_warning_errno(link, m, r, "Could not add routing policy rule");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} }

View File

@ -100,7 +100,7 @@ static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
r = sd_netlink_message_get_errno(m); r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) { if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Could not set QDisc: %m"); log_link_message_error_errno(link, m, r, "Could not set QDisc: %m");
link_enter_failed(link); link_enter_failed(link);
return 1; return 1;
} }

View File

@ -64,6 +64,8 @@ systemd_resolved_sources = files('''
resolved-etc-hosts.h resolved-etc-hosts.h
resolved-etc-hosts.c resolved-etc-hosts.c
resolved-dnstls.h resolved-dnstls.h
resolved-util.c
resolved-util.h
'''.split()) '''.split())
resolvectl_sources = files(''' resolvectl_sources = files('''
@ -228,4 +230,10 @@ tests += [
[], [],
[], [],
'ENABLE_RESOLVE', 'manual'], 'ENABLE_RESOLVE', 'manual'],
[['src/resolve/test-resolved-util.c',
'src/resolve/resolved-util.c',
'src/resolve/resolved-util.h'],
[],
[]],
] ]

View File

@ -8,6 +8,7 @@
#include "parse-util.h" #include "parse-util.h"
#include "resolved-conf.h" #include "resolved-conf.h"
#include "resolved-dnssd.h" #include "resolved-dnssd.h"
#include "resolved-util.h"
#include "specifier.h" #include "specifier.h"
#include "string-table.h" #include "string-table.h"
#include "string-util.h" #include "string-util.h"
@ -27,11 +28,12 @@ static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, cons
union in_addr_union address; union in_addr_union address;
int family, r, ifindex = 0; int family, r, ifindex = 0;
DnsServer *s; DnsServer *s;
_cleanup_free_ char *server_name = NULL;
assert(m); assert(m);
assert(word); assert(word);
r = in_addr_ifindex_from_string_auto(word, &family, &address, &ifindex); r = in_addr_ifindex_name_from_string_auto(word, &family, &address, &ifindex, &server_name);
if (r < 0) if (r < 0)
return r; return r;
@ -52,7 +54,7 @@ static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, cons
return 0; return 0;
} }
return dns_server_new(m, NULL, type, NULL, family, &address, ifindex); return dns_server_new(m, NULL, type, NULL, family, &address, ifindex, server_name);
} }
int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) { int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {

View File

@ -25,8 +25,10 @@ int dns_server_new(
Link *l, Link *l,
int family, int family,
const union in_addr_union *in_addr, const union in_addr_union *in_addr,
int ifindex) { int ifindex,
const char *server_name) {
_cleanup_free_ char *name = NULL;
DnsServer *s; DnsServer *s;
assert(m); assert(m);
@ -44,6 +46,12 @@ int dns_server_new(
return -E2BIG; return -E2BIG;
} }
if (server_name) {
name = strdup(server_name);
if (!name)
return -ENOMEM;
}
s = new(DnsServer, 1); s = new(DnsServer, 1);
if (!s) if (!s)
return -ENOMEM; return -ENOMEM;
@ -55,6 +63,7 @@ int dns_server_new(
.family = family, .family = family,
.address = *in_addr, .address = *in_addr,
.ifindex = ifindex, .ifindex = ifindex,
.server_name = TAKE_PTR(name),
}; };
dns_server_reset_features(s); dns_server_reset_features(s);
@ -107,6 +116,7 @@ static DnsServer* dns_server_free(DnsServer *s) {
#endif #endif
free(s->server_string); free(s->server_string);
free(s->server_name);
return mfree(s); return mfree(s);
} }

View File

@ -53,6 +53,8 @@ struct DnsServer {
char *server_string; char *server_string;
char *server_name;
/* The long-lived stream towards this server. */ /* The long-lived stream towards this server. */
DnsStream *stream; DnsStream *stream;
@ -94,7 +96,8 @@ int dns_server_new(
Link *link, Link *link,
int family, int family,
const union in_addr_union *address, const union in_addr_union *address,
int ifindex); int ifindex,
const char *server_string);
DnsServer* dns_server_ref(DnsServer *s); DnsServer* dns_server_ref(DnsServer *s);
DnsServer* dns_server_unref(DnsServer *s); DnsServer* dns_server_unref(DnsServer *s);

View File

@ -67,6 +67,12 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
gnutls_session_set_verify_cert2(gs, &stream->dnstls_data.validation, 1, 0); gnutls_session_set_verify_cert2(gs, &stream->dnstls_data.validation, 1, 0);
} }
if (server->server_name) {
r = gnutls_server_name_set(gs, GNUTLS_NAME_DNS, server->server_name, strlen(server->server_name));
if (r < 0)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to set server name: %s", gnutls_strerror(r));
}
gnutls_handshake_set_timeout(gs, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); gnutls_handshake_set_timeout(gs, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
gnutls_transport_set_ptr2(gs, (gnutls_transport_ptr_t) (long) stream->fd, stream); gnutls_transport_set_ptr2(gs, (gnutls_transport_ptr_t) (long) stream->fd, stream);

View File

@ -87,6 +87,17 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
return -ECONNREFUSED; return -ECONNREFUSED;
} }
if (server->server_name) {
r = SSL_set_tlsext_host_name(s, server->server_name);
if (r <= 0) {
char errbuf[256];
error = ERR_get_error();
ERR_error_string_n(error, errbuf, sizeof(errbuf));
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to set server name: %s", errbuf);
}
}
ERR_clear_error(); ERR_clear_error();
stream->dnstls_data.handshake = SSL_do_handshake(s); stream->dnstls_data.handshake = SSL_do_handshake(s);
if (stream->dnstls_data.handshake <= 0) { if (stream->dnstls_data.handshake <= 0) {

View File

@ -284,7 +284,7 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
if (s) if (s)
dns_server_move_back_and_unmark(s); dns_server_move_back_and_unmark(s);
else { else {
r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i].family, &dns[i].address, 0); r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i].family, &dns[i].address, 0, NULL);
if (r < 0) if (r < 0)
goto clear; goto clear;
} }

View File

@ -269,7 +269,7 @@ static int link_update_dns_server_one(Link *l, const char *name) {
return 0; return 0;
} }
return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, 0); return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, 0, NULL);
} }
static int link_update_dns_servers(Link *l) { static int link_update_dns_servers(Link *l) {

View File

@ -0,0 +1,36 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "alloc-util.h"
#include "in-addr-util.h"
#include "macro.h"
#include "resolved-util.h"
int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) {
_cleanup_free_ char *buf = NULL, *name = NULL;
const char *m;
int r;
assert(s);
m = strchr(s, '#');
if (m) {
name = strdup(m+1);
if (!name)
return -ENOMEM;
buf = strndup(s, m - s);
if (!buf)
return -ENOMEM;
s = buf;
}
r = in_addr_ifindex_from_string_auto(s, family, ret, ifindex);
if (r < 0)
return r;
if (server_name)
*server_name = TAKE_PTR(name);
return r;
}

View File

@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "in-addr-util.h"
int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name);

Some files were not shown because too many files have changed in this diff Show More