]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #14229 from yuwata/nspawn-network-interface-14223
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 5 Dec 2019 07:10:29 +0000 (16:10 +0900)
committerGitHub <noreply@github.com>
Thu, 5 Dec 2019 07:10:29 +0000 (16:10 +0900)
nspawn: do not fail if udev is not running

95 files changed:
TODO
man/resolved.conf.xml
man/rules/meson.build
man/sd_event_add_child.xml
man/sd_event_add_signal.xml
man/systemd.network.xml
meson.build
src/basic/errno-util.h
src/basic/memory-util.h
src/basic/missing_syscall.h
src/basic/ordered-set.h
src/basic/parse-util.c
src/basic/process-util.c
src/basic/process-util.h
src/basic/signal-util.c
src/basic/signal-util.h
src/basic/string-util.c
src/basic/string-util.h
src/basic/tmpfile-util.c
src/basic/user-util.c
src/basic/user-util.h
src/boot/efi/boot.c
src/core/namespace.c
src/dissect/dissect.c
src/libsystemd/libsystemd.sym
src/libsystemd/sd-event/event-source.h
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-event/test-event.c
src/libsystemd/sd-netlink/netlink-message.c
src/network/meson.build
src/network/netdev/bond.c
src/network/netdev/bridge.c
src/network/netdev/dummy.c
src/network/netdev/fou-tunnel.c
src/network/netdev/geneve.c
src/network/netdev/ipvlan.c
src/network/netdev/l2tp-tunnel.c
src/network/netdev/macsec.c
src/network/netdev/macvlan.c
src/network/netdev/netdev.c
src/network/netdev/netdev.h
src/network/netdev/netdevsim.c
src/network/netdev/nlmon.c
src/network/netdev/tunnel.c
src/network/netdev/tuntap.c
src/network/netdev/vcan.c
src/network/netdev/veth.c
src/network/netdev/vlan.c
src/network/netdev/vrf.c
src/network/netdev/vxcan.c
src/network/netdev/vxlan.c
src/network/netdev/wireguard.c
src/network/netdev/xfrm.c
src/network/networkd-link.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/tc/netem.c
src/network/tc/netem.h
src/network/tc/qdisc.c
src/network/tc/qdisc.h
src/network/tc/sfq.c [new file with mode: 0644]
src/network/tc/sfq.h [new file with mode: 0644]
src/network/tc/tbf.c [new file with mode: 0644]
src/network/tc/tbf.h [new file with mode: 0644]
src/nspawn/nspawn.c
src/portable/portable.c
src/resolve/meson.build
src/resolve/resolved-conf.c
src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-server.h
src/resolve/resolved-dnstls-gnutls.c
src/resolve/resolved-dnstls-openssl.c
src/resolve/resolved-link-bus.c
src/resolve/resolved-link.c
src/resolve/resolved-util.c [new file with mode: 0644]
src/resolve/resolved-util.h [new file with mode: 0644]
src/resolve/test-resolved-util.c [new file with mode: 0644]
src/shared/bootspec.c
src/shared/bootspec.h
src/shared/conf-parser.c
src/shared/conf-parser.h
src/shared/dissect-image.c
src/shared/install.c
src/shared/loop-util.c
src/shared/loop-util.h
src/shared/machine-image.c
src/shared/main-func.h
src/systemd/sd-event.h
src/test/test-conf-parser.c
src/test/test-dissect-image.c
src/test/test-string-util.c
test/fuzz/fuzz-network-parser/directives.network
test/test-network/conf/25-qdisc-netem.network [moved from test/test-network/conf/25-qdisc.network with 100% similarity]
test/test-network/conf/25-qdisc-tbf-and-sfq.network [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py

diff --git a/TODO b/TODO
index 88fd9d7f10615d1d2eeab3fbd193c9d1d7f60347..07f65ec80e967d5d46ae015dd5d6b443c6169cb7 100644 (file)
--- a/TODO
+++ b/TODO
@@ -701,7 +701,6 @@ Features:
   - allow multiple signal handlers per signal?
   - document chaining of signal handler for SIGCHLD and child handlers
   - 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
 
index 818000145b93b4b1cdf149572042d4ecea4dcae3..0f70ced5b54f6aba824fee8d4f2cf54e7e95cff2 100644 (file)
         resolver is not capable of authenticating the server, so it is
         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
         <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
         also maintains per-link DNSOverTLS settings. For system DNS
index 20c3d09da08ec8b0dd643764d1b543799007be48..0f6897a080bcb951dac8a8a91a7f65d4ef84ce2b 100644 (file)
@@ -371,7 +371,15 @@ manpages = [
  ['sd_bus_wait', '3', [], ''],
  ['sd_event_add_child',
   '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',
   '3',
index 509803d5c170272f66c5b84c0d1c2e9b4965243a..1db536ff2ad133a5021626d7c483533c99aa9cfb 100644 (file)
 
   <refnamediv>
     <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_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>
 
     <refpurpose>Add a child process state change event source to an event loop</refpurpose>
         <paramdef>void *<parameter>userdata</parameter></paramdef>
       </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>
         <funcdef>int <function>sd_event_source_get_child_pid</function></funcdef>
         <paramdef>sd_event_source *<parameter>source</parameter></paramdef>
         <paramdef>pid_t *<parameter>pid</parameter></paramdef>
       </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>
   </refsynopsisdiv>
 
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_event_add_child()</function> adds a new child
-    process state change event source to an event loop. The event loop
-    object is specified in the <parameter>event</parameter> parameter,
-    the event source object is returned in the
-    <parameter>source</parameter> parameter. The
-    <parameter>pid</parameter> parameter specifies the PID of the
-    process to watch. The <parameter>handler</parameter> must
-    reference a function to call when the process changes state. The
-    handler function will be passed the
-    <parameter>userdata</parameter> pointer, which may be chosen
-    freely by the caller. The handler also receives a pointer to a
-    <structname>siginfo_t</structname> structure containing
-    information about the child process event. The
-    <parameter>options</parameter> parameter determines which state
-    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><function>sd_event_add_child()</function> adds a new child process state change event source to an
+    event loop. The event loop object is specified in the <parameter>event</parameter> parameter, the event
+    source object is returned in the <parameter>source</parameter> parameter. The <parameter>pid</parameter>
+    parameter specifies the PID of the process to watch, which must be a direct child process of the invoking
+    process. The <parameter>handler</parameter> must reference a function to call when the process changes
+    state. The handler function will be passed the <parameter>userdata</parameter> pointer, which may be
+    chosen freely by the caller. The handler also receives a pointer to a <structname>siginfo_t</structname>
+    structure containing information about the child process event. The <parameter>options</parameter>
+    parameter determines which state 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
     child process. The handler is enabled for a single event
     <constant>SD_EVENT_OFF</constant> with
     <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
     <function>sd_event_add_child()</function> is passed as NULL no
     reference to the event source object is returned. In this case the
     processed first, it should leave the child processes for which
     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>
     retrieves the configured PID of a child process state change event
     source created previously with
     pointer to a <type>pid_t</type> variable to return the process ID
     in.
     </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>
         <varlistentry>
           <term><constant>-EBUSY</constant></term>
 
-          <listitem><para>A handler is already installed for this
-          child process.</para></listitem>
+          <listitem><para>A handler is already installed for this child process, or
+          <constant>SIGCHLD</constant> is not blocked.</para></listitem>
 
         </varlistentry>
 
           <listitem><para>The passed event source is not a child process event source.</para></listitem>
         </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>
     </refsect2>
   </refsect1>
       <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_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>
   </refsect1>
 
index a7148ca8dd13e3d6a81e73d6f4a0032dca356c48..43794bd7ceb683cf896aa00a194d282b3bd22111 100644 (file)
     project='man-pages'><refentrytitle>signalfd</refentrytitle><manvolnum>2</manvolnum></citerefentry>
     for further information.</para>
 
-    <para>Only a single handler may be installed for a specific
-    signal. The signal will be unblocked by this call, and must be
-    blocked before this function is called in all threads (using
+    <para>Only a single handler may be installed for a specific signal. The 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>sigprocmask</refentrytitle><manvolnum>2</manvolnum></citerefentry>). If
-    the handler is not specified (<parameter>handler</parameter> is
-    <constant>NULL</constant>), a default handler which causes the
-    program to exit cleanly will be used.</para>
+    project='man-pages'><refentrytitle>pthread_sigmask</refentrytitle><manvolnum>3</manvolnum></citerefentry>). If
+    the handler is not specified (<parameter>handler</parameter> is <constant>NULL</constant>), a default
+    handler which causes the program to exit cleanly will be used.</para>
 
     <para>By default, the event source is enabled permanently
     (<constant>SD_EVENT_ON</constant>), but this may be changed with
       <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 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>
   </refsect1>
 
index a26e08c99cc40966ddbdfbcff5fb8a8163ff6154..a2ac24059a9dcc5eb9faf94c27be0735971de769 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>TokenBufferFilterLatencySec=</varname></term>
+        <listitem>
+          <para>Specifies the latency parameter, which specifies the maximum amount of time a
+          packet can sit in the Token Buffer Filter (TBF). Defaults to unset.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>TokenBufferFilterBurst=</varname></term>
+        <listitem>
+          <para>Specifies the size of the bucket. This is the maximum amount of bytes that tokens
+          can be available for instantaneous transfer. When the size is suffixed with K, M, or G, it is
+          parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000. Defaults to
+          unset.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>TokenBufferFilterRate=</varname></term>
+        <listitem>
+          <para>Specifies the device specific bandwidth. When suffixed with K, M, or G, the specified
+          bandwidth is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000.
+          Defaults to unset.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>StochasticFairnessQueueingPerturbPeriodSec=</varname></term>
+        <listitem>
+          <para>Specifies the interval in seconds for queue algorithm perturbation. Defaults to unset.</para>
+        </listitem>
+      </varlistentry>
+
     </variablelist>
   </refsect1>
 
index 21d6968abdf47e87af26e7b6a83103748f221480..3085af49fd7e19ebf5b7cdf1eebf10e0e169e079 100644 (file)
@@ -517,6 +517,18 @@ foreach ident : [
                                  #include <unistd.h>'''],
         ['get_mempolicy',     '''#include <stdlib.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')
index 8f1be6c00ea38b49e18fec333b6742ae31e47dad..65a6384eeb0457013ff6bb2306d51b49da4eec4a 100644 (file)
@@ -101,3 +101,11 @@ static inline bool ERRNO_IS_PRIVILEGE(int r) {
                       EACCES,
                       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);
+}
index 46a6907a0cf96baf6832e52080d75eee8144e573..4f92a6434a52ecf2436162052345363ac32a0815 100644 (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);
 #endif
 
-static inline void erase_and_freep(void *p) {
-        void *ptr = *(void**) p;
+static inline void* erase_and_free(void *p) {
+        size_t l;
+
+        if (!p)
+                return NULL;
+
+        l = malloc_usable_size(p);
+        explicit_bzero_safe(p, l);
+        free(p);
 
-        if (ptr) {
-                size_t l = malloc_usable_size(ptr);
-                explicit_bzero_safe(ptr, l);
-                free(ptr);
-        }
+        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 */
index 1255d8b1972cca6cfd3f1d579974114ac4cdfde8..cbda3f7c6054793213c883e02933fd1b5a81a247 100644 (file)
@@ -5,8 +5,10 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <signal.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
 #ifdef ARCH_MIPS
@@ -524,3 +526,45 @@ static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask,
 
 #define get_mempolicy missing_get_mempolicy
 #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
index ba43451e27d4af2afde45d82f07bdfe194b6b460..383a729cab869c834b409656d140d8447d97dfc8 100644 (file)
@@ -50,6 +50,10 @@ static inline void* ordered_set_remove(OrderedSet *s, void *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) {
         return ordered_hashmap_steal_first((OrderedHashmap*) s);
 }
index aec6099c9cc175c81ea5818955ba3ae86176a6a5..b81db04989fb11851ef500e8fb8507e723df6385 100644 (file)
@@ -365,7 +365,6 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
         unsigned long l;
 
         assert(s);
-        assert(ret_u);
         assert(base <= 16);
 
         /* 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)
                 return -ERANGE;
 
-        *ret_u = (unsigned) l;
+        if (ret_u)
+                *ret_u = (unsigned) l;
+
         return 0;
 }
 
@@ -398,7 +399,6 @@ int safe_atoi(const char *s, int *ret_i) {
         long l;
 
         assert(s);
-        assert(ret_i);
 
         errno = 0;
         l = strtol(s, &x, 0);
@@ -409,7 +409,9 @@ int safe_atoi(const char *s, int *ret_i) {
         if ((long) (int) l != l)
                 return -ERANGE;
 
-        *ret_i = (int) l;
+        if (ret_i)
+                *ret_i = (int) l;
+
         return 0;
 }
 
@@ -418,7 +420,6 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) {
         unsigned long long l;
 
         assert(s);
-        assert(ret_llu);
 
         s += strspn(s, WHITESPACE);
 
@@ -431,7 +432,9 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) {
         if (*s == '-')
                 return -ERANGE;
 
-        *ret_llu = l;
+        if (ret_llu)
+                *ret_llu = l;
+
         return 0;
 }
 
@@ -440,7 +443,6 @@ int safe_atolli(const char *s, long long int *ret_lli) {
         long long l;
 
         assert(s);
-        assert(ret_lli);
 
         errno = 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)
                 return -EINVAL;
 
-        *ret_lli = l;
+        if (ret_lli)
+                *ret_lli = l;
+
         return 0;
 }
 
@@ -458,7 +462,6 @@ int safe_atou8(const char *s, uint8_t *ret) {
         unsigned long l;
 
         assert(s);
-        assert(ret);
 
         s += strspn(s, WHITESPACE);
 
@@ -473,7 +476,8 @@ int safe_atou8(const char *s, uint8_t *ret) {
         if ((unsigned long) (uint8_t) l != l)
                 return -ERANGE;
 
-        *ret = (uint8_t) l;
+        if (ret)
+                *ret = (uint8_t) l;
         return 0;
 }
 
@@ -507,7 +511,6 @@ int safe_atoi16(const char *s, int16_t *ret) {
         long l;
 
         assert(s);
-        assert(ret);
 
         errno = 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)
                 return -ERANGE;
 
-        *ret = (int16_t) l;
+        if (ret)
+                *ret = (int16_t) l;
+
         return 0;
 }
 
@@ -528,7 +533,6 @@ int safe_atod(const char *s, double *ret_d) {
         double d = 0;
 
         assert(s);
-        assert(ret_d);
 
         loc = newlocale(LC_NUMERIC_MASK, "C", (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)
                 return -EINVAL;
 
-        *ret_d = (double) d;
+        if (ret_d)
+                *ret_d = (double) d;
+
         return 0;
 }
 
index 9b6c4c31f713da377515ddf797900fe887b462ab..d60d8275ca2335686d138d747dcfee4e47a90f74 100644 (file)
@@ -40,6 +40,7 @@
 #include "rlimit-util.h"
 #include "signal-util.h"
 #include "stat-util.h"
+#include "stdio-util.h"
 #include "string-table.h"
 #include "string-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");
                         _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) {
@@ -1488,6 +1496,38 @@ int set_oom_score_adjust(int value) {
                                  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[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",
index 5f4e174f04cb180ee1a8094cbd36e441ed04bd83..57582738b3cf9e639e4cc3bbac91f5efa51331dd 100644 (file)
@@ -147,16 +147,17 @@ void reset_cached_pid(void);
 int must_be_root(void);
 
 typedef enum ForkFlags {
-        FORK_RESET_SIGNALS      = 1 << 0, /* Reset all signal handlers and signal mask */
-        FORK_CLOSE_ALL_FDS      = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */
-        FORK_DEATHSIG           = 1 << 2, /* Set PR_DEATHSIG in the child */
-        FORK_NULL_STDIO         = 1 << 3, /* Connect 0,1,2 to /dev/null */
-        FORK_REOPEN_LOG         = 1 << 4, /* Reopen log connection */
-        FORK_LOG                = 1 << 5, /* Log above LOG_DEBUG log level about failures */
-        FORK_WAIT               = 1 << 6, /* Wait until child exited */
-        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_RLIMIT_NOFILE_SAFE = 1 << 9, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
+        FORK_RESET_SIGNALS      = 1 <<  0, /* Reset all signal handlers and signal mask */
+        FORK_CLOSE_ALL_FDS      = 1 <<  1, /* Close all open file descriptors in the child, except for 0,1,2 */
+        FORK_DEATHSIG           = 1 <<  2, /* Set PR_DEATHSIG in the child */
+        FORK_NULL_STDIO         = 1 <<  3, /* Connect 0,1,2 to /dev/null */
+        FORK_REOPEN_LOG         = 1 <<  4, /* Reopen log connection */
+        FORK_LOG                = 1 <<  5, /* Log above LOG_DEBUG log level about failures */
+        FORK_WAIT               = 1 <<  6, /* Wait until child exited */
+        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_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;
 
 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_;                          \
         })
+
+int pidfd_get_pid(int fd, pid_t *ret);
index bfb83419c98967dfd4ccdd25964f5939f24df7db..cb59f6ca0f2639648989e22f2361f545c414f970 100644 (file)
@@ -287,3 +287,18 @@ int signal_from_string(const char *s) {
 void nop_signal_handler(int sig) {
         /* 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;
+}
index 92f2804cd2142bc24da3f4591993ac42538b3eaf..3909ee341dafacf904d6094cdbf5cd0b7b379ad2 100644 (file)
@@ -41,3 +41,5 @@ static inline const char* signal_to_string_with_check(int n) {
 
         return signal_to_string(n);
 }
+
+int signal_is_blocked(int sig);
index 8e6aa63806811bc8ab4c89094ab98ed86b813b7d..b477a5153420d8ed11fda8aa43f10fcd39a4af85 100644 (file)
@@ -1064,3 +1064,13 @@ bool string_is_safe(const char *p) {
 
         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;
+}
index 04cc82b386088a7eb8b243a48356fa10c3a5a9e0..f10af9ad2f82dd4ab5983a72c37811f3fac7966b 100644 (file)
@@ -278,3 +278,5 @@ static inline char* str_realloc(char **p) {
 
         return (*p = t);
 }
+
+char* string_erase(char *x);
index 7cdaca4e406c29d861006fe5ae427e74ed8f7d80..2d201e1a57f34d918e7a84c3a6d3d437d7306f2c 100644 (file)
 #include "tmpfile-util.h"
 #include "umask-util.h"
 
-int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
-        FILE *f;
-        char *t;
-        int r, fd;
+int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) {
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_free_ char *t = NULL;
+        _cleanup_close_ int fd = -1;
+        int r;
 
-        assert(path);
-        assert(_f);
-        assert(_temp_path);
+        if (path) {
+                r = tempfn_xxxxxx(path, NULL, &t);
+                if (r < 0)
+                        return r;
+        } else {
+                const char *d;
 
-        r = tempfn_xxxxxx(path, NULL, &t);
-        if (r < 0)
-                return r;
+                r = tmp_dir(&d);
+                if (r < 0)
+                        return r;
+
+                t = path_join(d, "XXXXXX");
+                if (!t)
+                        return -ENOMEM;
+        }
 
         fd = mkostemp_safe(t);
-        if (fd < 0) {
-                free(t);
+        if (fd < 0)
                 return -errno;
-        }
 
         /* 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. */
 
         r = fdopen_unlocked(fd, "w", &f);
         if (r < 0) {
-                unlink(t);
-                free(t);
-                safe_close(fd);
+                (void) unlink(t);
                 return r;
         }
 
-        *_f = f;
-        *_temp_path = t;
+        TAKE_FD(fd);
+
+        if (ret_f)
+                *ret_f = TAKE_PTR(f);
+
+        if (ret_temp_path)
+                *ret_temp_path = TAKE_PTR(t);
 
         return 0;
 }
 
 /* This is much like mkostemp() but is subject to umask(). */
 int mkostemp_safe(char *pattern) {
-        _unused_ _cleanup_umask_ mode_t u = umask(0077);
         int fd;
 
         assert(pattern);
 
-        fd = mkostemp(pattern, O_CLOEXEC);
+        RUN_WITH_UMASK(0077)
+                fd = mkostemp(pattern, O_CLOEXEC);
         if (fd < 0)
                 return -errno;
 
index 93ba4eedd2772deda7c14aa6e413e042ba8ca10f..985a669a1b1b04292f98c43cff05a4b12bcd8aee 100644 (file)
@@ -84,7 +84,7 @@ char *getusername_malloc(void) {
         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,
                            /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice
index cfa515f5e8a26be33e2a354dfc206ce84d91f170..7488071086ef135781d78d9384eab27e4bbfd5a9 100644 (file)
@@ -57,6 +57,14 @@ int take_etc_passwd_lock(const char *root);
 
 #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) {
         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);
 }
 
-static inline bool uid_is_system(uid_t uid) {
-        return uid <= SYSTEM_UID_MAX;
+static inline bool uid_is_container(uid_t uid) {
+        return CONTAINER_UID_BASE_MIN <= uid && uid <= CONTAINER_UID_BASE_MAX;
 }
 
-static inline bool gid_is_system(gid_t gid) {
-        return gid <= SYSTEM_GID_MAX;
+static inline bool gid_is_container(gid_t gid) {
+        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
@@ -127,3 +135,5 @@ int putsgent_sane(const struct sgrp *sg, FILE *stream);
 #endif
 
 int make_salt(char **ret);
+
+bool is_nologin_shell(const char *shell);
index 20ded3a63025ea1721c442a9a0e3f334fe6c4f1a..84293364b47eb03fd23cd0bed99d04bec966c520 100644 (file)
@@ -32,7 +32,7 @@ enum loader_type {
 };
 
 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;
         CHAR16 *version;
@@ -1310,7 +1310,6 @@ static VOID config_entry_add_from_file(
         CHAR8 *line;
         UINTN pos = 0;
         CHAR8 *key, *value;
-        UINTN len;
         EFI_STATUS err;
         EFI_FILE_HANDLE handle;
         _cleanup_freepool_ CHAR16 *initrd = NULL;
@@ -1431,10 +1430,6 @@ static VOID config_entry_add_from_file(
 
         entry->device = device;
         entry->id = StrDuplicate(file);
-        len = StrLen(entry->id);
-        /* remove ".conf" */
-        if (len > 5)
-                entry->id[len - 5] = '\0';
         StrLwr(entry->id);
 
         config_add_entry(config, entry);
@@ -1775,7 +1770,8 @@ static ConfigEntry *config_entry_add_loader(
                 CHAR16 *id,
                 CHAR16 key,
                 CHAR16 *title,
-                CHAR16 *loader) {
+                CHAR16 *loader,
+                CHAR16 *version) {
 
         ConfigEntry *entry;
 
@@ -1783,6 +1779,7 @@ static ConfigEntry *config_entry_add_loader(
         *entry = (ConfigEntry) {
                 .type = type,
                 .title = StrDuplicate(title),
+                .version = StrDuplicate(version),
                 .device = device,
                 .loader = StrDuplicate(loader),
                 .id = StrDuplicate(id),
@@ -1840,7 +1837,7 @@ static BOOLEAN config_entry_add_loader_auto(
                 return FALSE;
         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)
                 return FALSE;
 
@@ -1908,10 +1905,12 @@ static VOID config_entry_add_linux(
                 CHAR8 *line;
                 UINTN pos = 0;
                 CHAR8 *key, *value;
+                CHAR16 *os_name_pretty = NULL;
                 CHAR16 *os_name = NULL;
                 CHAR16 *os_id = 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);
                 if (bufsize == 0 || EFI_ERROR(err))
@@ -1927,6 +1926,8 @@ static VOID config_entry_add_linux(
                         continue;
                 if (StriCmp(f->FileName + len - 4, L".efi") != 0)
                         continue;
+                if (StrnCmp(f->FileName, L"auto-", 5) == 0)
+                        continue;
 
                 /* look for .osrel and .cmdline sections in the .efi binary */
                 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 */
                 while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) {
                         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);
                                 os_name = stra_to_str(value);
                                 continue;
@@ -1957,20 +1964,27 @@ static VOID config_entry_add_linux(
                                 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) {
-                                FreePool(os_build);
-                                os_build = stra_to_str(value);
+                                FreePool(os_build_id);
+                                os_build_id = stra_to_str(value);
                                 continue;
                         }
                 }
 
-                if (os_name && os_id && (os_version || os_build)) {
-                        _cleanup_freepool_ CHAR16 *conf = NULL, *path = NULL;
+                if ((os_name_pretty || os_name) && os_id && (os_version || os_version_id || os_build_id)) {
+                        _cleanup_freepool_ CHAR16 *path = NULL;
 
-                        conf = PoolPrint(L"%s-%s", os_id, os_version ? : os_build);
                         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);
                         content = NULL;
@@ -1989,10 +2003,12 @@ static VOID config_entry_add_linux(
                         config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi");
                 }
 
+                FreePool(os_name_pretty);
                 FreePool(os_name);
                 FreePool(os_id);
                 FreePool(os_version);
-                FreePool(os_build);
+                FreePool(os_version_id);
+                FreePool(os_build_id);
                 FreePool(content);
         }
 
index c0ff44c5137d46368a0be35b5cab1a0e52d2dbaf..104e96193d7de7dd756bf3e2d3da9b8538d5c9ec 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include <errno.h>
+#include <linux/loop.h>
 #include <sched.h>
 #include <stdio.h>
 #include <sys/mount.h>
@@ -1220,6 +1221,7 @@ int setup_namespace(
 
                 r = loop_device_make_by_path(root_image,
                                              dissect_image_flags & DISSECT_IMAGE_READ_ONLY ? O_RDONLY : O_RDWR,
+                                             LO_FLAGS_PARTSCAN,
                                              &loop_device);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to create loop device for root image: %m");
index 50de0afce6f006cac3d36b72434f51a04618e82d..c1be6c034c5619714e0992283294c345c12ecbbf 100644 (file)
@@ -1,8 +1,9 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include <fcntl.h>
-#include <stdio.h>
 #include <getopt.h>
+#include <linux/loop.h>
+#include <stdio.h>
 
 #include "architecture.h"
 #include "dissect-image.h"
@@ -171,7 +172,7 @@ static int run(int argc, char *argv[]) {
         if (r <= 0)
                 return r;
 
-        r = loop_device_make_by_path(arg_image, (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR, &d);
+        r = loop_device_make_by_path(arg_image, (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR, LO_FLAGS_PARTSCAN, &d);
         if (r < 0)
                 return log_error_errno(r, "Failed to set up loopback device: %m");
 
index 5ec42e0f1f826eb602ce121e2650012b3250f930..bed81bf17321cc953421fefc55480b3b36392afd 100644 (file)
@@ -682,3 +682,14 @@ global:
         sd_bus_object_vtable_format;
         sd_event_source_disable_unref;
 } 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;
index 99ab8fc1695f41bf515a9736f1db6399203a9132..08eb9b6a611e268c08c64654fcd215085058c798 100644 (file)
@@ -34,7 +34,7 @@ typedef enum EventSourceType {
  * we know how to dispatch it */
 typedef enum WakeupType {
         WAKEUP_NONE,
-        WAKEUP_EVENT_SOURCE,
+        WAKEUP_EVENT_SOURCE, /* either I/O or pidfd wakeup */
         WAKEUP_CLOCK_DATA,
         WAKEUP_SIGNAL_DATA,
         WAKEUP_INOTIFY_DATA,
@@ -96,6 +96,12 @@ struct sd_event_source {
                         siginfo_t siginfo;
                         pid_t pid;
                         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;
                 struct {
                         sd_event_handler_t callback;
index b76dbf3d230e1f2f5fe9de3830066fb2868815b3..4940345791149d1226d885a5677a1d70b27ee928 100644 (file)
@@ -9,6 +9,7 @@
 #include "sd-id128.h"
 
 #include "alloc-util.h"
+#include "env-util.h"
 #include "event-source.h"
 #include "fd-util.h"
 #include "fs-util.h"
 
 #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] = {
         [SOURCE_IO] = "io",
         [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) {
-        int r;
-
         assert(s);
         assert(s->type == SOURCE_IO);
 
@@ -367,8 +374,7 @@ static void source_io_unregister(sd_event_source *s) {
         if (!s->io.registered)
                 return;
 
-        r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL);
-        if (r < 0)
+        if (epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, 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));
 
@@ -404,6 +410,51 @@ static int source_io_register(
         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) {
 
         switch (t) {
@@ -614,9 +665,8 @@ static void event_gc_signal_data(sd_event *e, const int64_t *priority, int sig)
 
         assert(e);
 
-        /* Rechecks if the specified signal is still something we are
-         * interested in. If not, we'll unmask it, and possibly drop
-         * the signalfd for it. */
+        /* Rechecks if the specified signal is still something we are interested in. If not, we'll unmask it,
+         * and possibly drop the signalfd for it. */
 
         if (sig == SIGCHLD &&
             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));
-                        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;
 
         case SOURCE_DEFER:
@@ -790,6 +844,44 @@ static void source_free(sd_event_source *s) {
         if (s->type == SOURCE_IO && s->io.owned)
                 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)
                 s->destroy_callback(s->userdata);
 
@@ -1073,7 +1165,6 @@ _public_ int sd_event_add_signal(
 
         _cleanup_(source_freep) sd_event_source *s = NULL;
         struct signal_data *d;
-        sigset_t ss;
         int r;
 
         assert_return(e, -EINVAL);
@@ -1085,11 +1176,10 @@ _public_ int sd_event_add_signal(
         if (!callback)
                 callback = signal_exit_callback;
 
-        r = pthread_sigmask(SIG_SETMASK, NULL, &ss);
-        if (r != 0)
-                return -r;
-
-        if (!sigismember(&ss, sig))
+        r = signal_is_blocked(sig);
+        if (r < 0)
+                return r;
+        if (r == 0)
                 return -EBUSY;
 
         if (!e->signal_sources) {
@@ -1124,6 +1214,11 @@ _public_ int sd_event_add_signal(
         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(
                 sd_event *e,
                 sd_event_source **ret,
@@ -1144,6 +1239,20 @@ _public_ int sd_event_add_child(
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         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);
         if (r < 0)
                 return r;
@@ -1155,30 +1264,144 @@ _public_ int sd_event_add_child(
         if (!s)
                 return -ENOMEM;
 
+        s->wakeup = WAKEUP_EVENT_SOURCE;
         s->child.pid = pid;
         s->child.options = options;
         s->child.callback = callback;
         s->userdata = userdata;
         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++;
 
-        r = event_make_signal_data(e, SIGCHLD, NULL);
-        if (r < 0) {
-                e->n_enabled_child_sources--;
-                return r;
-        }
+        if (EVENT_SOURCE_WATCH_PIDFD(s)) {
+                /* We have a pidfd and we only want to watch for exit */
+
+                r = source_child_pidfd_register(s, s->enabled);
+                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;
+                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);
+        if (r < 0)
+                return r;
+
+        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);
+                if (r < 0) {
+                        e->n_enabled_child_sources--;
+                        return r;
+                }
+
+                e->need_process_child = true;
+        }
+
+        if (ret)
+                *ret = s;
+
+        TAKE_PTR(s);
         return 0;
 }
 
@@ -1765,7 +1988,7 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
                         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;
@@ -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);
                         s->event->n_enabled_child_sources--;
 
-                        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;
 
                 case SOURCE_EXIT:
@@ -2100,12 +2327,25 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
 
                         s->enabled = m;
 
-                        r = event_make_signal_data(s->event, SIGCHLD, NULL);
-                        if (r < 0) {
-                                s->enabled = SD_EVENT_OFF;
-                                s->event->n_enabled_child_sources--;
-                                event_gc_signal_data(s->event, &s->priority, SIGCHLD);
-                                return r;
+                        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);
+                                if (r < 0) {
+                                        s->enabled = SD_EVENT_OFF;
+                                        s->event->n_enabled_child_sources--;
+                                        event_gc_signal_data(s->event, &s->priority, SIGCHLD);
+                                        return r;
+                                }
                         }
 
                         break;
@@ -2228,6 +2468,98 @@ _public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) {
         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) {
         assert_return(s, -EINVAL);
         assert_return(mask, -EINVAL);
@@ -2538,6 +2870,12 @@ static int process_child(sd_event *e) {
                 if (s->enabled == SD_EVENT_OFF)
                         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);
                 r = waitid(P_PID, s->child.pid, &s->child.siginfo,
                            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) {
                         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 the child isn't dead then let's
                                  * immediately remove the state change
@@ -2566,6 +2907,33 @@ static int process_child(sd_event *e) {
         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) {
         bool read_one = false;
         int r;
@@ -2850,8 +3218,10 @@ static int source_dispatch(sd_event_source *s) {
                 r = s->child.callback(s, &s->child.siginfo, s->userdata);
 
                 /* Now, reap the PID for good. */
-                if (zombie)
+                if (zombie) {
                         (void) waitid(P_PID, s->child.pid, &s->child.siginfo, WNOHANG|WEXITED);
+                        s->child.waited = true;
+                }
 
                 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_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)
                 goto pending;
 
@@ -3147,12 +3522,33 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
 
                         switch (*t) {
 
-                        case WAKEUP_EVENT_SOURCE:
-                                r = process_io(e, ev_queue[i].data.ptr, ev_queue[i].events);
+                        case WAKEUP_EVENT_SOURCE: {
+                                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;
+
+                                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: {
                                 struct clock_data *d = ev_queue[i].data.ptr;
+
+                                assert(d);
+
                                 r = flush_timer(e, d->fd, ev_queue[i].events, &d->next);
                                 break;
                         }
@@ -3476,7 +3872,7 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) {
 
         } else {
                 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);
                 }
         }
index 954b93ada0cf8b314a6ebe021184cc1bd0892cfc..54d293ca4619153ca9fd5434783597c1604e38bc 100644 (file)
@@ -9,6 +9,7 @@
 #include "fs-util.h"
 #include "log.h"
 #include "macro.h"
+#include "missing_syscall.h"
 #include "parse-util.h"
 #include "path-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(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));
 
         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) {
         sd_event_source *p = NULL;
         pid_t pid;
+        siginfo_t plain_si;
 
         assert_se(s);
         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(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
+        assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGUSR2, -1) >= 0);
 
         pid = fork();
         assert_se(pid >= 0);
 
-        if (pid == 0)
-                _exit(EXIT_SUCCESS);
+        if (pid == 0) {
+                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_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);
 
@@ -119,7 +151,7 @@ static int defer_handler(sd_event_source *s, void *userdata) {
         return 1;
 }
 
-static bool do_quit = false;
+static bool do_quit;
 
 static int time_handler(sd_event_source *s, uint64_t usec, void *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;
 }
 
-static void test_basic(void) {
+static void test_basic(bool with_pidfd) {
         sd_event *e = NULL;
         sd_event_source *w = NULL, *x = NULL, *y = NULL, *z = NULL, *q = NULL, *t = NULL;
         static const char ch = 'x';
@@ -169,6 +201,8 @@ static void test_basic(void) {
         uint64_t event_now;
         int64_t priority;
 
+        assert_se(setenv("SYSTEMD_PIDFD", yes_no(with_pidfd), 1) >= 0);
+
         assert_se(pipe(a) >= 0);
         assert_se(pipe(b) >= 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, &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_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(d);
         safe_close_pair(k);
+
+        assert_se(unsetenv("SYSTEMD_PIDFD") >= 0);
 }
 
 static void test_sd_event_now(void) {
@@ -482,15 +520,89 @@ static void test_inotify(unsigned n_create_events) {
         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[]) {
         test_setup_logging(LOG_INFO);
 
-        test_basic();
+        test_basic(true);   /* test with pidfd */
+        test_basic(false);  /* test without pidfd */
+
         test_sd_event_now();
         test_rtqueue();
 
         test_inotify(100); /* should work without overflow */
         test_inotify(33000); /* should trigger a q overflow */
 
+        test_pidfd();
+
         return 0;
 }
index 34b66e6fa605defff13d192ca5ad7e43fe26a523..1569f34cc41ac5eb22091a730b275168b7d40a9d 100644 (file)
@@ -532,7 +532,6 @@ int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type) {
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
-        assert_return(m->n_containers > 0, -EINVAL);
 
         r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
         if (r < 0)
index d502279151cfc855918fc46cfde50bdc17aeacaf..e2324a01b38006bc110e89645b767ce65ffb9bdc 100644 (file)
@@ -109,6 +109,10 @@ sources = files('''
         tc/netem.h
         tc/qdisc.c
         tc/qdisc.h
+        tc/sfq.c
+        tc/sfq.h
+        tc/tbf.c
+        tc/tbf.h
         tc/tc-util.c
         tc/tc-util.h
 '''.split())
index 185b155440e77e6d2bce4413ed45805a71028b69..8df39e35843f625af265483a35811bdf014a8bae 100644 (file)
@@ -587,7 +587,7 @@ const NetDevVTable bond_vtable = {
         .object_size = sizeof(Bond),
         .init = bond_init,
         .done = bond_done,
-        .sections = "Match\0NetDev\0Bond\0",
+        .sections = NETDEV_COMMON_SECTIONS "Bond\0",
         .fill_message_create = netdev_bond_fill_message_create,
         .create_type = NETDEV_CREATE_MASTER,
         .generate_mac = true,
index 59a40faef8fa2cc639baef2b04f73f1f82a8adf8..6b8f9944612ea39765f4a3ead0ceee9813d6099f 100644 (file)
@@ -355,7 +355,7 @@ static void bridge_init(NetDev *n) {
 const NetDevVTable bridge_vtable = {
         .object_size = sizeof(Bridge),
         .init = bridge_init,
-        .sections = "Match\0NetDev\0Bridge\0",
+        .sections = NETDEV_COMMON_SECTIONS "Bridge\0",
         .post_create = netdev_bridge_post_create,
         .create_type = NETDEV_CREATE_MASTER,
 };
index 23c733cbe7b64d00018c7ff15253209df2c744dc..e06dc02f3aa37bc9b1b913dd87221f41cf4905d5 100644 (file)
@@ -4,7 +4,7 @@
 
 const NetDevVTable dummy_vtable = {
         .object_size = sizeof(Dummy),
-        .sections = "Match\0NetDev\0",
+        .sections = NETDEV_COMMON_SECTIONS,
         .create_type = NETDEV_CREATE_INDEPENDENT,
         .generate_mac = true,
 };
index 7627ccee9cd98bfdef7da3eab896dc4f1c5d0eea..3cc273c7fedafccbfe915a52452a74de8683ceff 100644 (file)
@@ -262,7 +262,7 @@ static void fou_tunnel_init(NetDev *netdev) {
 const NetDevVTable foutnl_vtable = {
         .object_size = sizeof(FouTunnel),
         .init = fou_tunnel_init,
-        .sections = "Match\0NetDev\0FooOverUDP\0",
+        .sections = NETDEV_COMMON_SECTIONS "FooOverUDP\0",
         .create = netdev_fou_tunnel_create,
         .create_type = NETDEV_CREATE_INDEPENDENT,
         .config_verify = netdev_fou_tunnel_verify,
index 771e0292def6aa8f9a4ed22aa3b4a341ca955d3c..b960840a543ea10f1eccec902c9b4906a2d0e2a3 100644 (file)
@@ -348,7 +348,7 @@ static void geneve_init(NetDev *netdev) {
 const NetDevVTable geneve_vtable = {
         .object_size = sizeof(Geneve),
         .init = geneve_init,
-        .sections = "Match\0NetDev\0GENEVE\0",
+        .sections = NETDEV_COMMON_SECTIONS "GENEVE\0",
         .create = netdev_geneve_create,
         .create_type = NETDEV_CREATE_INDEPENDENT,
         .config_verify = netdev_geneve_verify,
index 53b4bc944fd4692db3b06e436c271d298c8d64fa..1d87cfa865a13f853a35e491e4beea3ad87e7ea1 100644 (file)
@@ -74,7 +74,7 @@ static void ipvlan_init(NetDev *n) {
 const NetDevVTable ipvlan_vtable = {
         .object_size = sizeof(IPVlan),
         .init = ipvlan_init,
-        .sections = "Match\0NetDev\0IPVLAN\0",
+        .sections = NETDEV_COMMON_SECTIONS "IPVLAN\0",
         .fill_message_create = netdev_ipvlan_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .generate_mac = true,
@@ -83,7 +83,7 @@ const NetDevVTable ipvlan_vtable = {
 const NetDevVTable ipvtap_vtable = {
         .object_size = sizeof(IPVlan),
         .init = ipvlan_init,
-        .sections = "Match\0NetDev\0IPVTAP\0",
+        .sections = NETDEV_COMMON_SECTIONS "IPVTAP\0",
         .fill_message_create = netdev_ipvlan_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .generate_mac = true,
index f10506af1ea0761012959a20ba16c5acf6da85b4..19683c0a0049265cc09fa9ff83dea0fa9ec32662 100644 (file)
@@ -723,7 +723,7 @@ static void l2tp_tunnel_done(NetDev *netdev) {
 const NetDevVTable l2tptnl_vtable = {
         .object_size = sizeof(L2tpTunnel),
         .init = l2tp_tunnel_init,
-        .sections = "Match\0NetDev\0L2TP\0L2TPSession\0",
+        .sections = NETDEV_COMMON_SECTIONS "L2TP\0L2TPSession\0",
         .create_after_configured = l2tp_create_tunnel,
         .done = l2tp_tunnel_done,
         .create_type = NETDEV_CREATE_AFTER_CONFIGURED,
index 25dc23ff03388d10e72d31ed6406d3522f6881ab..010e16bcaf802d80bd62ae0290886a2c1953db29 100644 (file)
@@ -1235,7 +1235,7 @@ static void macsec_done(NetDev *netdev) {
 const NetDevVTable macsec_vtable = {
         .object_size = sizeof(MACsec),
         .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,
         .post_create = netdev_macsec_configure,
         .done = macsec_done,
index fe596c295ac60180cf1d8f54ab2c1f63fbcd5108..dbe25e9e34fb103e9cee6e060004e5335171d5d3 100644 (file)
@@ -58,7 +58,7 @@ static void macvlan_init(NetDev *n) {
 const NetDevVTable macvtap_vtable = {
         .object_size = sizeof(MacVlan),
         .init = macvlan_init,
-        .sections = "Match\0NetDev\0MACVTAP\0",
+        .sections = NETDEV_COMMON_SECTIONS "MACVTAP\0",
         .fill_message_create = netdev_macvlan_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .generate_mac = true,
@@ -67,7 +67,7 @@ const NetDevVTable macvtap_vtable = {
 const NetDevVTable macvlan_vtable = {
         .object_size = sizeof(MacVlan),
         .init = macvlan_init,
-        .sections = "Match\0NetDev\0MACVLAN\0",
+        .sections = NETDEV_COMMON_SECTIONS "MACVLAN\0",
         .fill_message_create = netdev_macvlan_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .generate_mac = true,
index 6908c4e811b0c11af90fe9e1046e6644d371b469..423750a6a0e4cf9f573b8ef83a05bfb951bf2c8c 100644 (file)
@@ -682,9 +682,9 @@ int netdev_load_one(Manager *manager, const char *filename) {
 
         dropin_dirname = strjoina(basename(filename), ".d");
         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_PARSE_WARN|CONFIG_PARSE_RELAXED, netdev_raw);
+                              CONFIG_PARSE_WARN, netdev_raw);
         if (r < 0)
                 return r;
 
index 8d16ec5769685eff8dbc28f9fe33d0570354e988..078d0aca4f9657bf8279dc9610769f5f1a00e5c4 100644 (file)
@@ -8,6 +8,35 @@
 #include "networkd-link.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;
 
 struct netdev_join_callback {
index 96f3932ccb910eb7451a79f97517cb7c9fb611c4..bfd2a16035c33539a1b2cd1de3f2ff86285a5a6b 100644 (file)
@@ -4,7 +4,7 @@
 
 const NetDevVTable netdevsim_vtable = {
         .object_size = sizeof(NetDevSim),
-        .sections = "Match\0NetDev\0",
+        .sections = NETDEV_COMMON_SECTIONS,
         .create_type = NETDEV_CREATE_INDEPENDENT,
         .generate_mac = true,
 };
index 3a6179f5037dff79af467dbca66e275be56b81c7..30e49a55abce9e6458ac1be52a23af0074c726dc 100644 (file)
@@ -16,7 +16,7 @@ static int netdev_nlmon_verify(NetDev *netdev, const char *filename) {
 
 const NetDevVTable nlmon_vtable = {
         .object_size = sizeof(NLMon),
-        .sections = "Match\0NetDev\0",
+        .sections = NETDEV_COMMON_SECTIONS,
         .create_type = NETDEV_CREATE_INDEPENDENT,
         .config_verify = netdev_nlmon_verify,
 };
index 8b79051ef579d86a467acee32169614c7e6f1473..8b0e3d27eae3194510802c6c50dac03fee5ba060 100644 (file)
@@ -805,7 +805,7 @@ static void ip6tnl_init(NetDev *n) {
 const NetDevVTable ipip_vtable = {
         .object_size = sizeof(Tunnel),
         .init = ipip_sit_init,
-        .sections = "Match\0NetDev\0Tunnel\0",
+        .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_ipip_sit_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .config_verify = netdev_tunnel_verify,
@@ -815,7 +815,7 @@ const NetDevVTable ipip_vtable = {
 const NetDevVTable sit_vtable = {
         .object_size = sizeof(Tunnel),
         .init = ipip_sit_init,
-        .sections = "Match\0NetDev\0Tunnel\0",
+        .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_ipip_sit_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .config_verify = netdev_tunnel_verify,
@@ -825,7 +825,7 @@ const NetDevVTable sit_vtable = {
 const NetDevVTable vti_vtable = {
         .object_size = sizeof(Tunnel),
         .init = vti_init,
-        .sections = "Match\0NetDev\0Tunnel\0",
+        .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_vti_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .config_verify = netdev_tunnel_verify,
@@ -835,7 +835,7 @@ const NetDevVTable vti_vtable = {
 const NetDevVTable vti6_vtable = {
         .object_size = sizeof(Tunnel),
         .init = vti_init,
-        .sections = "Match\0NetDev\0Tunnel\0",
+        .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_vti_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .config_verify = netdev_tunnel_verify,
@@ -845,7 +845,7 @@ const NetDevVTable vti6_vtable = {
 const NetDevVTable gre_vtable = {
         .object_size = sizeof(Tunnel),
         .init = gre_erspan_init,
-        .sections = "Match\0NetDev\0Tunnel\0",
+        .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_gre_erspan_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .config_verify = netdev_tunnel_verify,
@@ -855,7 +855,7 @@ const NetDevVTable gre_vtable = {
 const NetDevVTable gretap_vtable = {
         .object_size = sizeof(Tunnel),
         .init = gre_erspan_init,
-        .sections = "Match\0NetDev\0Tunnel\0",
+        .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_gre_erspan_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .config_verify = netdev_tunnel_verify,
@@ -865,7 +865,7 @@ const NetDevVTable gretap_vtable = {
 const NetDevVTable ip6gre_vtable = {
         .object_size = sizeof(Tunnel),
         .init = ip6gre_init,
-        .sections = "Match\0NetDev\0Tunnel\0",
+        .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_ip6gre_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .config_verify = netdev_tunnel_verify,
@@ -875,7 +875,7 @@ const NetDevVTable ip6gre_vtable = {
 const NetDevVTable ip6gretap_vtable = {
         .object_size = sizeof(Tunnel),
         .init = ip6gre_init,
-        .sections = "Match\0NetDev\0Tunnel\0",
+        .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_ip6gre_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .config_verify = netdev_tunnel_verify,
@@ -885,7 +885,7 @@ const NetDevVTable ip6gretap_vtable = {
 const NetDevVTable ip6tnl_vtable = {
         .object_size = sizeof(Tunnel),
         .init = ip6tnl_init,
-        .sections = "Match\0NetDev\0Tunnel\0",
+        .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_ip6tnl_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .config_verify = netdev_tunnel_verify,
@@ -895,7 +895,7 @@ const NetDevVTable ip6tnl_vtable = {
 const NetDevVTable erspan_vtable = {
         .object_size = sizeof(Tunnel),
         .init = gre_erspan_init,
-        .sections = "Match\0NetDev\0Tunnel\0",
+        .sections = NETDEV_COMMON_SECTIONS "Tunnel\0",
         .fill_message_create = netdev_gre_erspan_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .config_verify = netdev_tunnel_verify,
index ce69ef4124ac45157cb7bfb5b58a26f8ce697b58..aef72e7bbb47824f62875db96bece909198fbad4 100644 (file)
@@ -147,7 +147,7 @@ static int tuntap_verify(NetDev *netdev, const char *filename) {
 
 const NetDevVTable tun_vtable = {
         .object_size = sizeof(TunTap),
-        .sections = "Match\0NetDev\0Tun\0",
+        .sections = NETDEV_COMMON_SECTIONS "Tun\0",
         .config_verify = tuntap_verify,
         .done = tuntap_done,
         .create = netdev_create_tuntap,
@@ -156,7 +156,7 @@ const NetDevVTable tun_vtable = {
 
 const NetDevVTable tap_vtable = {
         .object_size = sizeof(TunTap),
-        .sections = "Match\0NetDev\0Tap\0",
+        .sections = NETDEV_COMMON_SECTIONS "Tap\0",
         .config_verify = tuntap_verify,
         .done = tuntap_done,
         .create = netdev_create_tuntap,
index 9b3ab48f1afcc7032e6ae148d85a980adef262b8..9a0f87b1e187033de68ae5af2342a67b7f4f5627 100644 (file)
@@ -4,7 +4,7 @@
 
 const NetDevVTable vcan_vtable = {
         .object_size = sizeof(VCan),
-        .sections = "Match\0NetDev\0",
+        .sections = NETDEV_COMMON_SECTIONS,
         .create_type = NETDEV_CREATE_INDEPENDENT,
         .generate_mac = true,
 };
index 4ed9e81511d69e4ee13a18e79bf57f234b8ef184..98bbe8681991ac885a19cb43c4c592d6ff116cb6 100644 (file)
@@ -85,7 +85,7 @@ static void veth_done(NetDev *n) {
 
 const NetDevVTable veth_vtable = {
         .object_size = sizeof(Veth),
-        .sections = "Match\0NetDev\0Peer\0",
+        .sections = NETDEV_COMMON_SECTIONS "Peer\0",
         .done = veth_done,
         .fill_message_create = netdev_veth_fill_message_create,
         .create_type = NETDEV_CREATE_INDEPENDENT,
index 1b4e5bc2d387074b40244c8529b98c89a4a67385..902aa804ffb527a81b0c866a55fb5ae52063239d 100644 (file)
@@ -85,7 +85,7 @@ static void vlan_init(NetDev *netdev) {
 const NetDevVTable vlan_vtable = {
         .object_size = sizeof(VLan),
         .init = vlan_init,
-        .sections = "Match\0NetDev\0VLAN\0",
+        .sections = NETDEV_COMMON_SECTIONS "VLAN\0",
         .fill_message_create = netdev_vlan_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .config_verify = netdev_vlan_verify,
index dc7a9672306640f15c364391aed6b70b6b94c36f..a8ed3fadcbebece67f8f73f102205d22dbf785df 100644 (file)
@@ -25,7 +25,7 @@ static int netdev_vrf_fill_message_create(NetDev *netdev, Link *link, sd_netlink
 
 const NetDevVTable vrf_vtable = {
         .object_size = sizeof(Vrf),
-        .sections = "Match\0NetDev\0VRF\0",
+        .sections = NETDEV_COMMON_SECTIONS "VRF\0",
         .fill_message_create = netdev_vrf_fill_message_create,
         .create_type = NETDEV_CREATE_MASTER,
         .generate_mac = true,
index d1c3f091c35808d1060bd61872225e235190b4fd..1a5786a0b7125691dca820ea5f80a0a2e1996972 100644 (file)
@@ -65,7 +65,7 @@ static void vxcan_done(NetDev *n) {
 
 const NetDevVTable vxcan_vtable = {
         .object_size = sizeof(VxCan),
-        .sections = "Match\0NetDev\0VXCAN\0",
+        .sections = NETDEV_COMMON_SECTIONS "VXCAN\0",
         .done = vxcan_done,
         .fill_message_create = netdev_vxcan_fill_message_create,
         .create_type = NETDEV_CREATE_INDEPENDENT,
index 92f6005dcd83bd1f3ea807d07c8bf161bc2639a3..ace3c5d2ed390216135c8d5652b789f0a9043939 100644 (file)
@@ -371,7 +371,7 @@ static void vxlan_init(NetDev *netdev) {
 const NetDevVTable vxlan_vtable = {
         .object_size = sizeof(VxLan),
         .init = vxlan_init,
-        .sections = "Match\0NetDev\0VXLAN\0",
+        .sections = NETDEV_COMMON_SECTIONS "VXLAN\0",
         .fill_message_create = netdev_vxlan_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
         .config_verify = netdev_vxlan_verify,
index 4a1228584d794f866b758421b83997594b4ba04c..71ff41d574d52683ba59475beaea0a11a41c5fa2 100644 (file)
@@ -967,7 +967,7 @@ static int wireguard_verify(NetDev *netdev, const char *filename) {
 
 const NetDevVTable wireguard_vtable = {
         .object_size = sizeof(Wireguard),
-        .sections = "Match\0NetDev\0WireGuard\0WireGuardPeer\0",
+        .sections = NETDEV_COMMON_SECTIONS "WireGuard\0WireGuardPeer\0",
         .post_create = netdev_wireguard_post_create,
         .init = wireguard_init,
         .done = wireguard_done,
index 7157af4df3e4ad127587606868f54051c4a8e31f..ff8ff35689706f641fe3a9a41b3e4b8d236d2d8c 100644 (file)
@@ -27,7 +27,7 @@ static int xfrm_fill_message_create(NetDev *netdev, Link *link, sd_netlink_messa
 
 const NetDevVTable xfrm_vtable = {
         .object_size = sizeof(Xfrm),
-        .sections = "Match\0NetDev\0Xfrm\0",
+        .sections = NETDEV_COMMON_SECTIONS "Xfrm\0",
         .fill_message_create = xfrm_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED
 };
index fc822d10523ae6a65a93b7db485c7d53f96ac653..e650ef7e0a7260a795e62a22171199403d0f2b69 100644 (file)
@@ -2585,7 +2585,7 @@ static int link_drop_config(Link *link) {
 }
 
 static int link_configure_qdiscs(Link *link) {
-        QDiscs *qdisc;
+        QDisc *qdisc;
         Iterator i;
         int r;
 
@@ -2601,7 +2601,7 @@ static int link_configure_qdiscs(Link *link) {
         if (link->qdisc_messages == 0)
                 link->qdiscs_configured = true;
         else
-                log_link_debug(link, "Configuring QDiscs");
+                log_link_debug(link, "Configuring queuing discipline (qdisc)");
 
         return 0;
 }
index f314b1ec16ef09d34b9a9b7ade7faa5d2a4a1b10..1bfd76ec735d4918fdc5e816e881007f4111cd06 100644 (file)
@@ -244,12 +244,16 @@ CAN.BitRate,                            config_parse_si_size,
 CAN.SamplePoint,                        config_parse_permille,                           0,                             offsetof(Network, can_sample_point)
 CAN.RestartSec,                         config_parse_sec,                                0,                             offsetof(Network, can_restart_us)
 CAN.TripleSampling,                     config_parse_tristate,                           0,                             offsetof(Network, can_triple_sampling)
-TrafficControlQueueingDiscipline.Parent,                             config_parse_tc_qdiscs_parent,                     0,                             0
-TrafficControlQueueingDiscipline.NetworkEmulatorDelaySec,            config_parse_tc_network_emulator_delay,            0,                             0
-TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec,      config_parse_tc_network_emulator_delay,            0,                             0
-TrafficControlQueueingDiscipline.NetworkEmulatorLossRate,            config_parse_tc_network_emulator_rate,             0,                             0
-TrafficControlQueueingDiscipline.NetworkEmulatorDuplicateRate,       config_parse_tc_network_emulator_rate,             0,                             0
-TrafficControlQueueingDiscipline.NetworkEmulatorPacketLimit,         config_parse_tc_network_emulator_packet_limit,     0,                             0
+TrafficControlQueueingDiscipline.Parent,                                     config_parse_tc_qdiscs_parent,                               0, 0
+TrafficControlQueueingDiscipline.NetworkEmulatorDelaySec,                    config_parse_tc_network_emulator_delay,                      0, 0
+TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec,              config_parse_tc_network_emulator_delay,                      0, 0
+TrafficControlQueueingDiscipline.NetworkEmulatorLossRate,                    config_parse_tc_network_emulator_rate,                       0, 0
+TrafficControlQueueingDiscipline.NetworkEmulatorDuplicateRate,               config_parse_tc_network_emulator_rate,                       0, 0
+TrafficControlQueueingDiscipline.NetworkEmulatorPacketLimit,                 config_parse_tc_network_emulator_packet_limit,               0, 0
+TrafficControlQueueingDiscipline.TokenBufferFilterRate,                      config_parse_tc_token_buffer_filter_size,                    0, 0
+TrafficControlQueueingDiscipline.TokenBufferFilterBurst,                     config_parse_tc_token_buffer_filter_size,                    0, 0
+TrafficControlQueueingDiscipline.TokenBufferFilterLatencySec,                config_parse_tc_token_buffer_filter_latency,                 0, 0
+TrafficControlQueueingDiscipline.StochasticFairnessQueueingPerturbPeriodSec, config_parse_tc_stochastic_fairness_queueing_perturb_period, 0, 0
 /* backwards compatibility: do not add new entries to this section */
 Network.IPv4LL,                         config_parse_ipv4ll,                             0,                             offsetof(Network, link_local)
 DHCP.ClientIdentifier,                  config_parse_dhcp_client_identifier,             0,                             offsetof(Network, dhcp_client_identifier)
index 6e443975f1716da7a59402d1a0e64669fb92d83d..181afbb4cd15c694f897ce4d5bb7440ded7374cd 100644 (file)
@@ -154,6 +154,8 @@ int network_verify(Network *network) {
         Prefix *prefix, *prefix_next;
         Route *route, *route_next;
         FdbEntry *fdb, *fdb_next;
+        QDisc *qdisc;
+        Iterator i;
 
         assert(network);
         assert(network->filename);
@@ -313,6 +315,11 @@ int network_verify(Network *network) {
                 if (routing_policy_rule_section_verify(rule) < 0)
                         routing_policy_rule_free(rule);
 
+        bool has_root = false, has_clsact = false;
+        ORDERED_HASHMAP_FOREACH(qdisc, network->qdiscs_by_section, i)
+                if (qdisc_section_verify(qdisc, &has_root, &has_clsact) < 0)
+                        qdisc_free(qdisc);
+
         return 0;
 }
 
index 053af3e7dbbd016dacde9885b7bd968b58fa0ae1..25a53150b07afddf66d741a2554b56919211329f 100644 (file)
@@ -2,12 +2,9 @@
  * Copyright Â© 2019 VMware, Inc. */
 
 #include <linux/pkt_sched.h>
-#include <math.h>
 
 #include "alloc-util.h"
 #include "conf-parser.h"
-#include "hashmap.h"
-#include "in-addr-util.h"
 #include "netem.h"
 #include "netlink-util.h"
 #include "networkd-manager.h"
@@ -15,7 +12,6 @@
 #include "qdisc.h"
 #include "string-util.h"
 #include "tc-util.h"
-#include "util.h"
 
 int network_emulator_new(NetworkEmulator **ret) {
         NetworkEmulator *ne = NULL;
@@ -34,33 +30,33 @@ int network_emulator_new(NetworkEmulator **ret) {
         return 0;
 }
 
-int network_emulator_fill_message(Link *link, QDiscs *qdisc, sd_netlink_message *req) {
+int network_emulator_fill_message(Link *link, const NetworkEmulator *ne, sd_netlink_message *req) {
         struct tc_netem_qopt opt = {
                .limit = 1000,
         };
         int r;
 
         assert(link);
-        assert(qdisc);
+        assert(ne);
         assert(req);
 
-        if (qdisc->ne.limit > 0)
-                opt.limit = qdisc->ne.limit;
+        if (ne->limit > 0)
+                opt.limit = ne->limit;
 
-        if (qdisc->ne.loss > 0)
-                opt.loss = qdisc->ne.loss;
+        if (ne->loss > 0)
+                opt.loss = ne->loss;
 
-        if (qdisc->ne.duplicate > 0)
-                opt.duplicate = qdisc->ne.duplicate;
+        if (ne->duplicate > 0)
+                opt.duplicate = ne->duplicate;
 
-        if (qdisc->ne.delay != USEC_INFINITY) {
-                r = tc_time_to_tick(qdisc->ne.delay, &opt.latency);
+        if (ne->delay != USEC_INFINITY) {
+                r = tc_time_to_tick(ne->delay, &opt.latency);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Failed to calculate latency in TCA_OPTION: %m");
         }
 
-        if (qdisc->ne.jitter != USEC_INFINITY) {
-                r = tc_time_to_tick(qdisc->ne.jitter, &opt.jitter);
+        if (ne->jitter != USEC_INFINITY) {
+                r = tc_time_to_tick(ne->jitter, &opt.jitter);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Failed to calculate jitter in TCA_OPTION: %m");
         }
@@ -84,7 +80,7 @@ int config_parse_tc_network_emulator_delay(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(qdisc_free_or_set_invalidp) QDiscs *qdisc = NULL;
+        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
         Network *network = data;
         usec_t u;
         int r;
@@ -139,7 +135,7 @@ int config_parse_tc_network_emulator_rate(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(qdisc_free_or_set_invalidp) QDiscs *qdisc = NULL;
+        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
         Network *network = data;
         uint32_t rate;
         int r;
@@ -189,7 +185,7 @@ int config_parse_tc_network_emulator_packet_limit(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(qdisc_free_or_set_invalidp) QDiscs *qdisc = NULL;
+        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
         Network *network = data;
         int r;
 
index 43abf20af87cda4dc1ef86075979ea2405d925af..66c3476a6beb70fa8459bb2901b6cfad2c87688e 100644 (file)
@@ -8,8 +8,6 @@
 #include "networkd-link.h"
 #include "time-util.h"
 
-typedef struct QDiscs QDiscs;
-
 typedef struct NetworkEmulator {
         usec_t delay;
         usec_t jitter;
@@ -20,7 +18,7 @@ typedef struct NetworkEmulator {
 } NetworkEmulator;
 
 int network_emulator_new(NetworkEmulator **ret);
-int network_emulator_fill_message(Link *link, QDiscs *qdisc, sd_netlink_message *req);
+int network_emulator_fill_message(Link *link, const NetworkEmulator *ne, sd_netlink_message *req);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_delay);
 CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_rate);
index ed9bd9167ae614efab098e49f41b37d234898b68..05c0ebbc1869d4743b29f40eb07d5fd3ad19ffd7 100644 (file)
 #include "qdisc.h"
 #include "set.h"
 #include "string-util.h"
-#include "util.h"
 
-static int qdisc_new(QDiscs **ret) {
-        QDiscs *qdisc;
+static int qdisc_new(QDisc **ret) {
+        QDisc *qdisc;
 
-        qdisc = new(QDiscs, 1);
+        qdisc = new(QDisc, 1);
         if (!qdisc)
                 return -ENOMEM;
 
-        *qdisc = (QDiscs) {
+        *qdisc = (QDisc) {
                 .family = AF_UNSPEC,
                 .parent = TC_H_ROOT,
         };
@@ -31,9 +30,9 @@ static int qdisc_new(QDiscs **ret) {
         return 0;
 }
 
-int qdisc_new_static(Network *network, const char *filename, unsigned section_line, QDiscs **ret) {
+int qdisc_new_static(Network *network, const char *filename, unsigned section_line, QDisc **ret) {
         _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
-        _cleanup_(qdisc_freep) QDiscs *qdisc = NULL;
+        _cleanup_(qdisc_freep) QDisc *qdisc = NULL;
         int r;
 
         assert(network);
@@ -76,7 +75,7 @@ int qdisc_new_static(Network *network, const char *filename, unsigned section_li
         return 0;
 }
 
-void qdisc_free(QDiscs *qdisc) {
+void qdisc_free(QDisc *qdisc) {
         if (!qdisc)
                 return;
 
@@ -106,7 +105,7 @@ static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         }
 
         if (link->route_messages == 0) {
-                log_link_debug(link, "QDiscs configured");
+                log_link_debug(link, "QDisc configured");
                 link->qdiscs_configured = true;
                 link_check_ready(link);
         }
@@ -114,7 +113,7 @@ static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         return 1;
 }
 
-int qdisc_configure(Link *link, QDiscs *qdisc) {
+int qdisc_configure(Link *link, QDisc *qdisc) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         _cleanup_free_ char *tca_kind = NULL;
         int r;
@@ -147,7 +146,27 @@ int qdisc_configure(Link *link, QDiscs *qdisc) {
                 if (r < 0)
                         return log_oom();
 
-                r = network_emulator_fill_message(link, qdisc, req);
+                r = network_emulator_fill_message(link, &qdisc->ne, req);
+                if (r < 0)
+                        return r;
+        }
+
+        if (qdisc->has_token_buffer_filter) {
+                r = free_and_strdup(&tca_kind, "tbf");
+                if (r < 0)
+                        return log_oom();
+
+                r = token_buffer_filter_fill_message(link, &qdisc->tbf, req);
+                if (r < 0)
+                        return r;
+        }
+
+        if (qdisc->has_stochastic_fairness_queueing) {
+                r = free_and_strdup(&tca_kind, "sfq");
+                if (r < 0)
+                        return log_oom();
+
+                r = stochastic_fairness_queueing_fill_message(link, &qdisc->sfq, req);
                 if (r < 0)
                         return r;
         }
@@ -168,6 +187,42 @@ int qdisc_configure(Link *link, QDiscs *qdisc) {
         return 0;
 }
 
+int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) {
+        unsigned i;
+
+        assert(qdisc);
+        assert(has_root);
+        assert(has_clsact);
+
+        if (section_is_invalid(qdisc->section))
+                return -EINVAL;
+
+        i = qdisc->has_network_emulator + qdisc->has_token_buffer_filter + qdisc->has_stochastic_fairness_queueing;
+        if (i > 1)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: TrafficControlQueueingDiscipline section has more than one type of discipline. "
+                                         "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+                                         qdisc->section->filename, qdisc->section->line);
+
+        if (qdisc->parent == TC_H_ROOT) {
+                if (*has_root)
+                        return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                 "%s: More than one root TrafficControlQueueingDiscipline sections are defined. "
+                                                 "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+                                                 qdisc->section->filename, qdisc->section->line);
+                *has_root = true;
+        } else if (qdisc->parent == TC_H_CLSACT) {
+                if (*has_clsact)
+                        return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                 "%s: More than one clsact TrafficControlQueueingDiscipline sections are defined. "
+                                                 "Ignoring [TrafficControlQueueingDiscipline] section from line %u.",
+                                                 qdisc->section->filename, qdisc->section->line);
+                *has_clsact = true;
+        }
+
+        return 0;
+}
+
 int config_parse_tc_qdiscs_parent(
                 const char *unit,
                 const char *filename,
@@ -180,7 +235,7 @@ int config_parse_tc_qdiscs_parent(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(qdisc_free_or_set_invalidp) QDiscs *qdisc = NULL;
+        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
         Network *network = data;
         int r;
 
index 95ff829b9e34045676a76a18d7da242cdd585fee..1d06dc53f443ff210976b90989304ddc4701b6c0 100644 (file)
@@ -7,8 +7,10 @@
 #include "networkd-link.h"
 #include "networkd-network.h"
 #include "networkd-util.h"
+#include "sfq.h"
+#include "tbf.h"
 
-typedef struct QDiscs {
+typedef struct QDisc {
         NetworkConfigSection *section;
         Network *network;
 
@@ -20,15 +22,21 @@ typedef struct QDiscs {
         uint32_t parent;
 
         bool has_network_emulator:1;
+        bool has_token_buffer_filter:1;
+        bool has_stochastic_fairness_queueing:1;
 
         NetworkEmulator ne;
-} QDiscs;
+        TokenBufferFilter tbf;
+        StochasticFairnessQueueing sfq;
+} QDisc;
 
-void qdisc_free(QDiscs *qdisc);
-int qdisc_new_static(Network *network, const char *filename, unsigned section_line, QDiscs **ret);
+void qdisc_free(QDisc *qdisc);
+int qdisc_new_static(Network *network, const char *filename, unsigned section_line, QDisc **ret);
 
-int qdisc_configure(Link *link, QDiscs *qdisc);
+int qdisc_configure(Link *link, QDisc *qdisc);
 
-DEFINE_NETWORK_SECTION_FUNCTIONS(QDiscs, qdisc_free);
+int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact);
+
+DEFINE_NETWORK_SECTION_FUNCTIONS(QDisc, qdisc_free);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_tc_qdiscs_parent);
diff --git a/src/network/tc/sfq.c b/src/network/tc/sfq.c
new file mode 100644 (file)
index 0000000..393b0e1
--- /dev/null
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright Â© 2019 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "qdisc.h"
+#include "sfq.h"
+#include "string-util.h"
+
+int stochastic_fairness_queueing_new(StochasticFairnessQueueing **ret) {
+        StochasticFairnessQueueing *sfq = NULL;
+
+        sfq = new0(StochasticFairnessQueueing, 1);
+        if (!sfq)
+                return -ENOMEM;
+
+        *ret = TAKE_PTR(sfq);
+
+        return 0;
+}
+
+int stochastic_fairness_queueing_fill_message(Link *link, const StochasticFairnessQueueing *sfq, sd_netlink_message *req) {
+        struct tc_sfq_qopt_v1 opt = {};
+        int r;
+
+        assert(link);
+        assert(sfq);
+        assert(req);
+
+        opt.v0.perturb_period = sfq->perturb_period / USEC_PER_SEC;
+
+        r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(struct tc_sfq_qopt_v1));
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_OPTIONS attribute: %m");
+
+        return 0;
+}
+
+int config_parse_tc_stochastic_fairness_queueing_perturb_period(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(network, filename, section_line, &qdisc);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                qdisc->sfq.perturb_period = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = parse_sec(rvalue, &qdisc->sfq.perturb_period);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        qdisc->has_stochastic_fairness_queueing = true;
+        qdisc = NULL;
+
+        return 0;
+}
diff --git a/src/network/tc/sfq.h b/src/network/tc/sfq.h
new file mode 100644 (file)
index 0000000..8c00e0e
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright Â© 2019 VMware, Inc. */
+#pragma once
+
+#include "sd-netlink.h"
+
+#include "conf-parser.h"
+#include "networkd-link.h"
+
+typedef struct StochasticFairnessQueueing {
+        usec_t perturb_period;
+} StochasticFairnessQueueing;
+
+int stochastic_fairness_queueing_new(StochasticFairnessQueueing **ret);
+int stochastic_fairness_queueing_fill_message(Link *link, const StochasticFairnessQueueing *sfq, sd_netlink_message *req);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_tc_stochastic_fairness_queueing_perturb_period);
diff --git a/src/network/tc/tbf.c b/src/network/tc/tbf.c
new file mode 100644 (file)
index 0000000..a4ef9ab
--- /dev/null
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright Â© 2019 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+#include <math.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "netem.h"
+#include "netlink-util.h"
+#include "networkd-manager.h"
+#include "parse-util.h"
+#include "qdisc.h"
+#include "string-util.h"
+#include "util.h"
+
+int token_buffer_filter_new(TokenBufferFilter **ret) {
+        TokenBufferFilter *ne = NULL;
+
+        ne = new0(TokenBufferFilter, 1);
+        if (!ne)
+                return -ENOMEM;
+
+        *ret = TAKE_PTR(ne);
+
+        return 0;
+}
+
+int token_buffer_filter_fill_message(Link *link, const TokenBufferFilter *tbf, sd_netlink_message *req) {
+        struct tc_tbf_qopt opt = {};
+        int r;
+
+        assert(link);
+        assert(tbf);
+        assert(req);
+
+        opt.rate.rate = tbf->rate >= (1ULL << 32) ? ~0U : tbf->rate;
+        opt.limit = tbf->rate * (double) tbf->latency / USEC_PER_SEC + tbf->burst;
+
+        r = sd_netlink_message_open_array(req, TCA_OPTIONS);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+        r = sd_netlink_message_append_data(req, TCA_TBF_PARMS, &opt, sizeof(struct tc_tbf_qopt));
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_TBF_PARMS attribute: %m");
+
+        r = sd_netlink_message_append_data(req, TCA_TBF_BURST, &tbf->burst, sizeof(tbf->burst));
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_TBF_BURST attribute: %m");
+
+        if (tbf->rate >= (1ULL << 32)) {
+                r = sd_netlink_message_append_data(req, TCA_TBF_RATE64, &tbf->rate, sizeof(tbf->rate));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_TBF_RATE64 attribute: %m");
+        }
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+
+        return 0;
+}
+
+int config_parse_tc_token_buffer_filter_size(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        Network *network = data;
+        uint64_t k;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(network, filename, section_line, &qdisc);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                if (streq(lvalue, "TokenBufferFilterRate"))
+                        qdisc->tbf.rate = 0;
+                else if (streq(lvalue, "TokenBufferFilterBurst"))
+                        qdisc->tbf.burst = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = parse_size(rvalue, 1000, &k);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        if (streq(lvalue, "TokenBufferFilterRate"))
+                qdisc->tbf.rate = k / 8;
+        else if (streq(lvalue, "TokenBufferFilterBurst"))
+                qdisc->tbf.burst = k;
+
+        qdisc->has_token_buffer_filter = true;
+        qdisc = NULL;
+
+        return 0;
+}
+
+int config_parse_tc_token_buffer_filter_latency(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        Network *network = data;
+        usec_t u;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(network, filename, section_line, &qdisc);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                qdisc->tbf.latency = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = parse_sec(rvalue, &u);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        qdisc->tbf.latency = u;
+
+        qdisc->has_token_buffer_filter = true;
+        qdisc = NULL;
+
+        return 0;
+}
diff --git a/src/network/tc/tbf.h b/src/network/tc/tbf.h
new file mode 100644 (file)
index 0000000..c8ae6d0
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright Â© 2019 VMware, Inc. */
+#pragma once
+
+#include "sd-netlink.h"
+
+#include "conf-parser.h"
+#include "networkd-link.h"
+
+typedef struct TokenBufferFilter {
+        uint64_t rate;
+
+        uint32_t burst;
+        uint32_t latency;
+} TokenBufferFilter;
+
+int token_buffer_filter_new(TokenBufferFilter **ret);
+int token_buffer_filter_fill_message(Link *link, const TokenBufferFilter *tbf, sd_netlink_message *req);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_latency);
+CONFIG_PARSER_PROTOTYPE(config_parse_tc_token_buffer_filter_size);
index 873a76596f0b485906b51e31372cdddf18cafbcb..9fac32621964d3a9e42edcc0d8fa926d5cd19e55 100644 (file)
@@ -5036,7 +5036,7 @@ static int run(int argc, char *argv[]) {
                         goto finish;
                 }
 
-                r = loop_device_make_by_path(arg_image, arg_read_only ? O_RDONLY : O_RDWR, &loop);
+                r = loop_device_make_by_path(arg_image, arg_read_only ? O_RDONLY : O_RDWR, LO_FLAGS_PARTSCAN, &loop);
                 if (r < 0) {
                         log_error_errno(r, "Failed to set up loopback block device: %m");
                         goto finish;
index 34b123e846925b4a5d62ccc83a22d9712f8fb154..7a86398a4b5d8fe6c1edc12dd9a1544729e881f7 100644 (file)
@@ -1,5 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
+#include <linux/loop.h>
+
 #include "bus-common-errors.h"
 #include "bus-error.h"
 #include "conf-files.h"
@@ -359,7 +361,7 @@ static int portable_extract_by_path(
 
         assert(path);
 
-        r = loop_device_make_by_path(path, O_RDONLY, &d);
+        r = loop_device_make_by_path(path, O_RDONLY, LO_FLAGS_PARTSCAN, &d);
         if (r == -EISDIR) {
                 /* We can't turn this into a loop-back block device, and this returns EISDIR? Then this is a directory
                  * tree and not a raw device. It's easy then. */
index 92b67b6333d764ce474aeb05a7085fcfebd178ae..c4d8d4e5d9a3bb309017553f7b6c813b22b6bfc8 100644 (file)
@@ -64,6 +64,8 @@ systemd_resolved_sources = files('''
         resolved-etc-hosts.h
         resolved-etc-hosts.c
         resolved-dnstls.h
+        resolved-util.c
+        resolved-util.h
 '''.split())
 
 resolvectl_sources = files('''
@@ -228,4 +230,10 @@ tests += [
          [],
          [],
          'ENABLE_RESOLVE', 'manual'],
+
+        [['src/resolve/test-resolved-util.c',
+          'src/resolve/resolved-util.c',
+          'src/resolve/resolved-util.h'],
+         [],
+         []],
 ]
index a46c45385b5a1f84227a169b11ae781091eaeeac..ca5b8e7918391c782eb2a3fd31c675f9943b3910 100644 (file)
@@ -8,6 +8,7 @@
 #include "parse-util.h"
 #include "resolved-conf.h"
 #include "resolved-dnssd.h"
+#include "resolved-util.h"
 #include "specifier.h"
 #include "string-table.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;
         int family, r, ifindex = 0;
         DnsServer *s;
+        _cleanup_free_ char *server_name = NULL;
 
         assert(m);
         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)
                 return r;
 
@@ -52,7 +54,7 @@ static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, cons
                 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) {
index 9f2c97314fb475f8e6a40da572c2d7638b369feb..4b0599ab9cf3df9e1f3bb839b0e304df44b0397c 100644 (file)
@@ -25,8 +25,10 @@ int dns_server_new(
                 Link *l,
                 int family,
                 const union in_addr_union *in_addr,
-                int ifindex) {
+                int ifindex,
+                const char *server_name) {
 
+        _cleanup_free_ char *name = NULL;
         DnsServer *s;
 
         assert(m);
@@ -44,6 +46,12 @@ int dns_server_new(
                         return -E2BIG;
         }
 
+        if (server_name) {
+                name = strdup(server_name);
+                if (!name)
+                        return -ENOMEM;
+        }
+
         s = new(DnsServer, 1);
         if (!s)
                 return -ENOMEM;
@@ -55,6 +63,7 @@ int dns_server_new(
                 .family = family,
                 .address = *in_addr,
                 .ifindex = ifindex,
+                .server_name = TAKE_PTR(name),
         };
 
         dns_server_reset_features(s);
@@ -107,6 +116,7 @@ static DnsServer* dns_server_free(DnsServer *s)  {
 #endif
 
         free(s->server_string);
+        free(s->server_name);
         return mfree(s);
 }
 
index 54339355aa85367d73ae5c7ef2b14c97dd9c4354..889c80a2054bdaf488eb0632c8af0f6014796e46 100644 (file)
@@ -53,6 +53,8 @@ struct DnsServer {
 
         char *server_string;
 
+        char *server_name;
+
         /* The long-lived stream towards this server. */
         DnsStream *stream;
 
@@ -94,7 +96,8 @@ int dns_server_new(
                 Link *link,
                 int family,
                 const union in_addr_union *address,
-                int ifindex);
+                int ifindex,
+                const char *server_string);
 
 DnsServer* dns_server_ref(DnsServer *s);
 DnsServer* dns_server_unref(DnsServer *s);
index ed0a31e8bf39f51efb86a9ebffe2cdd57d223e64..aad3bb4481f5e2bf1b03416892b9db23db0843ef 100644 (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);
         }
 
+        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_transport_set_ptr2(gs, (gnutls_transport_ptr_t) (long) stream->fd, stream);
index 85e202ff741d46a09911c1247577ef980f6a2952..ce0a4373715582e30e650450707ec4e48eb35a43 100644 (file)
@@ -87,6 +87,17 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
                         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();
         stream->dnstls_data.handshake = SSL_do_handshake(s);
         if (stream->dnstls_data.handshake <= 0) {
index 8a2768b1e2c8e60bf6e92a6d24cab0152ceffddc..dae8435b45bec93d5e784ba7142e06c51204cfc8 100644 (file)
@@ -284,7 +284,7 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
                 if (s)
                         dns_server_move_back_and_unmark(s);
                 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)
                                 goto clear;
                 }
index 96ebb4d23d360020a39eeb98d6f2a7a619d4c1a6..f19fc2f3aa12d480188faedea3048c5ff8b7b2b0 100644 (file)
@@ -269,7 +269,7 @@ static int link_update_dns_server_one(Link *l, const char *name) {
                 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) {
diff --git a/src/resolve/resolved-util.c b/src/resolve/resolved-util.c
new file mode 100644 (file)
index 0000000..2f18f8c
--- /dev/null
@@ -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;
+}
diff --git a/src/resolve/resolved-util.h b/src/resolve/resolved-util.h
new file mode 100644 (file)
index 0000000..10ebbc0
--- /dev/null
@@ -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);
diff --git a/src/resolve/test-resolved-util.c b/src/resolve/test-resolved-util.c
new file mode 100644 (file)
index 0000000..35bd73c
--- /dev/null
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "log.h"
+#include "resolved-util.h"
+#include "string-util.h"
+#include "tests.h"
+
+
+static void test_in_addr_ifindex_name_from_string_auto_one(const char *a, const char *expected) {
+        int family, ifindex;
+        union in_addr_union ua;
+        _cleanup_free_ char *server_name = NULL;
+
+        assert_se(in_addr_ifindex_name_from_string_auto(a, &family, &ua, &ifindex, &server_name) >= 0);
+        assert_se(streq_ptr(server_name, expected));
+}
+
+static void test_in_addr_ifindex_name_from_string_auto(void) {
+        log_info("/* %s */", __func__);
+
+        test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1", NULL);
+        test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1#test.com", "test.com");
+        test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19", NULL);
+        test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19#another.test.com", "another.test.com");
+}
+
+int main(int argc, char **argv) {
+        test_setup_logging(LOG_DEBUG);
+
+        test_in_addr_ifindex_name_from_string_auto();
+        return 0;
+}
index 4f750dc1da1b50c282576429529bd0024a3b2b63..699b101b3901688f05311decd72c2a7bf2edc6be 100644 (file)
@@ -36,6 +36,7 @@ static void boot_entry_free(BootEntry *entry) {
         assert(entry);
 
         free(entry->id);
+        free(entry->id_old);
         free(entry->path);
         free(entry->root);
         free(entry->title);
@@ -73,12 +74,13 @@ static int boot_entry_load(
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry file suffix: %s", path);
 
         b = basename(path);
-        tmp.id = strndup(b, c - b);
-        if (!tmp.id)
+        tmp.id = strdup(b);
+        tmp.id_old = strndup(b, c - b);
+        if (!tmp.id || !tmp.id_old)
                 return log_oom();
 
         if (!efi_loader_entry_name_valid(tmp.id))
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry filename: %s", path);
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry: %s", tmp.id);
 
         tmp.path = strdup(path);
         if (!tmp.path)
@@ -289,6 +291,7 @@ static int boot_entry_load_unified(
         };
         _cleanup_fclose_ FILE *f = NULL;
         const char *k;
+        char *b;
         int r;
 
         assert(root);
@@ -314,8 +317,10 @@ static int boot_entry_load_unified(
         if (!os_pretty_name || !os_id || !(version_id || build_id))
                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path);
 
-        tmp.id = strjoin(os_id, "-", version_id ?: build_id);
-        if (!tmp.id)
+        b = basename(path);
+        tmp.id = strdup(b);
+        tmp.id_old = strjoin(os_id, "-", version_id ?: build_id);
+        if (!tmp.id || !tmp.id_old)
                 return log_oom();
 
         if (!efi_loader_entry_name_valid(tmp.id))
index c18d89494a512132be7adde966bf200ef2e3a062..a825b35bc58c829caabb0036e5ea44e4b98c1a89 100644 (file)
@@ -21,6 +21,7 @@ typedef enum BootEntryType {
 typedef struct BootEntry {
         BootEntryType type;
         char *id;       /* This is the file basename without extension */
+        char *id_old;   /* Old-style ID, for deduplication purposes. */
         char *path;     /* This is the full path to the drop-in file */
         char *root;     /* The root path in which the drop-in was found, i.e. to which 'kernel', 'efi' and 'initrd' are relative */
         char *title;
@@ -54,9 +55,12 @@ typedef struct BootConfig {
 static inline bool boot_config_has_entry(BootConfig *config, const char *id) {
         size_t j;
 
-        for (j = 0; j < config->n_entries; j++)
-                if (streq(config->entries[j].id, id))
+        for (j = 0; j < config->n_entries; j++) {
+                const char* entry_id_old = config->entries[j].id_old;
+                if (streq(config->entries[j].id, id) ||
+                    (entry_id_old && streq(entry_id_old, id)))
                         return true;
+        }
 
         return false;
 }
index 3269d8303117c35de933e374c0f0fac8e2b9b27d..90b31148f3e66a9cc6ccbf27ed089599c53859dc 100644 (file)
@@ -221,8 +221,19 @@ static int parse_line(
                         return -ENOMEM;
 
                 if (sections && !nulstr_contains(sections, n)) {
+                        bool ignore = flags & CONFIG_PARSE_RELAXED;
+                        const char *t;
 
-                        if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(n, "X-"))
+                        ignore = ignore || startswith(n, "X-");
+
+                        if (!ignore)
+                                NULSTR_FOREACH(t, sections)
+                                        if (streq_ptr(n, startswith(t, "-"))) {
+                                                ignore = true;
+                                                break;
+                                        }
+
+                        if (!ignore)
                                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
 
                         free(n);
@@ -282,7 +293,7 @@ int config_parse(const char *unit,
         _cleanup_free_ char *section = NULL, *continuation = NULL;
         _cleanup_fclose_ FILE *ours = NULL;
         unsigned line = 0, section_line = 0;
-        bool section_ignored = false;
+        bool section_ignored = false, bom_seen = false;
         int r;
 
         assert(filename);
@@ -328,13 +339,13 @@ int config_parse(const char *unit,
                         continue;
 
                 l = buf;
-                if (!(flags & CONFIG_PARSE_REFUSE_BOM)) {
+                if (!bom_seen) {
                         char *q;
 
                         q = startswith(buf, UTF8_BYTE_ORDER_MARK);
                         if (q) {
                                 l = q;
-                                flags |= CONFIG_PARSE_REFUSE_BOM;
+                                bom_seen = true;
                         }
                 }
 
index 04c68b18d8a09ade3ae63b8af55492b851c9edea..9ec77778670d5f62383037db28de0aa2d9790660 100644 (file)
 /* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */
 
 typedef enum ConfigParseFlags {
-        CONFIG_PARSE_RELAXED       = 1 << 0,
-        CONFIG_PARSE_ALLOW_INCLUDE = 1 << 1,
-        CONFIG_PARSE_WARN          = 1 << 2,
-        CONFIG_PARSE_REFUSE_BOM    = 1 << 3,
+        CONFIG_PARSE_RELAXED       = 1 << 0, /* Do not warn about unknown non-extension fields */
+        CONFIG_PARSE_ALLOW_INCLUDE = 1 << 1, /* Allow the deprecated .include stanza */
+        CONFIG_PARSE_WARN          = 1 << 2, /* Emit non-debug messages */
 } ConfigParseFlags;
 
 /* Argument list for parsers of specific configuration settings. */
index 934e0fe830ffb899691051e4bec99c5c5df7a717..11d21c3a4d1b900af3883edf2e28034590a5521e 100644 (file)
@@ -1,5 +1,9 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
+#if HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
 #include <linux/dm-ioctl.h>
 #include <linux/loop.h>
 #include <sys/mount.h>
@@ -215,9 +219,15 @@ static int wait_for_partitions_to_appear(
                          * an explicit recognizable error about this, so that callers can generate a
                          * proper message explaining the situation. */
 
-                        if (ioctl(fd, LOOP_GET_STATUS64, &info) >= 0 && (info.lo_flags & LO_FLAGS_PARTSCAN) == 0) {
-                                log_debug("Device is a loop device and partition scanning is off!");
-                                return -EPROTONOSUPPORT;
+                        if (ioctl(fd, LOOP_GET_STATUS64, &info) >= 0) {
+#if HAVE_VALGRIND_MEMCHECK_H
+                                /* Valgrind currently doesn't know LOOP_GET_STATUS64. Remove this once it does */
+                                VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
+#endif
+
+                                if ((info.lo_flags & LO_FLAGS_PARTSCAN) == 0)
+                                        return log_debug_errno(EPROTONOSUPPORT,
+                                                               "Device is a loop device and partition scanning is off!");
                         }
                 }
                 if (r != -EBUSY)
index 14dfd331d584a0f5ded6c70ed4a8f50640124053..56806609280533821e82defcfabf2976a851f8dd 100644 (file)
@@ -1290,9 +1290,21 @@ static int unit_file_load(
         assert(c);
 
         r = config_parse(info->name, path, f,
-                         NULL,
+                         "Install\0"
+                         "-Unit\0"
+                         "-Automount\0"
+                         "-Device\0"
+                         "-Mount\0"
+                         "-Path\0"
+                         "-Scope\0"
+                         "-Service\0"
+                         "-Slice\0"
+                         "-Socket\0"
+                         "-Swap\0"
+                         "-Target\0"
+                         "-Timer\0",
                          config_item_table_lookup, items,
-                         CONFIG_PARSE_RELAXED|CONFIG_PARSE_ALLOW_INCLUDE, info);
+                         CONFIG_PARSE_ALLOW_INCLUDE, info);
         if (r < 0)
                 return log_debug_errno(r, "Failed to parse %s: %m", info->name);
 
index 559d7f8174eff6813b6594e1da87ea0ecdf8b691..acf3eae2d72cfbc40d7a3360b30ae00908234e5a 100644 (file)
@@ -1,26 +1,40 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
+#if HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/blkpg.h>
+#include <linux/fs.h>
 #include <linux/loop.h>
+#include <sys/file.h>
 #include <sys/ioctl.h>
 
 #include "alloc-util.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "loop-util.h"
+#include "parse-util.h"
 #include "stat-util.h"
+#include "stdio-util.h"
 
-int loop_device_make(int fd, int open_flags, LoopDevice **ret) {
-        const struct loop_info64 info = {
-                .lo_flags = LO_FLAGS_AUTOCLEAR|LO_FLAGS_PARTSCAN|(open_flags == O_RDONLY ? LO_FLAGS_READ_ONLY : 0),
-        };
+int loop_device_make_full(
+                int fd,
+                int open_flags,
+                uint64_t offset,
+                uint64_t size,
+                uint32_t loop_flags,
+                LoopDevice **ret) {
 
         _cleanup_close_ int control = -1, loop = -1;
         _cleanup_free_ char *loopdev = NULL;
         unsigned n_attempts = 0;
+        struct loop_info64 info;
+        LoopDevice *d = NULL;
         struct stat st;
-        LoopDevice *d;
-        int nr, r;
+        int nr = -1, r;
 
         assert(fd >= 0);
         assert(ret);
@@ -30,31 +44,47 @@ int loop_device_make(int fd, int open_flags, LoopDevice **ret) {
                 return -errno;
 
         if (S_ISBLK(st.st_mode)) {
-                int copy;
+                if (ioctl(loop, LOOP_GET_STATUS64, &info) >= 0) {
+                        /* Oh! This is a loopback device? That's interesting! */
 
-                /* If this is already a block device, store a copy of the fd as it is */
+#if HAVE_VALGRIND_MEMCHECK_H
+                        /* Valgrind currently doesn't know LOOP_GET_STATUS64. Remove this once it does */
+                        VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
+#endif
+                        nr = info.lo_number;
 
-                copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
-                if (copy < 0)
-                        return -errno;
+                        if (asprintf(&loopdev, "/dev/loop%i", nr) < 0)
+                                return -ENOMEM;
+                }
 
-                d = new0(LoopDevice, 1);
-                if (!d)
-                        return -ENOMEM;
+                if (offset == 0 && IN_SET(size, 0, UINT64_MAX)) {
+                        int copy;
 
-                *d = (LoopDevice) {
-                        .fd = copy,
-                        .nr = -1,
-                        .relinquished = true, /* It's not allocated by us, don't destroy it when this object is freed */
-                };
+                        /* If this is already a block device, store a copy of the fd as it is */
 
-                *ret = d;
-                return d->fd;
-        }
+                        copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+                        if (copy < 0)
+                                return -errno;
 
-        r = stat_verify_regular(&st);
-        if (r < 0)
-                return r;
+                        d = new(LoopDevice, 1);
+                        if (!d)
+                                return -ENOMEM;
+
+                        *d = (LoopDevice) {
+                                .fd = copy,
+                                .nr = nr,
+                                .node = TAKE_PTR(loopdev),
+                                .relinquished = true, /* It's not allocated by us, don't destroy it when this object is freed */
+                        };
+
+                        *ret = d;
+                        return d->fd;
+                }
+        } else {
+                r = stat_verify_regular(&st);
+                if (r < 0)
+                        return r;
+        }
 
         control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
         if (control < 0)
@@ -86,12 +116,23 @@ int loop_device_make(int fd, int open_flags, LoopDevice **ret) {
                 loop = safe_close(loop);
         }
 
-        if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0)
-                return -errno;
+        info = (struct loop_info64) {
+                /* Use the specified flags, but configure the read-only flag from the open flags, and force autoclear */
+                .lo_flags = (loop_flags & ~LO_FLAGS_READ_ONLY) | ((loop_flags & O_ACCMODE) == O_RDONLY ? LO_FLAGS_READ_ONLY : 0) | LO_FLAGS_AUTOCLEAR,
+                .lo_offset = offset,
+                .lo_sizelimit = size == UINT64_MAX ? 0 : size,
+        };
+
+        if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) {
+                r = -errno;
+                goto fail;
+        }
 
         d = new(LoopDevice, 1);
-        if (!d)
-                return -ENOMEM;
+        if (!d) {
+                r = -ENOMEM;
+                goto fail;
+        }
 
         *d = (LoopDevice) {
                 .fd = TAKE_FD(loop),
@@ -101,9 +142,17 @@ int loop_device_make(int fd, int open_flags, LoopDevice **ret) {
 
         *ret = d;
         return d->fd;
+
+fail:
+        if (fd >= 0)
+                (void) ioctl(fd, LOOP_CLR_FD);
+        if (d && d->fd >= 0)
+                (void) ioctl(d->fd, LOOP_CLR_FD);
+
+        return r;
 }
 
-int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret) {
+int loop_device_make_by_path(const char *path, int open_flags, uint32_t loop_flags, LoopDevice **ret) {
         _cleanup_close_ int fd = -1;
 
         assert(path);
@@ -114,7 +163,7 @@ int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret)
         if (fd < 0)
                 return -errno;
 
-        return loop_device_make(fd, open_flags, ret);
+        return loop_device_make(fd, open_flags, loop_flags, ret);
 }
 
 LoopDevice* loop_device_unref(LoopDevice *d) {
@@ -156,3 +205,190 @@ void loop_device_relinquish(LoopDevice *d) {
 
         d->relinquished = true;
 }
+
+int loop_device_open(const char *loop_path, int open_flags, LoopDevice **ret) {
+        _cleanup_close_ int loop_fd = -1;
+        _cleanup_free_ char *p = NULL;
+        struct loop_info64 info;
+        struct stat st;
+        LoopDevice *d;
+        int nr;
+
+        assert(loop_path);
+        assert(ret);
+
+        loop_fd = open(loop_path, O_CLOEXEC|O_NONBLOCK|O_NOCTTY|open_flags);
+        if (loop_fd < 0)
+                return -errno;
+
+        if (fstat(loop_fd, &st) < 0)
+                return -errno;
+        if (!S_ISBLK(st.st_mode))
+                return -ENOTBLK;
+
+        if (ioctl(loop_fd, LOOP_GET_STATUS64, &info) >= 0) {
+#if HAVE_VALGRIND_MEMCHECK_H
+                /* Valgrind currently doesn't know LOOP_GET_STATUS64. Remove this once it does */
+                VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
+#endif
+                nr = info.lo_number;
+        } else
+                nr = -1;
+
+        p = strdup(loop_path);
+        if (!p)
+                return -ENOMEM;
+
+        d = new(LoopDevice, 1);
+        if (!d)
+                return -ENOMEM;
+
+        *d = (LoopDevice) {
+                .fd = TAKE_FD(loop_fd),
+                .nr = nr,
+                .node = TAKE_PTR(p),
+                .relinquished = true, /* It's not ours, don't try to destroy it when this object is freed */
+        };
+
+        *ret = d;
+        return d->fd;
+}
+
+static int resize_partition(int partition_fd, uint64_t offset, uint64_t size) {
+        char sysfs[STRLEN("/sys/dev/block/:/partition") + 2*DECIMAL_STR_MAX(dev_t) + 1];
+        _cleanup_free_ char *whole = NULL, *buffer = NULL;
+        uint64_t current_offset, current_size, partno;
+        _cleanup_close_ int whole_fd = -1;
+        struct stat st;
+        dev_t devno;
+        int r;
+
+        assert(partition_fd >= 0);
+
+        /* Resizes the partition the loopback device refer to (assuming it refers to one instead of an actual
+         * loopback device), and changes the offset, if needed. This is a fancy wrapper around
+         * BLKPG_RESIZE_PARTITION. */
+
+        if (fstat(partition_fd, &st) < 0)
+                return -errno;
+
+        assert(S_ISBLK(st.st_mode));
+
+        xsprintf(sysfs, "/sys/dev/block/%u:%u/partition", major(st.st_rdev), minor(st.st_rdev));
+        r = read_one_line_file(sysfs, &buffer);
+        if (r == -ENOENT) /* not a partition, cannot resize */
+                return -ENOTTY;
+        if (r < 0)
+                return r;
+        r = safe_atou64(buffer, &partno);
+        if (r < 0)
+                return r;
+
+        xsprintf(sysfs, "/sys/dev/block/%u:%u/start", major(st.st_rdev), minor(st.st_rdev));
+
+        buffer = mfree(buffer);
+        r = read_one_line_file(sysfs, &buffer);
+        if (r < 0)
+                return r;
+        r = safe_atou64(buffer, &current_offset);
+        if (r < 0)
+                return r;
+        if (current_offset > UINT64_MAX/512U)
+                return -EINVAL;
+        current_offset *= 512U;
+
+        if (ioctl(partition_fd, BLKGETSIZE64, &current_size) < 0)
+                return -EINVAL;
+
+        if (size == UINT64_MAX && offset == UINT64_MAX)
+                return 0;
+        if (current_size == size && current_offset == offset)
+                return 0;
+
+        xsprintf(sysfs, "/sys/dev/block/%u:%u/../dev", major(st.st_rdev), minor(st.st_rdev));
+
+        buffer = mfree(buffer);
+        r = read_one_line_file(sysfs, &buffer);
+        if (r < 0)
+                return r;
+        r = parse_dev(buffer, &devno);
+        if (r < 0)
+                return r;
+
+        r = device_path_make_major_minor(S_IFBLK, devno, &whole);
+        if (r < 0)
+                return r;
+
+        whole_fd = open(whole, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+        if (whole_fd < 0)
+                return -errno;
+
+        struct blkpg_partition bp = {
+                .pno = partno,
+                .start = offset == UINT64_MAX ? current_offset : offset,
+                .length = size == UINT64_MAX ? current_size : size,
+        };
+
+        struct blkpg_ioctl_arg ba = {
+                .op = BLKPG_RESIZE_PARTITION,
+                .data = &bp,
+                .datalen = sizeof(bp),
+        };
+
+        if (ioctl(whole_fd, BLKPG, &ba) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int loop_device_refresh_size(LoopDevice *d, uint64_t offset, uint64_t size) {
+        struct loop_info64 info;
+        assert(d);
+
+        /* Changes the offset/start of the loop device relative to the beginning of the underlying file or
+         * block device. If this loop device actually refers to a partition and not a loopback device, we'll
+         * try to adjust the partition offsets instead.
+         *
+         * If either offset or size is UINT64_MAX we won't change that parameter. */
+
+        if (d->fd < 0)
+                return -EBADF;
+
+        if (d->nr < 0) /* not a loopback device */
+                return resize_partition(d->fd, offset, size);
+
+        if (ioctl(d->fd, LOOP_GET_STATUS64, &info) < 0)
+                return -errno;
+
+#if HAVE_VALGRIND_MEMCHECK_H
+        /* Valgrind currently doesn't know LOOP_GET_STATUS64. Remove this once it does */
+        VALGRIND_MAKE_MEM_DEFINED(&info, sizeof(info));
+#endif
+
+        if (size == UINT64_MAX && offset == UINT64_MAX)
+                return 0;
+        if (info.lo_sizelimit == size && info.lo_offset == offset)
+                return 0;
+
+        if (size != UINT64_MAX)
+                info.lo_sizelimit = size;
+        if (offset != UINT64_MAX)
+                info.lo_offset = offset;
+
+        if (ioctl(d->fd, LOOP_SET_STATUS64, &info) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int loop_device_flock(LoopDevice *d, int operation) {
+        assert(d);
+
+        if (d->fd < 0)
+                return -EBADF;
+
+        if (flock(d->fd, operation) < 0)
+                return -errno;
+
+        return 0;
+}
index d78466c5ee68369620f7e0383cd0aed1330ce7da..5156b46ad611a37d856af0151503ad75be301219 100644 (file)
@@ -14,10 +14,19 @@ struct LoopDevice {
         bool relinquished;
 };
 
-int loop_device_make(int fd, int open_flags, LoopDevice **ret);
-int loop_device_make_by_path(const char *path, int open_flags, LoopDevice **ret);
+int loop_device_make_full(int fd, int open_flags, uint64_t offset, uint64_t size, uint32_t loop_flags, LoopDevice **ret);
+static inline int loop_device_make(int fd, int open_flags, uint32_t loop_flags, LoopDevice **ret) {
+        return loop_device_make_full(fd, open_flags, 0, 0, loop_flags, ret);
+}
+
+int loop_device_make_by_path(const char *path, int open_flags, uint32_t loop_flags, LoopDevice **ret);
+int loop_device_open(const char *loop_path, int open_flags, LoopDevice **ret);
 
 LoopDevice* loop_device_unref(LoopDevice *d);
 DEFINE_TRIVIAL_CLEANUP_FUNC(LoopDevice*, loop_device_unref);
 
 void loop_device_relinquish(LoopDevice *d);
+
+int loop_device_refresh_size(LoopDevice *d, uint64_t offset, uint64_t size);
+
+int loop_device_flock(LoopDevice *d, int operation);
index a11803e731adfa5424f87666c900fca83b2ecf21..15fd514353a78f3fc9cc2deb8d51eb24413076d9 100644 (file)
@@ -2,13 +2,14 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/fs.h>
+#include <linux/loop.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/file.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <unistd.h>
-#include <linux/fs.h>
 
 #include "alloc-util.h"
 #include "btrfs-util.h"
@@ -1166,7 +1167,7 @@ int image_read_metadata(Image *i) {
                 _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
                 _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
 
-                r = loop_device_make_by_path(i->path, O_RDONLY, &d);
+                r = loop_device_make_by_path(i->path, O_RDONLY, LO_FLAGS_PARTSCAN, &d);
                 if (r < 0)
                         return r;
 
index 6c26cb9fb5673d62eac07cbdbe20216288ca6b04..cf23ad450c5ceb49b66fa9efd14604e77f4483b6 100644 (file)
@@ -3,6 +3,8 @@
 
 #include <stdlib.h>
 
+#include "sd-daemon.h"
+
 #include "pager.h"
 #include "selinux-util.h"
 #include "spawn-ask-password-agent.h"
@@ -16,6 +18,8 @@
                 save_argc_argv(argc, argv);                             \
                 intro;                                                  \
                 r = impl;                                               \
+                if (r < 0)                                              \
+                        (void) sd_notifyf(0, "ERRNO=%i", -r);           \
                 ask_password_agent_close();                             \
                 polkit_agent_close();                                   \
                 pager_close();                                          \
index b14c92697b44803ad2d26d0409ff849de0d71684..2ec726a897abb23cd56d2e30514c258e9e599ee7 100644 (file)
@@ -23,6 +23,7 @@
 #include <sys/inotify.h>
 #include <sys/signalfd.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <time.h>
 
 #include "_sd-common.h"
@@ -89,6 +90,7 @@ int sd_event_add_io(sd_event *e, sd_event_source **s, int fd, uint32_t events, s
 int sd_event_add_time(sd_event *e, sd_event_source **s, clockid_t clock, uint64_t usec, uint64_t accuracy, sd_event_time_handler_t callback, void *userdata);
 int sd_event_add_signal(sd_event *e, sd_event_source **s, int sig, sd_event_signal_handler_t callback, void *userdata);
 int sd_event_add_child(sd_event *e, sd_event_source **s, pid_t pid, int options, sd_event_child_handler_t callback, void *userdata);
+int sd_event_add_child_pidfd(sd_event *e, sd_event_source **s, int pidfd, int options, sd_event_child_handler_t callback, void *userdata);
 int sd_event_add_inotify(sd_event *e, sd_event_source **s, const char *path, uint32_t mask, sd_event_inotify_handler_t callback, void *userdata);
 int sd_event_add_defer(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
 int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
@@ -141,6 +143,16 @@ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec);
 int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock);
 int sd_event_source_get_signal(sd_event_source *s);
 int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid);
+int sd_event_source_get_child_pidfd(sd_event_source *s);
+int sd_event_source_get_child_pidfd_own(sd_event_source *s);
+int sd_event_source_set_child_pidfd_own(sd_event_source *s, int own);
+int sd_event_source_get_child_process_own(sd_event_source *s);
+int sd_event_source_set_child_process_own(sd_event_source *s, int own);
+#if defined _GNU_SOURCE || (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L)
+int sd_event_source_send_child_signal(sd_event_source *s, int sig, const siginfo_t *si, unsigned flags);
+#else
+int sd_event_source_send_child_signal(sd_event_source *s, int sig, const void *si, unsigned flags);
+#endif
 int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret);
 int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback);
 int sd_event_source_get_destroy_callback(sd_event_source *s, sd_event_destroy_t *ret);
index 661eb1795a1fde802bdae39454bcba3b1e35a79a..18b083d87fcb9d0abccd5d8996c580aa304ac122 100644 (file)
@@ -299,6 +299,15 @@ static const char* const config_file[] = {
         "[Section]\n"
         "setting1="          /* many continuation lines, together above the limit */
         x1000(x1000("x") x10("abcde") "\\\n") "xxx",
+
+        "[Section]\n"
+        "setting1=2\n"
+        "[NoWarnSection]\n"
+        "setting1=3\n"
+        "[WarnSection]\n"
+        "setting1=3\n"
+        "[X-Section]\n"
+        "setting1=3\n",
 };
 
 static void test_config_parse(unsigned i, const char *s) {
@@ -325,14 +334,12 @@ static void test_config_parse(unsigned i, const char *s) {
                          const char *sections,
                          ConfigItemLookup lookup,
                          const void *table,
-                         bool relaxed,
-                         bool allow_include,
-                         bool warn,
+                         ConfigParseFlags flags,
                          void *userdata)
         */
 
         r = config_parse(NULL, name, f,
-                         "Section\0",
+                         "Section\0-NoWarnSection\0",
                          config_item_table_lookup, items,
                          CONFIG_PARSE_WARN, NULL);
 
@@ -366,6 +373,11 @@ static void test_config_parse(unsigned i, const char *s) {
                 assert_se(r == -ENOBUFS);
                 assert_se(setting1 == NULL);
                 break;
+
+        case 17:
+                assert_se(r == 0);
+                assert_se(streq(setting1, "2"));
+                break;
         }
 }
 
index 7b32e8373f920a01ca44e3b67958c3375cbca783..12685dad1373de390555d5b7a779f3221470d798 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include <fcntl.h>
+#include <linux/loop.h>
 #include <stdio.h>
 
 #include "dissect-image.h"
@@ -21,7 +22,7 @@ int main(int argc, char *argv[]) {
                 return EXIT_FAILURE;
         }
 
-        r = loop_device_make_by_path(argv[1], O_RDONLY, &d);
+        r = loop_device_make_by_path(argv[1], O_RDONLY, LO_FLAGS_PARTSCAN, &d);
         if (r < 0) {
                 log_error_errno(r, "Failed to set up loopback device: %m");
                 return EXIT_FAILURE;
index 8ea399436621939d8ad8c646cc245e100c713522..7a05afb4acaadb3313c1414444c13418a8eca815 100644 (file)
@@ -9,6 +9,29 @@
 #include "utf8.h"
 #include "util.h"
 
+static void test_string_erase(void) {
+        char *x;
+
+        x = strdupa("");
+        assert_se(streq(string_erase(x), ""));
+
+        x = strdupa("1");
+        assert_se(streq(string_erase(x), ""));
+
+        x = strdupa("123456789");
+        assert_se(streq(string_erase(x), ""));
+
+        assert_se(x[1] == '\0');
+        assert_se(x[2] == '\0');
+        assert_se(x[3] == '\0');
+        assert_se(x[4] == '\0');
+        assert_se(x[5] == '\0');
+        assert_se(x[6] == '\0');
+        assert_se(x[7] == '\0');
+        assert_se(x[8] == '\0');
+        assert_se(x[9] == '\0');
+}
+
 static void test_free_and_strndup_one(char **t, const char *src, size_t l, const char *expected, bool change) {
         int r;
 
@@ -543,6 +566,7 @@ static void test_memory_startswith_no_case(void) {
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_DEBUG);
 
+        test_string_erase();
         test_free_and_strndup();
         test_ascii_strcasecmp_n();
         test_ascii_strcasecmp_nn();
index cb10ca306a478d47d5ad91093c25fde6a84f6b61..2a6f111d83b65b2da47c3ef7239d1287ffb69eaa 100644 (file)
@@ -270,3 +270,7 @@ NetworkEmulatorDelayJitterSec=
 NetworkEmulatorLossRate=
 NetworkEmulatorDuplicateRate=
 NetworkEmulatorPacketLimit=
+TokenBufferFilterRate=
+TokenBufferFilterBurst=
+TokenBufferFilterLatencySec=
+StochasticFairnessQueueingPerturbPeriodSec=
diff --git a/test/test-network/conf/25-qdisc-tbf-and-sfq.network b/test/test-network/conf/25-qdisc-tbf-and-sfq.network
new file mode 100644 (file)
index 0000000..7a6d331
--- /dev/null
@@ -0,0 +1,16 @@
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.4/16
+
+[TrafficControlQueueingDiscipline]
+Parent=root
+TokenBufferFilterRate=0.5M
+TokenBufferFilterBurst=5K
+TokenBufferFilterLatencySec=70msec
+
+[TrafficControlQueueingDiscipline]
+Parent=clsact
+StochasticFairnessQueueingPerturbPeriodSec=5sec
index ab6e11e0e527dbbe022298832913cee1c288c427..f47463956e75455e4179b6d87bc0106dd8922c12 100755 (executable)
@@ -1498,7 +1498,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         '25-neighbor-ip-dummy.network',
         '25-neighbor-ip.network',
         '25-nexthop.network',
-        '25-qdisc.network',
+        '25-qdisc-netem.network',
+        '25-qdisc-tbf-and-sfq.network',
         '25-route-ipv6-src.network',
         '25-route-static.network',
         '25-gateway-static.network',
@@ -2057,15 +2058,23 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, '192.168.5.1')
 
     def test_qdisc(self):
-        copy_unit_to_networkd_unit_path('25-qdisc.network', '12-dummy.netdev')
+        copy_unit_to_networkd_unit_path('25-qdisc-netem.network', '12-dummy.netdev',
+                                        '25-qdisc-tbf-and-sfq.network', '11-dummy.netdev')
         start_networkd()
 
-        self.wait_online(['dummy98:routable'])
+        self.wait_online(['dummy98:routable', 'test1:routable'])
 
         output = check_output('tc qdisc show dev dummy98')
         print(output)
+        self.assertRegex(output, 'qdisc netem')
         self.assertRegex(output, 'limit 100 delay 50.0ms  10.0ms loss 20%')
         self.assertRegex(output, 'limit 200 delay 100.0ms  13.0ms loss 20.5%')
+        output = check_output('tc qdisc show dev test1')
+        print(output)
+        self.assertRegex(output, 'qdisc tbf')
+        self.assertRegex(output, 'rate 500Kbit burst 5000b lat 70.0ms')
+        self.assertRegex(output, 'qdisc sfq')
+        self.assertRegex(output, 'perturb 5sec')
 
 class NetworkdStateFileTests(unittest.TestCase, Utilities):
     links = [