]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #13022 from keszybz/coverity-cleanups
authorLennart Poettering <lennart@poettering.net>
Fri, 12 Jul 2019 05:37:44 +0000 (07:37 +0200)
committerGitHub <noreply@github.com>
Fri, 12 Jul 2019 05:37:44 +0000 (07:37 +0200)
Coverity cleanups

181 files changed:
TODO
man/systemctl.xml
man/systemd.exec.xml
man/systemd.netdev.xml
man/systemd.network.xml
man/systemd.service.xml
man/systemd.timer.xml
man/tmpfiles.d.xml
src/activate/activate.c
src/basic/btrfs-util.c
src/basic/cgroup-util.c
src/basic/env-util.c
src/basic/errno-util.h
src/basic/fileio.c
src/basic/glob-util.c
src/basic/in-addr-util.c
src/basic/in-addr-util.h
src/basic/io-util.c
src/basic/memfd-util.c
src/basic/rm-rf.c
src/basic/rm-rf.h
src/basic/socket-util.c
src/basic/string-util.c
src/basic/string-util.h
src/basic/strv.c
src/basic/unit-def.c
src/basic/unit-def.h
src/basic/unit-name.c
src/basic/user-util.c
src/binfmt/binfmt.c
src/core/dbus-cgroup.c
src/core/dbus-manager.c
src/core/dbus-service.c
src/core/dbus-unit.c
src/core/dbus-unit.h
src/core/dynamic-user.c
src/core/execute.c
src/core/execute.h
src/core/job.c
src/core/job.h
src/core/load-fragment-gperf.gperf.m4
src/core/manager.c
src/core/mount-setup.c
src/core/service.c
src/core/service.h
src/core/timer.c
src/core/transaction.c
src/core/unit.c
src/core/unit.h
src/coredump/coredump.c
src/cryptsetup/cryptsetup-generator.c
src/debug-generator/debug-generator.c
src/getty-generator/getty-generator.c
src/gpt-auto-generator/gpt-auto-generator.c
src/hostname/hostnamed.c
src/import/pull-job.c
src/journal/journalctl.c
src/journal/journald-kmsg.c
src/journal/journald-server.c
src/journal/journald-stream.c
src/journal/test-journal-flush.c
src/libsystemd/sd-bus/bus-common-errors.h
src/libsystemd/sd-bus/bus-control.c
src/libsystemd/sd-bus/bus-control.h
src/libsystemd/sd-bus/bus-error.c
src/libsystemd/sd-bus/bus-internal.h
src/libsystemd/sd-bus/bus-match.c
src/libsystemd/sd-bus/bus-message.h
src/libsystemd/sd-bus/bus-socket.c
src/libsystemd/sd-bus/sd-bus.c
src/libsystemd/sd-login/sd-login.c
src/libsystemd/sd-netlink/netlink-message.c
src/libsystemd/sd-netlink/netlink-types.c
src/libsystemd/sd-path/sd-path.c
src/locale/keymap-util.c
src/login/logind-acl.c
src/login/logind-core.c
src/login/logind-dbus.c
src/login/logind-inhibit.c
src/login/logind-seat-dbus.c
src/login/logind-seat.c
src/login/logind-session-dbus.c
src/login/logind-session.c
src/login/sysfs-show.c
src/machine/image-dbus.c
src/machine/machine-dbus.c
src/machine/machine.c
src/machine/machined-dbus.c
src/network/netdev/netdev-gperf.gperf
src/network/netdev/tunnel.c
src/network/netdev/tunnel.h
src/network/netdev/xfrm.c
src/network/networkctl.c
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp-common.h
src/network/networkd-dhcp4.c
src/network/networkd-dhcp6.c
src/network/networkd-fdb.c
src/network/networkd-fdb.h
src/network/networkd-link.c
src/network/networkd-manager.c
src/network/networkd-ndisc.c
src/network/networkd-neighbor.c
src/network/networkd-neighbor.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-route.c
src/network/networkd-route.h
src/notify/notify.c
src/nspawn/nspawn-network.c
src/nspawn/nspawn-settings.c
src/nspawn/nspawn.c
src/resolve/resolvectl.c
src/resolve/resolved-dns-answer.c
src/resolve/resolved-dns-dnssec.c
src/resolve/resolved-manager.c
src/shared/bus-unit-procs.c
src/shared/bus-unit-util.c
src/shared/bus-util.c
src/shared/bus-wait-for-units.c [new file with mode: 0644]
src/shared/bus-wait-for-units.h [new file with mode: 0644]
src/shared/cgroup-show.c
src/shared/dissect-image.c
src/shared/generator.c
src/shared/install.c
src/shared/json.c
src/shared/machine-image.c
src/shared/meson.build
src/shared/path-lookup.c
src/sysctl/sysctl.c
src/systemctl/systemctl.c
src/sysv-generator/sysv-generator.c
src/test/test-conf-files.c
src/test/test-copy.c
src/test/test-engine.c
src/test/test-path-util.c
src/test/test-string-util.c
src/timedate/timedated.c
src/tmpfiles/tmpfiles.c
src/tty-ask-password-agent/tty-ask-password-agent.c
src/udev/udev-builtin-blkid.c
src/udev/udevadm-info.c
test/TEST-33-CLEAN-UNIT/Makefile [new symlink]
test/TEST-33-CLEAN-UNIT/test.sh [new file with mode: 0755]
test/TEST-33-CLEAN-UNIT/testsuite.sh [new file with mode: 0755]
test/a-conj.service [new file with mode: 0644]
test/fuzz/fuzz-netdev-parser/directives.netdev
test/fuzz/fuzz-network-parser/directives.network
test/i.service [new file with mode: 0644]
test/meson.build
test/test-network/conf/25-gre-tunnel-any-any.netdev [new file with mode: 0644]
test/test-network/conf/25-ip6gre-tunnel-any-any.netdev [new file with mode: 0644]
test/test-network/conf/25-ipip-tunnel-any-any.netdev [new file with mode: 0644]
test/test-network/conf/25-ipip-tunnel-independent-loopback.netdev [new file with mode: 0644]
test/test-network/conf/25-neighbor-ip-dummy.network [new file with mode: 0644]
test/test-network/conf/25-neighbor-ip.network [new file with mode: 0644]
test/test-network/conf/25-neighbor-section.network
test/test-network/conf/25-route-static.network
test/test-network/conf/25-sit-tunnel-any-any.netdev [new file with mode: 0644]
test/test-network/conf/25-tunnel-any-any.network [new file with mode: 0644]
test/test-network/conf/25-vti-tunnel-any-any.netdev [new file with mode: 0644]
test/test-network/conf/25-xfrm-independent.netdev [new file with mode: 0644]
test/test-network/conf/25-xfrm.netdev [new file with mode: 0644]
test/test-network/conf/26-bridge-slave-interface-1.network
test/test-network/conf/26-bridge-slave-interface-2.network
test/test-network/conf/dhcp-client-reassign-static-routes-ipv4.network [moved from test/test-network/conf/dhcp-client-use-routes-no.network with 100% similarity]
test/test-network/conf/dhcp-client-reassign-static-routes-ipv6.network [new file with mode: 0644]
test/test-network/conf/dhcp-client-use-dns-ipv4-and-ra.network [new file with mode: 0644]
test/test-network/conf/dhcp-client-use-dns-ipv4.network [new file with mode: 0644]
test/test-network/conf/dhcp-client-use-dns-no.network [new file with mode: 0644]
test/test-network/conf/dhcp-client-use-dns-yes.network [new file with mode: 0644]
test/test-network/conf/gretun.network
test/test-network/conf/ip6gretun.network
test/test-network/conf/ipip.network
test/test-network/conf/netdev-link-local-addressing-yes.network
test/test-network/conf/sit.network
test/test-network/conf/vti.network
test/test-network/conf/xfrm.network [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py
travis-ci/managers/fuzzit.sh

diff --git a/TODO b/TODO
index df5e18bd91213b4466a46457feec4b9bdc8ecb31..24a2e2590e022c5104500c876299587b9d9efa1f 100644 (file)
--- a/TODO
+++ b/TODO
@@ -313,9 +313,6 @@ Features:
 * expose IO accounting data on the bus, show it in systemd-run --wait and log
   about it in the resource log message
 
-* add "systemctl purge" for flushing out configuration, state, logs, ... of a
-  unit when it is stopped
-
 * show whether a service has out-of-date configuration in "systemctl status" by
   using mtime data of ConfigurationDirectory=.
 
index 5ebe1832bcdf92b2cd4138fa0d89e246248e7e5c..b2e3cbcb214d9085ddafd8721d6d672806acc9fb 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--what=</option></term>
+
+        <listitem>
+          <para>Select what type of per-unit resources to remove when the <command>clean</command> command is
+          invoked, see below. Takes one of <constant>configuration</constant>, <constant>state</constant>,
+          <constant>cache</constant>, <constant>logs</constant>, <constant>runtime</constant> to select the
+          type of resource. This option may be specified more than once, in which case all specified resource
+          types are removed. Also accepts the special value <constant>all</constant> as a shortcut for
+          specifiying all five resource types. If this option is not specified defaults to the combination of
+          <constant>cache</constant> and <constant>runtime</constant>, i.e. the two kinds of resources that
+          are generally considered to be redundant and can be reconstructed on next invocation.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>-f</option></term>
         <term><option>--force</option></term>
@@ -904,6 +919,24 @@ Sun 2017-02-26 20:57:49 EST  2h 3min left  Sun 2017-02-26 11:56:36 EST  6h ago
             the signal to send.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><command>clean <replaceable>PATTERN</replaceable>…</command></term>
+
+          <listitem>
+            <para>Remove the configuration, state, cache, logs or runtime data of the specified units. Use
+            <option>--what=</option> to select which kind of resource to remove. For service units this may
+            be used to remove the directories configured with <varname>ConfigurationDirectory=</varname>,
+            <varname>StateDirectory=</varname>, <varname>CacheDirectory=</varname>,
+            <varname>LogsDirectory=</varname> and <varname>RuntimeDirectory=</varname>, see
+            <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+            for details. For timer units this may be used to clear out the persistent timestamp data if
+            <varname>Persistent=</varname> is used and <option>--what=state</option> is selected, see
+            <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
+            command only applies to units that use either of these settings. If <option>--what=</option> is
+            not specified, both the cache and runtime data are removed (as these two types of data are
+            generally redundant and reproducible on the next invocation of the unit).</para>
+          </listitem>
+        </varlistentry>
         <varlistentry>
           <term><command>is-active <replaceable>PATTERN</replaceable>…</command></term>
 
index 56a029a82eeb95e63d0786b501369b29013de28d..48dd42ca3cf557cc82eaaf4cf080e3c624f06521 100644 (file)
@@ -981,6 +981,11 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
         the directories is tied directly to the lifetime of the unit, and it is not necessary to ensure that the
         <filename>tmpfiles.d</filename> configuration is executed before the unit is started.</para>
 
+        <para>To remove any of the directories created by these settings, use the <command>systemctl clean
+        â€¦</command> command on the relevant units, see
+        <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+        details.</para>
+
         <para>Example: if a system service unit has the following,
         <programlisting>RuntimeDirectory=foo/bar baz</programlisting>
         the service manager creates <filename>/run/foo</filename> (if it does not exist),
index 3cce776cc226b1f7f74dbcd5d826e3a86719de3a..ff37f26990cfb3de8648a2575efbc15e51c8c583 100644 (file)
           </para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>AssignToLoopback=</varname></term>
+        <listitem>
+          <para>Takes a boolean. If set to <literal>yes</literal>, the loopback interface <literal>lo</literal>
+          is used as the underlying device of the tunnel interface. Defaults to <literal>no</literal>.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>AllowLocalRemote=</varname></term>
         <listitem>
index c48b294551be95f8cb609eb1a3ed88e8578df4e9..c9d6fd4d72caaf5fda09159186c51dca7e585f9e 100644 (file)
             specified through DHCP is not used for name resolution.
             See option <option>UseDomains=</option> below.</para>
 
-            <para>See the <literal>[DHCP]</literal> section below for further configuration options for the DHCP client
-            support.</para>
+            <para>See the <literal>[DHCPv4]</literal> or <literal>[DHCPv6]</literal> section below for
+            further configuration options for the DHCP client support.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
           </listitem>
         </varlistentry>
         <varlistentry>
-          <term><varname>MACAddress=</varname></term>
+          <term><varname>LinkLayerAddress=</varname></term>
           <listitem>
-            <para>The hardware address of the neighbor.</para>
+            <para>The link layer address (MAC address or IP address) of the neighbor.</para>
           </listitem>
         </varlistentry>
       </variablelist>
         <varlistentry>
           <term><varname>Table=</varname></term>
           <listitem>
-            <para>Specifies the routing table identifier to lookup if the rule
-            selector matches. The table identifier for a route (a number between 1 and 4294967295).</para>
+            <para>Specifies the routing table identifier to lookup if the rule selector matches. Takes
+            one of <literal>default</literal>, <literal>main</literal>, and <literal>local</literal>,
+            or a number between 1 and 4294967295. Defaults to <literal>main</literal>.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
         <varlistentry>
           <term><varname>Type=</varname></term>
           <listitem>
-            <para>Specifies the type for the route. If <literal>unicast</literal>, a regular route is defined, i.e. a
+            <para>Specifies the type for the route. Takes one of <literal>unicast</literal>,
+            <literal>local</literal>, <literal>broadcast</literal>, <literal>anycast</literal>,
+            <literal>multicast</literal>, <literal>blackhole</literal>, <literal>unreachable</literal>,
+            <literal>prohibit</literal>, <literal>throw</literal>, <literal>nat</literal>, and
+            <literal>xresolve</literal>. If <literal>unicast</literal>, a regular route is defined, i.e. a
             route indicating the path to take to a destination network address. If <literal>blackhole</literal>, packets
             to the defined route are discarded silently. If <literal>unreachable</literal>, packets to the defined route
             are discarded and the ICMP message "Host Unreachable" is generated. If <literal>prohibit</literal>, packets
   </refsect1>
 
   <refsect1>
-    <title>[DHCP] Section Options</title>
-      <para>The <literal>[DHCP]</literal> section configures the
-      DHCPv4 and DHCP6 client, if it is enabled with the
+    <title>[DHCPv4] Section Options</title>
+      <para>The <literal>[DHCPv4]</literal> section configures the
+      DHCPv4 client, if it is enabled with the
       <varname>DHCP=</varname> setting described above:</para>
 
       <variablelist class='network-directives'>
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><varname>BlackList=</varname></term>
+          <listitem>
+            <para>A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are rejected.</para>
+          </listitem>
+        </varlistentry>
+
+       </variablelist>
+   </refsect1>
+
+  <refsect1>
+    <title>[DHCPv6] Section Options</title>
+      <para>The <literal>[DHCPv6]</literal> section configures the DHCPv6 client, if it is enabled with the
+      <varname>DHCP=</varname> setting described above, or invoked by the IPv6 Router Advertisement:</para>
+
+      <variablelist class='network-directives'>
+        <varlistentry>
+          <term><varname>UseDNS=</varname></term>
+          <term><varname>UseNTP=</varname></term>
+          <listitem>
+            <para>As in the <literal>[DHCP]</literal> section.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><varname>RapidCommit=</varname></term>
           <listitem>
           </listitem>
         </varlistentry>
 
-        <varlistentry>
-          <term><varname>BlackList=</varname></term>
-          <listitem>
-            <para>A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are rejected.</para>
-          </listitem>
-        </varlistentry>
-
       </variablelist>
-    </refsect1>
+  </refsect1>
 
   <refsect1>
     <title>[IPv6AcceptRA] Section Options</title>
index 22329f6c2f16f5cd169e1fdf0f408c46d0f66587..145f97206c5fb3f0acaafb235c332f605f702c11 100644 (file)
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>TimeoutCleanSec=</varname></term>
+        <listitem><para>Configures a timeout on the clean-up operation requested through <command>systemctl
+        clean â€¦</command>, see
+        <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+        details. Takes the usual time values and defaults to <constant>infinity</constant>, i.e. by default
+        no time-out is applied. If a time-out is configured the clean operation will be aborted forcibly when
+        the time-out is reached, potentially leaving resources on disk.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>RuntimeMaxSec=</varname></term>
 
index 340286d9128aa5cef8f3bb495951723d3f68dc81..0f6518dbc2d94006980f647b10b2fa5f7297a295 100644 (file)
       <varlistentry>
         <term><varname>Persistent=</varname></term>
 
-        <listitem><para>Takes a boolean argument. If true, the time
-        when the service unit was last triggered is stored on disk.
-        When the timer is activated, the service unit is triggered
-        immediately if it would have been triggered at least once
-        during the time when the timer was inactive. This is useful to
-        catch up on missed runs of the service when the machine was
-        off. Note that this setting only has an effect on timers
-        configured with <varname>OnCalendar=</varname>. Defaults
-        to <varname>false</varname>.
-        </para></listitem>
+        <listitem><para>Takes a boolean argument. If true, the time when the service unit was last triggered
+        is stored on disk.  When the timer is activated, the service unit is triggered immediately if it
+        would have been triggered at least once during the time when the timer was inactive. This is useful
+        to catch up on missed runs of the service when the system was powered down. Note that this setting
+        only has an effect on timers configured with <varname>OnCalendar=</varname>. Defaults to
+        <varname>false</varname>.</para>
+
+        <para>Use <command>systemctl clean --what=state â€¦</command> on the timer unit to remove the timestamp
+        file maintained by this option from disk. In particular, use this command before uninstalling a timer
+        unit. See
+        <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+        details.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index ac6565ec93cb62a4f7aa5b8a5c20c78a7232fc71..f097045b7bbeea4c8420695aa77ac536e8576689 100644 (file)
 <filename>…</filename>
 <filename>/usr/share/user-tmpfiles.d/*.conf</filename>
     </literallayout></para>
+
+    <programlisting>#Type Path                                     Mode User Group Age         Argument
+f     /file/to/create                          mode user group -           content
+F     /file/to/create-or-truncate              mode user group -           content
+w     /file/to/write-to                        -    -    -     -           content
+d     /directory/to/create-and-cleanup         mode user group cleanup-age -
+D     /directory/to/create-and-remove          mode user group cleanup-age -
+e     /directory/to/cleanup                    mode user group cleanup-age -
+v     /subvolume/to/create                     mode user group -           -
+v     /subvolume-or-directory/to/create        mode user group -           -
+Q     /subvolume/to/create                     mode user group -           -
+p     /fifo/to/create                          mode user group -           -
+L     /symlink/to/create                       -    -    -     -           symlink/target/path
+c     /dev/char-device-to-create               mode user group -           -
+b     /dev/block-device-to-create              mode user group -           -
+# p+, L+, c+, b+ create target unconditionally
+C     /target/to/create                        -    -    -     -           /source/to/copy
+x     /path-or-glob/to/ignore                  -    -    -     -           -
+X     /path-or-glob/to/ignore/recursively      -    -    -     -           -
+r     /empty/dir/to/remove                     -    -    -     -           -
+R     /dir/to/remove/recursively               -    -    -     -           -
+z     /path-or-glob/to/adjust/mode             mode user group -           MAC context
+Z     /path-or-glob/to/adjust/mode/recursively mode user group -           MAC context
+t     /path-or-glob/to/set/xattrs              -    -    -     -           xattrs
+T     /path-or-glob/to/set/xattrs/recursively  -    -    -     -           xattrs
+h     /path-or-glob/to/set/attrs               -    -    -     -           file attrs
+H     /path-or-glob/to/set/attrs/recursively   -    -    -     -           file attrs
+a     /path-or-glob/to/set/acls                -    -    -     -           POSIX ACLs
+A     /path-or-glob/to/set/acls/recursively    -    -    -     -           POSIX ACLs
+# a+, A+ append ACLs
+</programlisting>
   </refsynopsisdiv>
 
   <refsect1>
index 8bdf2e50aae43f1c71dc559e056e9ccf424cca77..15a3150666879dba70bcd4c9fe21b7a528d51d0e 100644 (file)
@@ -151,7 +151,7 @@ static int exec_process(const char *name, char **argv, char **env, int start_fd,
                         _cleanup_free_ char *p;
                         const char *n;
 
-                        p = strappend(*s, "=");
+                        p = strjoin(*s, "=");
                         if (!p)
                                 return log_oom();
 
@@ -226,7 +226,7 @@ static int exec_process(const char *name, char **argv, char **env, int start_fd,
                         if (!names)
                                 return log_oom();
 
-                        e = strappend("LISTEN_FDNAMES=", names);
+                        e = strjoin("LISTEN_FDNAMES=", names);
                         if (!e)
                                 return log_oom();
 
index 10ca893da016fb9430327ee489519f6a101fea49..1ee0110e2ff070efdf73cedf5c1fababab990ade 100644 (file)
@@ -1517,12 +1517,7 @@ static int subvol_snapshot_children(
                         if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
                                 return -errno;
 
-                        /* The kernel returns an empty name if the
-                         * subvolume is in the top-level directory,
-                         * and otherwise appends a slash, so that we
-                         * can just concatenate easily here, without
-                         * adding a slash. */
-                        c = strappend(ino_args.name, p);
+                        c = path_join(ino_args.name, p);
                         if (!c)
                                 return -ENOMEM;
 
index 9b145aa70f4c8bafa45ca47077e2af6f965568f2..7d610475a83a8b04b0b581f48b5d6338bf5db115 100644 (file)
@@ -81,7 +81,7 @@ int cg_read_pid(FILE *f, pid_t *_pid) {
                 if (feof(f))
                         return 0;
 
-                return errno > 0 ? -errno : -EIO;
+                return errno_or_else(EIO);
         }
 
         if (ul <= 0)
@@ -770,10 +770,8 @@ int cg_trim(const char *controller, const char *path, bool delete_root) {
         if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) {
                 if (errno == ENOENT)
                         r = 0;
-                else if (errno > 0)
-                        r = -errno;
                 else
-                        r = -EIO;
+                        r = errno_or_else(EIO);
         }
 
         if (delete_root) {
@@ -1924,7 +1922,7 @@ char *cg_escape(const char *p) {
         }
 
         if (need_prefix)
-                return strappend("_", p);
+                return strjoin("_", p);
 
         return strdup(p);
 }
@@ -2502,8 +2500,8 @@ int cg_kernel_controllers(Set **ret) {
                         if (feof(f))
                                 break;
 
-                        if (ferror(f) && errno > 0)
-                                return -errno;
+                        if (ferror(f))
+                                return errno_or_else(EIO);
 
                         return -EBADMSG;
                 }
index b39b1ed0f8bd16e83ce71edf4da2a24659089468..a6503cf2b65175ff7e9da9fd746fefaeb363eebe 100644 (file)
@@ -567,7 +567,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
 
                                 t = strv_env_get_n(env, word+2, e-word-2, flags);
 
-                                k = strappend(r, t);
+                                k = strjoin(r, t);
                                 if (!k)
                                         return NULL;
 
@@ -623,7 +623,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
                                 else if (!t && state == DEFAULT_VALUE)
                                         t = v = replace_env_n(test_value, e-test_value, env, flags);
 
-                                k = strappend(r, t);
+                                k = strjoin(r, t);
                                 if (!k)
                                         return NULL;
 
@@ -642,7 +642,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
 
                                 t = strv_env_get_n(env, word+1, e-word-1, flags);
 
-                                k = strappend(r, t);
+                                k = strjoin(r, t);
                                 if (!k)
                                         return NULL;
 
@@ -661,7 +661,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
                 assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
 
                 t = strv_env_get_n(env, word+1, e-word-1, flags);
-                return strappend(r, t);
+                return strjoin(r, t);
         } else
                 return strnappend(r, word, e-word);
 }
index 3ad732b91ee2ea6ad8022491ebd1e178b5563e80..34859d6d8a970fa4c62bc6bbbaf557d894030eeb 100644 (file)
@@ -36,6 +36,17 @@ static inline char *strerror_safe(int error) {
         return strerror(abs(error));
 }
 
+static inline int errno_or_else(int fallback) {
+        /* To be used when invoking library calls where errno handling is not defined clearly: we return
+         * errno if it is set, and the specified error otherwise. The idea is that the caller initializes
+         * errno to zero before doing an API call, and then uses this helper to retrieve a somewhat useful
+         * error code */
+        if (errno > 0)
+                return -errno;
+
+        return -abs(fallback);
+}
+
 /* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5.
  *
  * Hint #2: The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases.  See the
index 40a20c2b3b7918943090e83b0f261a64bbffd484..bd4c964bec560c5cab01ef8143d071baef4b32b9 100644 (file)
@@ -298,7 +298,7 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
         errno = 0;
         k = fread(buf, 1, l + accept_extra_nl + 1, f);
         if (ferror(f))
-                return errno > 0 ? -errno : -EIO;
+                return errno_or_else(EIO);
 
         if (k != l && k != l + accept_extra_nl)
                 return 0;
@@ -382,7 +382,7 @@ int read_full_stream_full(
                         l += k;
 
                 if (ferror(f)) {
-                        r = errno > 0 ? -errno : -EIO;
+                        r = errno_or_else(EIO);
                         goto finalize;
                 }
 
@@ -661,7 +661,7 @@ int fflush_and_check(FILE *f) {
         fflush(f);
 
         if (ferror(f))
-                return errno > 0 ? -errno : -EIO;
+                return errno_or_else(EIO);
 
         return 0;
 }
@@ -888,7 +888,7 @@ int safe_fgetc(FILE *f, char *ret) {
         k = fgetc(f);
         if (k == EOF) {
                 if (ferror(f))
-                        return errno > 0 ? -errno : -EIO;
+                        return errno_or_else(EIO);
 
                 if (ret)
                         *ret = 0;
index 32c53f8c736f05920683f5d42ab860d6f072dd56..b335af8d971ca7e84584847ea1739c8ceadfd8f8 100644 (file)
@@ -8,6 +8,7 @@
 #include <unistd.h>
 
 #include "dirent-util.h"
+#include "errno-util.h"
 #include "glob-util.h"
 #include "macro.h"
 #include "path-util.h"
@@ -36,13 +37,12 @@ int safe_glob(const char *path, int flags, glob_t *pglob) {
 
         errno = 0;
         k = glob(path, flags | GLOB_ALTDIRFUNC, NULL, pglob);
-
         if (k == GLOB_NOMATCH)
                 return -ENOENT;
         if (k == GLOB_NOSPACE)
                 return -ENOMEM;
         if (k != 0)
-                return errno > 0 ? -errno : -EIO;
+                return errno_or_else(EIO);
         if (strv_isempty(pglob->gl_pathv))
                 return -ENOENT;
 
index c1fab51b5e43db651d29dce01077d269bb98d862..06b92db579fda40e2a728eab9a963d32f41e37b5 100644 (file)
@@ -9,6 +9,7 @@
 #include <stdlib.h>
 
 #include "alloc-util.h"
+#include "errno-util.h"
 #include "in-addr-util.h"
 #include "macro.h"
 #include "parse-util.h"
@@ -91,12 +92,19 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) {
         return -EAFNOSUPPORT;
 }
 
+bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b) {
+        assert(a);
+        assert(b);
+
+        return a->s_addr == b->s_addr;
+}
+
 int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) {
         assert(a);
         assert(b);
 
         if (family == AF_INET)
-                return a->in.s_addr == b->in.s_addr;
+                return in4_addr_equal(&a->in, &b->in);
 
         if (family == AF_INET6)
                 return
@@ -315,7 +323,7 @@ int in_addr_to_string(int family, const union in_addr_union *u, char **ret) {
 
         errno = 0;
         if (!inet_ntop(family, u, x, l))
-                return errno > 0 ? -errno : -EINVAL;
+                return errno_or_else(EINVAL);
 
         *ret = TAKE_PTR(x);
         return 0;
@@ -345,7 +353,7 @@ int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned
 
         errno = 0;
         if (!inet_ntop(family, u, x, l))
-                return errno > 0 ? -errno : -EINVAL;
+                return errno_or_else(EINVAL);
 
         p = x + strlen(x);
         l -= strlen(x);
@@ -384,7 +392,7 @@ int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifin
 
         errno = 0;
         if (!inet_ntop(family, u, x, l))
-                return errno > 0 ? -errno : -EINVAL;
+                return errno_or_else(EINVAL);
 
         sprintf(strchr(x, 0), "%%%i", ifindex);
 
@@ -404,7 +412,7 @@ int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
 
         errno = 0;
         if (inet_pton(family, s, ret ?: &buffer) <= 0)
-                return errno > 0 ? -errno : -EINVAL;
+                return errno_or_else(EINVAL);
 
         return 0;
 }
index 2ca7f4b32fc88b5d8a9ecd2674661839821f4015..28afc7d86cfd91485d6f1fba8c871830d22434d6 100644 (file)
@@ -32,6 +32,7 @@ int in_addr_is_localhost(int family, const union in_addr_union *u);
 
 bool in4_addr_is_non_local(const struct in_addr *a);
 
+bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b);
 int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
 int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
 int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
index d19c78c2ca663385d9c8b5bd4a4856fd6bcb23c4..4acd17df84178d80fcaf8a11b8e6ab891cd01b98 100644 (file)
@@ -257,7 +257,7 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) {
 char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value) {
         char *x;
 
-        x = strappend(field, value);
+        x = strjoin(field, value);
         if (x)
                 iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x);
         return x;
@@ -312,7 +312,7 @@ int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const c
         _cleanup_free_ char *x = NULL;
         int r;
 
-        x = strappend(field, value);
+        x = strjoin(field, value);
         if (!x)
                 return log_oom();
 
index f88f0fc80c6bf12bba51cb15e6d60daa7f6d7daf..a382b0494a4e3b49b1b6b068f5f2d60a871b5638 100644 (file)
@@ -41,7 +41,7 @@ int memfd_new(const char *name) {
                         if (!e)
                                 return -ENOMEM;
 
-                        g = strappend("sd-", e);
+                        g = strjoin("sd-", e);
                         if (!g)
                                 return -ENOMEM;
 
index b751933c83b4c9974d807d9059a19b9278b3a813..796eb93c4b3db0ac9c8607647296d3f892b29d5e 100644 (file)
@@ -161,9 +161,8 @@ int rm_rf(const char *path, RemoveFlags flags) {
         if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME))
                 return -EINVAL;
 
-        /* We refuse to clean the root file system with this
-         * call. This is extra paranoia to never cause a really
-         * seriously broken system. */
+        /* We refuse to clean the root file system with this call. This is extra paranoia to never cause a
+         * really seriously broken system. */
         if (path_equal_or_files_same(path, "/", AT_SYMLINK_NOFOLLOW))
                 return log_error_errno(SYNTHETIC_ERRNO(EPERM),
                                        "Attempted to remove entire root file system (\"%s\"), and we can't allow that.",
@@ -175,6 +174,9 @@ int rm_rf(const char *path, RemoveFlags flags) {
                 if (r >= 0)
                         return r;
 
+                if (FLAGS_SET(flags, REMOVE_MISSING_OK) && r == -ENOENT)
+                        return 0;
+
                 if (!IN_SET(r, -ENOTTY, -EINVAL, -ENOTDIR))
                         return r;
 
@@ -183,34 +185,45 @@ int rm_rf(const char *path, RemoveFlags flags) {
 
         fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
         if (fd < 0) {
+                if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT)
+                        return 0;
+
                 if (!IN_SET(errno, ENOTDIR, ELOOP))
                         return -errno;
 
-                if (!(flags & REMOVE_PHYSICAL)) {
-                        if (statfs(path, &s) < 0)
-                                return -errno;
+                if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES))
+                        return 0;
 
-                        if (is_physical_fs(&s))
-                                return log_error_errno(SYNTHETIC_ERRNO(EPERM),
-                                                       "Attempted to remove files from a disk file system under \"%s\", refusing.",
-                                                       path);
-                }
+                if (FLAGS_SET(flags, REMOVE_ROOT)) {
+
+                        if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) {
+                                if (statfs(path, &s) < 0)
+                                        return -errno;
+
+                                if (is_physical_fs(&s))
+                                        return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+                                                               "Attempted to remove files from a disk file system under \"%s\", refusing.",
+                                                               path);
+                        }
+
+                        if (unlink(path) < 0) {
+                                if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT)
+                                        return 0;
 
-                if ((flags & REMOVE_ROOT) && !(flags & REMOVE_ONLY_DIRECTORIES))
-                        if (unlink(path) < 0 && errno != ENOENT)
                                 return -errno;
+                        }
+                }
 
                 return 0;
         }
 
         r = rm_rf_children(fd, flags, NULL);
 
-        if (flags & REMOVE_ROOT) {
-                if (rmdir(path) < 0) {
-                        if (r == 0 && errno != ENOENT)
-                                r = -errno;
-                }
-        }
+        if (FLAGS_SET(flags, REMOVE_ROOT) &&
+            rmdir(path) < 0 &&
+            r >= 0 &&
+            (!FLAGS_SET(flags, REMOVE_MISSING_OK) || errno != ENOENT))
+                r = -errno;
 
         return r;
 }
index d42ebef4349135ff533de1a372b805a4c0a35a9c..40cbff21c04e50697b19244df6dc73955b0b3243 100644 (file)
@@ -6,10 +6,11 @@
 #include "errno-util.h"
 
 typedef enum RemoveFlags {
-        REMOVE_ONLY_DIRECTORIES = 1 << 0,
-        REMOVE_ROOT             = 1 << 1,
-        REMOVE_PHYSICAL         = 1 << 2, /* if not set, only removes files on tmpfs, never physical file systems */
-        REMOVE_SUBVOLUME        = 1 << 3,
+        REMOVE_ONLY_DIRECTORIES = 1 << 0, /* Only remove empty directories, no files */
+        REMOVE_ROOT             = 1 << 1, /* Remove the specified directory itself too, not just the contents of it */
+        REMOVE_PHYSICAL         = 1 << 2, /* If not set, only removes files on tmpfs, never physical file systems */
+        REMOVE_SUBVOLUME        = 1 << 3, /* Drop btrfs subvolumes in the tree too */
+        REMOVE_MISSING_OK       = 1 << 4, /* If the top-level directory is missing, ignore the ENOENT for it */
 } RemoveFlags;
 
 int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
index e1636bca6e485b28dfd71c353bcfa3260a54d91c..82bda648014cb1cd6a380ed3bd0b0185e6b39b51 100644 (file)
@@ -78,7 +78,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
 
                 errno = 0;
                 if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
-                        return errno > 0 ? -errno : -EINVAL;
+                        return errno_or_else(EINVAL);
 
                 e++;
                 if (*e != ':')
index 474903b2a87b0b96496efd1fe7263edd018b0810..7c487fb9a34dc47f22a096481c9c97c89f24df89 100644 (file)
@@ -206,10 +206,6 @@ char *strnappend(const char *s, const char *suffix, size_t b) {
         return r;
 }
 
-char *strappend(const char *s, const char *suffix) {
-        return strnappend(s, suffix, strlen_ptr(suffix));
-}
-
 char *strjoin_real(const char *x, ...) {
         va_list ap;
         size_t l;
index ef136da49e02bfaf89c7d7d7195628a92b980467..76767afcac49a2e5ab89212f3a8b8fd81f1f2724 100644 (file)
@@ -108,7 +108,6 @@ const char* split(const char **state, size_t *l, const char *separator, SplitFla
 #define _FOREACH_WORD(word, length, s, separator, flags, state)         \
         for ((state) = (s), (word) = split(&(state), &(length), (separator), (flags)); (word); (word) = split(&(state), &(length), (separator), (flags)))
 
-char *strappend(const char *s, const char *suffix);
 char *strnappend(const char *s, const char *suffix, size_t length);
 
 char *strjoin_real(const char *x, ...) _sentinel_;
index 0a1adbf30bd7a0c3d5c801b196019c2105af90ba..2ae685f35a24751e9d21545c7aac126b8dee8b20 100644 (file)
@@ -233,7 +233,7 @@ int strv_extend_strv_concat(char ***a, char **b, const char *suffix) {
         STRV_FOREACH(s, b) {
                 char *v;
 
-                v = strappend(*s, suffix);
+                v = strjoin(*s, suffix);
                 if (!v)
                         return -ENOMEM;
 
index 245daabcf82f280ed97263abcd31e99d42859e88..70985ddecb802afbb8dc7c51ea3ed5d2438ffe84 100644 (file)
@@ -15,7 +15,7 @@ char *unit_dbus_path_from_name(const char *name) {
         if (!e)
                 return NULL;
 
-        return strappend("/org/freedesktop/systemd1/unit/", e);
+        return strjoin("/org/freedesktop/systemd1/unit/", e);
 }
 
 int unit_name_from_dbus_path(const char *path, char **name) {
@@ -102,7 +102,8 @@ static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
         [UNIT_INACTIVE] = "inactive",
         [UNIT_FAILED] = "failed",
         [UNIT_ACTIVATING] = "activating",
-        [UNIT_DEACTIVATING] = "deactivating"
+        [UNIT_DEACTIVATING] = "deactivating",
+        [UNIT_MAINTENANCE] = "maintenance",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
@@ -177,6 +178,7 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = {
         [SERVICE_FINAL_SIGKILL] = "final-sigkill",
         [SERVICE_FAILED] = "failed",
         [SERVICE_AUTO_RESTART] = "auto-restart",
+        [SERVICE_CLEANING] = "cleaning",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
index 85f3e42d30b9c652aa14c877c63ccec4a2d7a870..a7cdb97ad2bd8dc3d8a8694a54930cfa61eb0a07 100644 (file)
@@ -40,6 +40,7 @@ typedef enum UnitActiveState {
         UNIT_FAILED,
         UNIT_ACTIVATING,
         UNIT_DEACTIVATING,
+        UNIT_MAINTENANCE,
         _UNIT_ACTIVE_STATE_MAX,
         _UNIT_ACTIVE_STATE_INVALID = -1
 } UnitActiveState;
@@ -116,6 +117,7 @@ typedef enum ServiceState {
         SERVICE_FINAL_SIGKILL,
         SERVICE_FAILED,
         SERVICE_AUTO_RESTART,
+        SERVICE_CLEANING,
         _SERVICE_STATE_MAX,
         _SERVICE_STATE_INVALID = -1
 } ServiceState;
index 1b81fe268f212e8cb43e10f24e227b7236655a30..af873d0ffd752ce1399db42ba035ff4a252174ba 100644 (file)
@@ -402,7 +402,7 @@ int unit_name_path_escape(const char *f, char **ret) {
 }
 
 int unit_name_path_unescape(const char *f, char **ret) {
-        char *s;
+        _cleanup_free_ char *s = NULL;
         int r;
 
         assert(f);
@@ -415,34 +415,27 @@ int unit_name_path_unescape(const char *f, char **ret) {
                 if (!s)
                         return -ENOMEM;
         } else {
-                char *w;
+                _cleanup_free_ char *w = NULL;
 
                 r = unit_name_unescape(f, &w);
                 if (r < 0)
                         return r;
 
                 /* Don't accept trailing or leading slashes */
-                if (startswith(w, "/") || endswith(w, "/")) {
-                        free(w);
+                if (startswith(w, "/") || endswith(w, "/"))
                         return -EINVAL;
-                }
 
                 /* Prefix a slash again */
-                s = strappend("/", w);
-                free(w);
+                s = strjoin("/", w);
                 if (!s)
                         return -ENOMEM;
 
-                if (!path_is_normalized(s)) {
-                        free(s);
+                if (!path_is_normalized(s))
                         return -EINVAL;
-                }
         }
 
         if (ret)
-                *ret = s;
-        else
-                free(s);
+                *ret = TAKE_PTR(s);
 
         return 0;
 }
@@ -519,7 +512,7 @@ int unit_name_from_path(const char *path, const char *suffix, char **ret) {
         if (r < 0)
                 return r;
 
-        s = strappend(p, suffix);
+        s = strjoin(p, suffix);
         if (!s)
                 return -ENOMEM;
 
@@ -719,7 +712,7 @@ int slice_build_subslice(const char *slice, const char *name, char **ret) {
                 return -EINVAL;
 
         if (streq(slice, SPECIAL_ROOT_SLICE))
-                subslice = strappend(name, ".slice");
+                subslice = strjoin(name, ".slice");
         else {
                 char *e;
 
index 1dd8e11e9ee408054bcc171858f28c94d08b87ac..b8eb894f40e6ba3ae8995b7575d0962c7635c8f7 100644 (file)
@@ -15,6 +15,7 @@
 #include <utmp.h>
 
 #include "alloc-util.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "format-util.h"
@@ -213,7 +214,7 @@ int get_user_creds(
                 p = getpwnam(*username);
         }
         if (!p) {
-                r = errno > 0 ? -errno : -ESRCH;
+                r = errno_or_else(ESRCH);
 
                 /* If the user requested that we only synthesize as fallback, do so now */
                 if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
@@ -307,7 +308,7 @@ int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
         }
 
         if (!g)
-                return errno > 0 ? -errno : -ESRCH;
+                return errno_or_else(ESRCH);
 
         if (gid) {
                 if (!gid_is_valid(g->gr_gid))
@@ -492,7 +493,7 @@ int get_home_dir(char **_h) {
         errno = 0;
         p = getpwuid(u);
         if (!p)
-                return errno > 0 ? -errno : -ESRCH;
+                return errno_or_else(ESRCH);
 
         if (!path_is_valid(p->pw_dir) ||
             !path_is_absolute(p->pw_dir))
@@ -549,7 +550,7 @@ int get_shell(char **_s) {
         errno = 0;
         p = getpwuid(u);
         if (!p)
-                return errno > 0 ? -errno : -ESRCH;
+                return errno_or_else(ESRCH);
 
         if (!path_is_valid(p->pw_shell) ||
             !path_is_absolute(p->pw_shell))
@@ -770,7 +771,7 @@ int putpwent_sane(const struct passwd *pw, FILE *stream) {
 
         errno = 0;
         if (putpwent(pw, stream) != 0)
-                return errno > 0 ? -errno : -EIO;
+                return errno_or_else(EIO);
 
         return 0;
 }
@@ -781,7 +782,7 @@ int putspent_sane(const struct spwd *sp, FILE *stream) {
 
         errno = 0;
         if (putspent(sp, stream) != 0)
-                return errno > 0 ? -errno : -EIO;
+                return errno_or_else(EIO);
 
         return 0;
 }
@@ -792,7 +793,7 @@ int putgrent_sane(const struct group *gr, FILE *stream) {
 
         errno = 0;
         if (putgrent(gr, stream) != 0)
-                return errno > 0 ? -errno : -EIO;
+                return errno_or_else(EIO);
 
         return 0;
 }
@@ -804,7 +805,7 @@ int putsgent_sane(const struct sgrp *sg, FILE *stream) {
 
         errno = 0;
         if (putsgent(sg, stream) != 0)
-                return errno > 0 ? -errno : -EIO;
+                return errno_or_else(EIO);
 
         return 0;
 }
@@ -819,7 +820,7 @@ int fgetpwent_sane(FILE *stream, struct passwd **pw) {
         errno = 0;
         p = fgetpwent(stream);
         if (!p && errno != ENOENT)
-                return errno > 0 ? -errno : -EIO;
+                return errno_or_else(EIO);
 
         *pw = p;
         return !!p;
@@ -834,7 +835,7 @@ int fgetspent_sane(FILE *stream, struct spwd **sp) {
         errno = 0;
         s = fgetspent(stream);
         if (!s && errno != ENOENT)
-                return errno > 0 ? -errno : -EIO;
+                return errno_or_else(EIO);
 
         *sp = s;
         return !!s;
@@ -849,7 +850,7 @@ int fgetgrent_sane(FILE *stream, struct group **gr) {
         errno = 0;
         g = fgetgrent(stream);
         if (!g && errno != ENOENT)
-                return errno > 0 ? -errno : -EIO;
+                return errno_or_else(EIO);
 
         *gr = g;
         return !!g;
@@ -865,7 +866,7 @@ int fgetsgent_sane(FILE *stream, struct sgrp **sg) {
         errno = 0;
         s = fgetsgent(stream);
         if (!s && errno != ENOENT)
-                return errno > 0 ? -errno : -EIO;
+                return errno_or_else(EIO);
 
         *sg = s;
         return !!s;
index dda979cc42f818599d723fca1054bf1d653cd1b8..aa9d811f2e9d2769a2786bd3903405c93bc8d48f 100644 (file)
@@ -44,7 +44,7 @@ static int delete_rule(const char *rule) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Rule file name '%s' is not valid, refusing.", x + 1);
 
-        fn = strappend("/proc/sys/fs/binfmt_misc/", x+1);
+        fn = path_join("/proc/sys/fs/binfmt_misc", x+1);
         if (!fn)
                 return log_oom();
 
index f70e6c87ee977f9a3484b0688831119935f195c1..2f2313c5994441b3035189a8799eafcaa2b04348 100644 (file)
@@ -10,6 +10,7 @@
 #include "cgroup.h"
 #include "dbus-cgroup.h"
 #include "dbus-util.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "limits-util.h"
@@ -1492,7 +1493,7 @@ int bus_cgroup_set_property(
 
                                 errno = 0;
                                 if (!inet_ntop(item->family, &item->address, buffer, sizeof(buffer)))
-                                        return errno > 0 ? -errno : -EINVAL;
+                                        return errno_or_else(EINVAL);
 
                                 fprintf(f, "%s=%s/%u\n", name, buffer, item->prefixlen);
                         }
index cdd312b9ca8f08d2205dc0c6d3311f6e5b01c8e3..9fb3ed516a9784c6c01a94fb29d992cec4d61116 100644 (file)
@@ -634,6 +634,12 @@ static int method_kill_unit(sd_bus_message *message, void *userdata, sd_bus_erro
         return method_generic_unit_operation(message, userdata, error, bus_unit_method_kill, 0);
 }
 
+static int method_clean_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        /* Load the unit if necessary, in order to load it, and insist on the unit being loaded to be
+         * cleaned */
+        return method_generic_unit_operation(message, userdata, error, bus_unit_method_clean, GENERIC_UNIT_LOAD|GENERIC_UNIT_VALIDATE_LOADED);
+}
+
 static int method_reset_failed_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         /* Don't load the unit (because unloaded units can't be in failed state), and don't insist on the
          * unit to be loaded properly (since a failed unit might have its unit file disappeared) */
@@ -2473,6 +2479,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
         SD_BUS_METHOD("ReloadOrTryRestartUnit", "ss", "o", method_reload_or_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("EnqueueUnitJob", "sss", "uososa(uosos)", method_enqueue_unit_job, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("KillUnit", "ssi", NULL, method_kill_unit, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("CleanUnit", "sas", NULL, method_clean_unit, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("RefUnit", "s", NULL, method_ref_unit, SD_BUS_VTABLE_UNPRIVILEGED),
index a0699bee60b50d63486b1ebe97d039a9edf6c913..e0cc9a8c871a06d58e4a8a7694aa89aa9b292668 100644 (file)
@@ -105,6 +105,7 @@ const sd_bus_vtable bus_service_vtable[] = {
         SD_BUS_PROPERTY("TimeoutStartUSec", "t", bus_property_get_usec, offsetof(Service, timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Service, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("TimeoutAbortUSec", "t", property_get_timeout_abort_usec, 0, 0),
+        SD_BUS_PROPERTY("TimeoutCleanUSec", "t", bus_property_get_usec, offsetof(Service, timeout_clean_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0),
@@ -124,6 +125,7 @@ const sd_bus_vtable bus_service_vtable[] = {
         SD_BUS_PROPERTY("StatusErrno", "i", bus_property_get_int, offsetof(Service, status_errno), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("ReloadResult", "s", property_get_result, offsetof(Service, reload_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("CleanResult", "s", property_get_result, offsetof(Service, clean_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("USBFunctionDescriptors", "s", NULL, offsetof(Service, usb_function_descriptors), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("USBFunctionStrings", "s", NULL, offsetof(Service, usb_function_strings), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
index 0dac290bc16b49f016bd2d34cd8fc9241f539e48..53daa9c2ece8df277002f7bc222cde8042ad7ec7 100644 (file)
@@ -52,6 +52,42 @@ static BUS_DEFINE_PROPERTY_GET(property_get_can_isolate, "b", Unit, unit_can_iso
 static BUS_DEFINE_PROPERTY_GET(property_get_need_daemon_reload, "b", Unit, unit_need_daemon_reload);
 static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_strv, "as", 0);
 
+static int property_get_can_clean(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Unit *u = userdata;
+        ExecCleanMask mask;
+        int r;
+
+        assert(bus);
+        assert(reply);
+
+        r = unit_can_clean(u, &mask);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_open_container(reply, 'a', "s");
+        if (r < 0)
+                return r;
+
+        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
+                if (!FLAGS_SET(mask, 1U << t))
+                        continue;
+
+                r = sd_bus_message_append(reply, "s", exec_resource_type_to_string(t));
+                if (r < 0)
+                        return r;
+        }
+
+        return sd_bus_message_close_container(reply);
+}
+
 static int property_get_names(
                 sd_bus *bus,
                 const char *path,
@@ -617,6 +653,74 @@ int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error
         return sd_bus_reply_method_return(message, NULL);
 }
 
+int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        ExecCleanMask mask = 0;
+        Unit *u = userdata;
+        int r;
+
+        assert(message);
+        assert(u);
+
+        r = mac_selinux_unit_access_check(u, message, "stop", error);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_enter_container(message, 'a', "s");
+        if (r < 0)
+                return r;
+
+        for (;;) {
+                const char *i;
+
+                r = sd_bus_message_read(message, "s", &i);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                if (streq(i, "all"))
+                        mask |= EXEC_CLEAN_ALL;
+                else {
+                        ExecDirectoryType t;
+
+                        t = exec_resource_type_from_string(i);
+                        if (t < 0)
+                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid resource type: %s", i);
+
+                        mask |= 1U << t;
+                }
+        }
+
+        r = sd_bus_message_exit_container(message);
+        if (r < 0)
+                return r;
+
+        r = bus_verify_manage_units_async_full(
+                        u,
+                        "clean",
+                        CAP_DAC_OVERRIDE,
+                        N_("Authentication is required to delete files and directories associated with '$(unit)'."),
+                        true,
+                        message,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+        r = unit_clean(u, mask);
+        if (r == -EOPNOTSUPP)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit '%s' does not supporting cleaning.", u->id);
+        if (r == -EUNATCH)
+                return sd_bus_error_setf(error, BUS_ERROR_NOTHING_TO_CLEAN, "No matching resources found.");
+        if (r == -EBUSY)
+                return sd_bus_error_setf(error, BUS_ERROR_UNIT_BUSY, "Unit is not inactive or has pending job.");
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
 static int property_get_refs(
                 sd_bus *bus,
                 const char *path,
@@ -701,6 +805,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_PROPERTY("CanStop", "b", property_get_can_stop, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("CanReload", "b", property_get_can_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("CanIsolate", "b", property_get_can_isolate, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("CanClean", "as", property_get_can_clean, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Job", "(uo)", property_get_job, offsetof(Unit, job), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("StopWhenUnneeded", "b", bus_property_get_bool, offsetof(Unit, stop_when_unneeded), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RefuseManualStart", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_start), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -748,6 +853,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Ref", NULL, NULL, bus_unit_method_ref, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Unref", NULL, NULL, bus_unit_method_unref, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Clean", "as", NULL, bus_unit_method_clean, SD_BUS_VTABLE_UNPRIVILEGED),
 
         /* For dependency types we don't support anymore always return an empty array */
         SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_empty_strv, 0, SD_BUS_VTABLE_HIDDEN),
index 740bb1c86bcbc16c929b8776f1f23afcaa98cb4d..24abf3c088e9226f2bdb9a37abc5b9bc29bb2292 100644 (file)
@@ -25,6 +25,7 @@ int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bu
 int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error *error);
 
 typedef enum BusUnitQueueFlags {
         BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE = 1 << 0,
index c380bee9e5c53bbf4ff60d5904ace84d3dd38288..fb0a7943487c486889dbb04be6b064ade15ba593 100644 (file)
@@ -494,7 +494,7 @@ static int dynamic_user_realize(
                 errno = 0;
                 p = getpwuid(num);
                 if (!p)
-                        return errno > 0 ? -errno : -ESRCH;
+                        return errno_or_else(ESRCH);
 
                 gid = p->pw_gid;
         }
index 1cbb2a83de45132a8d7a1fbeac2bfd19e0bbc879..9c086103aec724841a33d6078ae6174f20cc8869 100644 (file)
@@ -999,12 +999,8 @@ static int get_supplementary_groups(const ExecContext *c, const char *user,
          */
         errno = 0;
         ngroups_max = (int) sysconf(_SC_NGROUPS_MAX);
-        if (ngroups_max <= 0) {
-                if (errno > 0)
-                        return -errno;
-                else
-                        return -EOPNOTSUPP; /* For all other values */
-        }
+        if (ngroups_max <= 0)
+                return errno_or_else(EOPNOTSUPP);
 
         l_gids = new(gid_t, ngroups_max);
         if (!l_gids)
@@ -1704,7 +1700,7 @@ static int build_environment(
         }
 
         if (home) {
-                x = strappend("HOME=", home);
+                x = strjoin("HOME=", home);
                 if (!x)
                         return -ENOMEM;
 
@@ -1713,19 +1709,19 @@ static int build_environment(
         }
 
         if (username) {
-                x = strappend("LOGNAME=", username);
+                x = strjoin("LOGNAME=", username);
                 if (!x)
                         return -ENOMEM;
                 our_env[n_env++] = x;
 
-                x = strappend("USER=", username);
+                x = strjoin("USER=", username);
                 if (!x)
                         return -ENOMEM;
                 our_env[n_env++] = x;
         }
 
         if (shell) {
-                x = strappend("SHELL=", shell);
+                x = strjoin("SHELL=", shell);
                 if (!x)
                         return -ENOMEM;
 
@@ -1754,7 +1750,7 @@ static int build_environment(
                 if (!term)
                         term = default_term_for_tty(tty_path);
 
-                x = strappend("TERM=", term);
+                x = strjoin("TERM=", term);
                 if (!x)
                         return -ENOMEM;
                 our_env[n_env++] = x;
@@ -4774,6 +4770,60 @@ void exec_context_revert_tty(ExecContext *c) {
         }
 }
 
+int exec_context_get_clean_directories(
+                ExecContext *c,
+                char **prefix,
+                ExecCleanMask mask,
+                char ***ret) {
+
+        _cleanup_strv_free_ char **l = NULL;
+        ExecDirectoryType t;
+        int r;
+
+        assert(c);
+        assert(prefix);
+        assert(ret);
+
+        for (t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
+                char **i;
+
+                if (!FLAGS_SET(mask, 1U << t))
+                        continue;
+
+                if (!prefix[t])
+                        continue;
+
+                STRV_FOREACH(i, c->directories[t].paths) {
+                        char *j;
+
+                        j = path_join(prefix[t], *i);
+                        if (!j)
+                                return -ENOMEM;
+
+                        r = strv_consume(&l, j);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        *ret = TAKE_PTR(l);
+        return 0;
+}
+
+int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
+        ExecCleanMask mask = 0;
+
+        assert(c);
+        assert(ret);
+
+        for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
+                if (!strv_isempty(c->directories[t].paths))
+                        mask |= 1U << t;
+
+        *ret = mask;
+        return 0;
+}
+
 void exec_status_start(ExecStatus *s, pid_t pid) {
         assert(s);
 
@@ -5449,6 +5499,7 @@ static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
 
+/* This table maps ExecDirectoryType to the setting it is configured with in the unit */
 static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
         [EXEC_DIRECTORY_RUNTIME] = "RuntimeDirectory",
         [EXEC_DIRECTORY_STATE] = "StateDirectory",
@@ -5459,6 +5510,21 @@ static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType);
 
+/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
+ * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
+ * directories, specifically .timer units with their timestamp touch file. */
+static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
+        [EXEC_DIRECTORY_RUNTIME] = "runtime",
+        [EXEC_DIRECTORY_STATE] = "state",
+        [EXEC_DIRECTORY_CACHE] = "cache",
+        [EXEC_DIRECTORY_LOGS] = "logs",
+        [EXEC_DIRECTORY_CONFIGURATION] = "configuration",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
+
+/* And this table also maps ExecDirectoryType, to the environment variable we pass the selected directory to
+ * the service payload in. */
 static const char* const exec_directory_env_name_table[_EXEC_DIRECTORY_TYPE_MAX] = {
         [EXEC_DIRECTORY_RUNTIME] = "RUNTIME_DIRECTORY",
         [EXEC_DIRECTORY_STATE] = "STATE_DIRECTORY",
index 609e15fc07383cba8fcfa48eaa59d007de4f61f0..0d5398acfae473686251c6bc1b612e2dea6ee3cb 100644 (file)
@@ -132,6 +132,19 @@ typedef struct ExecDirectory {
         mode_t mode;
 } ExecDirectory;
 
+typedef enum ExecCleanMask {
+        /* In case you wonder why the bitmask below doesn't use "directory" in its name: we want to keep this
+         * generic so that .timer timestamp files can nicely be covered by this too, and similar. */
+        EXEC_CLEAN_RUNTIME       = 1U << EXEC_DIRECTORY_RUNTIME,
+        EXEC_CLEAN_STATE         = 1U << EXEC_DIRECTORY_STATE,
+        EXEC_CLEAN_CACHE         = 1U << EXEC_DIRECTORY_CACHE,
+        EXEC_CLEAN_LOGS          = 1U << EXEC_DIRECTORY_LOGS,
+        EXEC_CLEAN_CONFIGURATION = 1U << EXEC_DIRECTORY_CONFIGURATION,
+        EXEC_CLEAN_NONE          = 0,
+        EXEC_CLEAN_ALL           = (1U << _EXEC_DIRECTORY_TYPE_MAX) - 1,
+        _EXEC_CLEAN_MASK_INVALID = -1,
+} ExecCleanMask;
+
 /* Encodes configuration parameters applied to invoked commands. Does not carry runtime data, but only configuration
  * changes sourced from unit files and suchlike. ExecContext objects are usually embedded into Unit objects, and do not
  * change after being loaded. */
@@ -369,6 +382,9 @@ void exec_context_free_log_extra_fields(ExecContext *c);
 
 void exec_context_revert_tty(ExecContext *c);
 
+int exec_context_get_clean_directories(ExecContext *c, char **prefix, ExecCleanMask mask, char ***ret);
+int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret);
+
 void exec_status_start(ExecStatus *s, pid_t pid);
 void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status);
 void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix);
@@ -401,3 +417,6 @@ ExecKeyringMode exec_keyring_mode_from_string(const char *s) _pure_;
 
 const char* exec_directory_type_to_string(ExecDirectoryType i) _const_;
 ExecDirectoryType exec_directory_type_from_string(const char *s) _pure_;
+
+const char* exec_resource_type_to_string(ExecDirectoryType i) _const_;
+ExecDirectoryType exec_resource_type_from_string(const char *s) _pure_;
index c69ccc221d1f5d5a0e67e2388c7bfc9e87298e33..90c829d08d26b5b4a34564eb5cae4d16bcd38f07 100644 (file)
@@ -477,36 +477,14 @@ static bool job_is_runnable(Job *j) {
         if (j->type == JOB_NOP)
                 return true;
 
-        if (IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD)) {
-                /* Immediate result is that the job is or might be
-                 * started. In this case let's wait for the
-                 * dependencies, regardless whether they are
-                 * starting or stopping something. */
-
-                HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i)
-                        if (other->job)
-                                return false;
-        }
-
-        /* Also, if something else is being stopped and we should
-         * change state after it, then let's wait. */
+        HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i)
+                if (other->job && job_compare(j, other->job, UNIT_AFTER) > 0)
+                        return false;
 
         HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i)
-                if (other->job &&
-                    IN_SET(other->job->type, JOB_STOP, JOB_RESTART))
+                if (other->job && job_compare(j, other->job, UNIT_BEFORE) > 0)
                         return false;
 
-        /* This means that for a service a and a service b where b
-         * shall be started after a:
-         *
-         *  start a + start b â†’ 1st step start a, 2nd step start b
-         *  start a + stop b  â†’ 1st step stop b,  2nd step start a
-         *  stop a  + start b â†’ 1st step stop a,  2nd step start b
-         *  stop a  + stop b  â†’ 1st step stop b,  2nd step stop a
-         *
-         *  This has the side effect that restarts are properly
-         *  synchronized too. */
-
         return true;
 }
 
@@ -1455,46 +1433,14 @@ bool job_may_gc(Job *j) {
         if (j->type == JOB_NOP)
                 return false;
 
-        /* If a job is ordered after ours, and is to be started, then it needs to wait for us, regardless if we stop or
-         * start, hence let's not GC in that case. */
-        HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i) {
-                if (!other->job)
-                        continue;
-
-                if (other->job->ignore_order)
-                        continue;
-
-                if (IN_SET(other->job->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD))
+        /* The logic is inverse to job_is_runnable, we cannot GC as long as we block any job. */
+        HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i)
+                if (other->job && job_compare(j, other->job, UNIT_BEFORE) < 0)
                         return false;
-        }
-
-        /* If we are going down, but something else is ordered After= us, then it needs to wait for us */
-        if (IN_SET(j->type, JOB_STOP, JOB_RESTART))
-                HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
-                        if (!other->job)
-                                continue;
-
-                        if (other->job->ignore_order)
-                                continue;
 
+        HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i)
+                if (other->job && job_compare(j, other->job, UNIT_AFTER) < 0)
                         return false;
-                }
-
-        /* The logic above is kinda the inverse of the job_is_runnable() logic. Specifically, if the job "we" is
-         * ordered before the job "other":
-         *
-         *  we start + other start â†’ stay
-         *  we start + other stop  â†’ gc
-         *  we stop  + other start â†’ stay
-         *  we stop  + other stop  â†’ gc
-         *
-         * "we" are ordered after "other":
-         *
-         *  we start + other start â†’ gc
-         *  we start + other stop  â†’ gc
-         *  we stop  + other start â†’ stay
-         *  we stop  + other stop  â†’ stay
-         */
 
         return true;
 }
@@ -1512,7 +1458,7 @@ void job_add_to_gc_queue(Job *j) {
         j->in_gc_queue = true;
 }
 
-static int job_compare(Job * const *a, Job * const *b) {
+static int job_compare_id(Job * const *a, Job * const *b) {
         return CMP((*a)->id, (*b)->id);
 }
 
@@ -1521,7 +1467,7 @@ static size_t sort_job_list(Job **list, size_t n) {
         size_t a, b;
 
         /* Order by numeric IDs */
-        typesafe_qsort(list, n, job_compare);
+        typesafe_qsort(list, n, job_compare_id);
 
         /* Filter out duplicates */
         for (a = 0, b = 0; a < n; a++) {
@@ -1552,23 +1498,21 @@ int job_get_before(Job *j, Job*** ret) {
                 return 0;
         }
 
-        if (IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD)) {
-
-                HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
-                        if (!other->job)
-                                continue;
+        HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
+                if (!other->job)
+                        continue;
+                if (job_compare(j, other->job, UNIT_AFTER) <= 0)
+                        continue;
 
-                        if (!GREEDY_REALLOC(list, n_allocated, n+1))
-                                return -ENOMEM;
-                        list[n++] = other->job;
-                }
+                if (!GREEDY_REALLOC(list, n_allocated, n+1))
+                        return -ENOMEM;
+                list[n++] = other->job;
         }
 
         HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i) {
                 if (!other->job)
                         continue;
-
-                if (!IN_SET(other->job->type, JOB_STOP, JOB_RESTART))
+                if (job_compare(j, other->job, UNIT_BEFORE) <= 0)
                         continue;
 
                 if (!GREEDY_REALLOC(list, n_allocated, n+1))
@@ -1602,7 +1546,7 @@ int job_get_after(Job *j, Job*** ret) {
                 if (other->job->ignore_order)
                         continue;
 
-                if (!IN_SET(other->job->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD))
+                if (job_compare(j, other->job, UNIT_BEFORE) >= 0)
                         continue;
 
                 if (!GREEDY_REALLOC(list, n_allocated, n+1))
@@ -1610,19 +1554,20 @@ int job_get_after(Job *j, Job*** ret) {
                 list[n++] = other->job;
         }
 
-        if (IN_SET(j->type, JOB_STOP, JOB_RESTART)) {
 
-                HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
-                        if (!other->job)
-                                continue;
+        HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
+                if (!other->job)
+                        continue;
+
+                if (other->job->ignore_order)
+                        continue;
 
-                        if (other->job->ignore_order)
-                                continue;
+                if (job_compare(j, other->job, UNIT_AFTER) >= 0)
+                        continue;
 
-                        if (!GREEDY_REALLOC(list, n_allocated, n+1))
-                                return -ENOMEM;
-                        list[n++] = other->job;
-                }
+                if (!GREEDY_REALLOC(list, n_allocated, n+1))
+                        return -ENOMEM;
+                list[n++] = other->job;
         }
 
         n = sort_job_list(list, n);
@@ -1692,3 +1637,45 @@ const char* job_type_to_access_method(JobType t) {
         else
                 return "reload";
 }
+
+/*
+ * assume_dep   assumed dependency between units (a is before/after b)
+ *
+ * Returns
+ *    0         jobs are independent,
+ *   >0         a should run after b,
+ *   <0         a should run before b,
+ *
+ * The logic means that for a service a and a service b where b.After=a:
+ *
+ *  start a + start b â†’ 1st step start a, 2nd step start b
+ *  start a + stop b  â†’ 1st step stop b,  2nd step start a
+ *  stop a  + start b â†’ 1st step stop a,  2nd step start b
+ *  stop a  + stop b  â†’ 1st step stop b,  2nd step stop a
+ *
+ *  This has the side effect that restarts are properly
+ *  synchronized too.
+ */
+int job_compare(Job *a, Job *b, UnitDependency assume_dep) {
+        assert(a->type < _JOB_TYPE_MAX_IN_TRANSACTION);
+        assert(b->type < _JOB_TYPE_MAX_IN_TRANSACTION);
+        assert(IN_SET(assume_dep, UNIT_AFTER, UNIT_BEFORE));
+
+        /* Trivial cases first */
+        if (a->type == JOB_NOP || b->type == JOB_NOP)
+                return 0;
+
+        if (a->ignore_order || b->ignore_order)
+                return 0;
+
+        if (assume_dep == UNIT_AFTER)
+                return -job_compare(b, a, UNIT_BEFORE);
+
+        /* Let's make it simple, JOB_STOP goes always first (in case both ua and ub stop,
+         * then ub's stop goes first anyway).
+         * JOB_RESTART is JOB_STOP in disguise (before it is patched to JOB_START). */
+        if (IN_SET(b->type, JOB_STOP, JOB_RESTART))
+                return 1;
+        else
+                return -1;
+}
index 0f15cbf821cb791cf26c1356c69b80096f1db148..9c79c873d157ec1c3ad2019194b9f81fa5c139cb 100644 (file)
@@ -237,3 +237,5 @@ const char* job_result_to_string(JobResult t) _const_;
 JobResult job_result_from_string(const char *s) _pure_;
 
 const char* job_type_to_access_method(JobType t);
+
+int job_compare(Job *a, Job *b, UnitDependency assume_dep);
index 19ee56662c5626a69049e070dc4794ab703d91cf..76c50166b6479f0200675912997107f2ab4ad1d5 100644 (file)
@@ -315,6 +315,7 @@ Service.TimeoutSec,              config_parse_service_timeout,       0,
 Service.TimeoutStartSec,         config_parse_service_timeout,       0,                             0
 Service.TimeoutStopSec,          config_parse_sec_fix_0,             0,                             offsetof(Service, timeout_stop_usec)
 Service.TimeoutAbortSec,         config_parse_service_timeout_abort, 0,                             0
+Service.TimeoutCleanSec,         config_parse_sec,                   0,                             offsetof(Service, timeout_clean_usec)
 Service.RuntimeMaxSec,           config_parse_sec,                   0,                             offsetof(Service, runtime_max_usec)
 Service.WatchdogSec,             config_parse_sec,                   0,                             offsetof(Service, watchdog_usec)
 m4_dnl The following five only exist for compatibility, they moved into Unit, see above
index 09507648fb1a2e88b2f2c9e59f84dbde7e510110..3d3c3b0cbac6570ccc71afced59558ce37d0e3c1 100644 (file)
@@ -893,7 +893,7 @@ static int manager_setup_notify(Manager *m) {
 
                 fd_inc_rcvbuf(fd, NOTIFY_RCVBUF_SIZE);
 
-                m->notify_socket = strappend(m->prefix[EXEC_DIRECTORY_RUNTIME], "/systemd/notify");
+                m->notify_socket = path_join(m->prefix[EXEC_DIRECTORY_RUNTIME], "systemd/notify");
                 if (!m->notify_socket)
                         return log_oom();
 
index b97285e556c3d3867399791059c74ab0a8715328..ea7b0a80cbd379a081f229351ee04ca5fa544ccb 100644 (file)
@@ -332,7 +332,7 @@ int mount_cgroup_controllers(void) {
                 if (!options)
                         options = TAKE_PTR(controller);
 
-                where = strappend("/sys/fs/cgroup/", options);
+                where = path_join("/sys/fs/cgroup", options);
                 if (!where)
                         return log_oom();
 
index a7031df48a674086caab96835220896773af01e9..f72d6d9cbeb70108b73682210641d5bd1e43ab9e 100644 (file)
@@ -30,6 +30,7 @@
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
+#include "rm-rf.h"
 #include "serialize.h"
 #include "service.h"
 #include "signal-util.h"
@@ -59,7 +60,8 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
         [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
         [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
         [SERVICE_FAILED] = UNIT_FAILED,
-        [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING
+        [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
+        [SERVICE_CLEANING] = UNIT_MAINTENANCE,
 };
 
 /* For Type=idle we never want to delay any other jobs, hence we
@@ -80,7 +82,8 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
         [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
         [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
         [SERVICE_FAILED] = UNIT_FAILED,
-        [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING
+        [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
+        [SERVICE_CLEANING] = UNIT_MAINTENANCE,
 };
 
 static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
@@ -103,6 +106,7 @@ static void service_init(Unit *u) {
         s->timeout_abort_set = u->manager->default_timeout_abort_set;
         s->restart_usec = u->manager->default_restart_usec;
         s->runtime_max_usec = USEC_INFINITY;
+        s->timeout_clean_usec = USEC_INFINITY;
         s->type = _SERVICE_TYPE_INVALID;
         s->socket_fd = -1;
         s->stdin_fd = s->stdout_fd = s->stderr_fd = -1;
@@ -787,8 +791,9 @@ static int service_load(Unit *u) {
 }
 
 static void service_dump(Unit *u, FILE *f, const char *prefix) {
-        char buf_restart[FORMAT_TIMESPAN_MAX], buf_start[FORMAT_TIMESPAN_MAX], buf_stop[FORMAT_TIMESPAN_MAX];
-        char buf_runtime[FORMAT_TIMESPAN_MAX], buf_watchdog[FORMAT_TIMESPAN_MAX], buf_abort[FORMAT_TIMESPAN_MAX];
+        char buf_restart[FORMAT_TIMESPAN_MAX], buf_start[FORMAT_TIMESPAN_MAX], buf_stop[FORMAT_TIMESPAN_MAX],
+                buf_runtime[FORMAT_TIMESPAN_MAX], buf_watchdog[FORMAT_TIMESPAN_MAX], buf_abort[FORMAT_TIMESPAN_MAX],
+                buf_clean[FORMAT_TIMESPAN_MAX];
         ServiceExecCommand c;
         Service *s = SERVICE(u);
         const char *prefix2;
@@ -802,6 +807,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
                 "%sService State: %s\n"
                 "%sResult: %s\n"
                 "%sReload Result: %s\n"
+                "%sClean Result: %s\n"
                 "%sPermissionsStartOnly: %s\n"
                 "%sRootDirectoryStartOnly: %s\n"
                 "%sRemainAfterExit: %s\n"
@@ -814,6 +820,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
                 prefix, service_state_to_string(s->state),
                 prefix, service_result_to_string(s->result),
                 prefix, service_result_to_string(s->reload_result),
+                prefix, service_result_to_string(s->clean_result),
                 prefix, yes_no(s->permissions_start_only),
                 prefix, yes_no(s->root_directory_start_only),
                 prefix, yes_no(s->remain_after_exit),
@@ -869,8 +876,10 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
                         prefix, format_timespan(buf_abort, sizeof(buf_abort), s->timeout_abort_usec, USEC_PER_SEC));
 
         fprintf(f,
+                "%sTimeoutCleanSec: %s\n"
                 "%sRuntimeMaxSec: %s\n"
                 "%sWatchdogSec: %s\n",
+                prefix, format_timespan(buf_clean, sizeof(buf_clean), s->timeout_clean_usec, USEC_PER_SEC),
                 prefix, format_timespan(buf_runtime, sizeof(buf_runtime), s->runtime_max_usec, USEC_PER_SEC),
                 prefix, format_timespan(buf_watchdog, sizeof(buf_watchdog), s->watchdog_usec, USEC_PER_SEC));
 
@@ -1069,7 +1078,8 @@ static void service_set_state(Service *s, ServiceState state) {
                     SERVICE_RELOAD,
                     SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
                     SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
-                    SERVICE_AUTO_RESTART))
+                    SERVICE_AUTO_RESTART,
+                    SERVICE_CLEANING))
                 s->timer_event_source = sd_event_source_unref(s->timer_event_source);
 
         if (!IN_SET(state,
@@ -1085,7 +1095,8 @@ static void service_set_state(Service *s, ServiceState state) {
                     SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
                     SERVICE_RELOAD,
                     SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
-                    SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
+                    SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
+                    SERVICE_CLEANING)) {
                 service_unwatch_control_pid(s);
                 s->control_command = NULL;
                 s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
@@ -1151,6 +1162,9 @@ static usec_t service_coldplug_timeout(Service *s) {
         case SERVICE_AUTO_RESTART:
                 return usec_add(UNIT(s)->inactive_enter_timestamp.monotonic, s->restart_usec);
 
+        case SERVICE_CLEANING:
+                return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_clean_usec);
+
         default:
                 return USEC_INFINITY;
         }
@@ -1188,13 +1202,14 @@ static int service_coldplug(Unit *u) {
                    SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
                    SERVICE_RELOAD,
                    SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
-                   SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
+                   SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
+                   SERVICE_CLEANING)) {
                 r = unit_watch_pid(UNIT(s), s->control_pid, false);
                 if (r < 0)
                         return r;
         }
 
-        if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) {
+        if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART, SERVICE_CLEANING)) {
                 (void) unit_enqueue_rewatch_pids(u);
                 (void) unit_setup_dynamic_creds(u);
                 (void) unit_setup_exec_runtime(u);
@@ -1517,7 +1532,7 @@ static int service_spawn(
                         if (r < 0)
                                 return r;
 
-                        t = strappend("REMOTE_ADDR=", addr);
+                        t = strjoin("REMOTE_ADDR=", addr);
                         if (!t)
                                 return -ENOMEM;
                         our_env[n_env++] = t;
@@ -2368,7 +2383,7 @@ static int service_start(Unit *u) {
          * please! */
         if (IN_SET(s->state,
                    SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
-                   SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))
+                   SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, SERVICE_CLEANING))
                 return -EAGAIN;
 
         /* Already on it! */
@@ -2455,6 +2470,12 @@ static int service_stop(Unit *u) {
                 return 0;
         }
 
+        /* If we are currently cleaning, then abort it, brutally. */
+        if (s->state == SERVICE_CLEANING) {
+                service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_SUCCESS);
+                return 0;
+        }
+
         assert(IN_SET(s->state, SERVICE_RUNNING, SERVICE_EXITED));
 
         service_enter_stop(s, SERVICE_SUCCESS);
@@ -3563,6 +3584,14 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                         service_enter_dead(s, f, true);
                                 break;
 
+                        case SERVICE_CLEANING:
+
+                                if (s->clean_result == SERVICE_SUCCESS)
+                                        s->clean_result = f;
+
+                                service_enter_dead(s, SERVICE_SUCCESS, false);
+                                break;
+
                         default:
                                 assert_not_reached("Uh, control process died at wrong time.");
                         }
@@ -3679,6 +3708,15 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
                 service_enter_restart(s);
                 break;
 
+        case SERVICE_CLEANING:
+                log_unit_warning(UNIT(s), "Cleaning timed out. killing.");
+
+                if (s->clean_result == SERVICE_SUCCESS)
+                        s->clean_result = SERVICE_FAILURE_TIMEOUT;
+
+                service_enter_signal(s, SERVICE_FINAL_SIGKILL, 0);
+                break;
+
         default:
                 assert_not_reached("Timeout at wrong time.");
         }
@@ -4086,6 +4124,7 @@ static void service_reset_failed(Unit *u) {
 
         s->result = SERVICE_SUCCESS;
         s->reload_result = SERVICE_SUCCESS;
+        s->clean_result = SERVICE_SUCCESS;
         s->n_restarts = 0;
         s->flush_n_restarts = false;
 }
@@ -4155,6 +4194,77 @@ static int service_exit_status(Unit *u) {
         return s->main_exec_status.status;
 }
 
+static int service_clean(Unit *u, ExecCleanMask mask) {
+        _cleanup_strv_free_ char **l = NULL;
+        Service *s = SERVICE(u);
+        pid_t pid;
+        int r;
+
+        assert(s);
+        assert(mask != 0);
+
+        if (s->state != SERVICE_DEAD)
+                return -EBUSY;
+
+        r = exec_context_get_clean_directories(&s->exec_context, u->manager->prefix, mask, &l);
+        if (r < 0)
+                return r;
+
+        if (strv_isempty(l))
+                return -EUNATCH;
+
+        service_unwatch_control_pid(s);
+        s->clean_result = SERVICE_SUCCESS;
+        s->control_command = NULL;
+        s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
+
+        r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_clean_usec));
+        if (r < 0)
+                goto fail;
+
+        r = unit_fork_helper_process(UNIT(s), "(sd-rmrf)", &pid);
+        if (r < 0)
+                goto fail;
+        if (r == 0) {
+                int ret = EXIT_SUCCESS;
+                char **i;
+
+                STRV_FOREACH(i, l) {
+                        r = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_MISSING_OK);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to remove '%s': %m", *i);
+                                ret = EXIT_FAILURE;
+                        }
+                }
+
+                _exit(ret);
+        }
+
+        r = unit_watch_pid(u, pid, true);
+        if (r < 0)
+                goto fail;
+
+        s->control_pid = pid;
+
+        service_set_state(s, SERVICE_CLEANING);
+
+        return 0;
+
+fail:
+        log_unit_warning_errno(UNIT(s), r, "Failed to initiate cleaning: %m");
+        s->clean_result = SERVICE_FAILURE_RESOURCES;
+        s->timer_event_source = sd_event_source_unref(s->timer_event_source);
+        return r;
+}
+
+static int service_can_clean(Unit *u, ExecCleanMask *ret) {
+        Service *s = SERVICE(u);
+
+        assert(s);
+
+        return exec_context_get_clean_mask(&s->exec_context, ret);
+}
+
 static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
         [SERVICE_RESTART_NO] = "no",
         [SERVICE_RESTART_ON_SUCCESS] = "on-success",
@@ -4255,6 +4365,8 @@ const UnitVTable service_vtable = {
         .can_reload = service_can_reload,
 
         .kill = service_kill,
+        .clean = service_clean,
+        .can_clean = service_can_clean,
 
         .serialize = service_serialize,
         .deserialize_item = service_deserialize_item,
index d6182dbaa0295016aeff0cd169714ccedba7281e..de56728c224ab51f302e470a44c7ab850c6a6617 100644 (file)
@@ -99,6 +99,7 @@ struct Service {
         usec_t timeout_stop_usec;
         usec_t timeout_abort_usec;
         bool timeout_abort_set;
+        usec_t timeout_clean_usec;
         usec_t runtime_max_usec;
 
         dual_timestamp watchdog_timestamp;
@@ -147,6 +148,7 @@ struct Service {
         /* If we shut down, remember why */
         ServiceResult result;
         ServiceResult reload_result;
+        ServiceResult clean_result;
 
         bool main_pid_known:1;
         bool main_pid_alien:1;
index ba74dc7aea3ab06e3f17f9de0490241da0027c3a..7d816856b19d70266675078440e3ec3e17c532f9 100644 (file)
@@ -146,7 +146,7 @@ static int timer_setup_persistent(Timer *t) {
                 if (r < 0)
                         return r;
 
-                t->stamp_path = strappend("/var/lib/systemd/timers/stamp-", UNIT(t)->id);
+                t->stamp_path = strjoin("/var/lib/systemd/timers/stamp-", UNIT(t)->id);
         } else {
                 const char *e;
 
@@ -833,6 +833,41 @@ static void timer_timezone_change(Unit *u) {
         }
 }
 
+static int timer_clean(Unit *u, ExecCleanMask mask) {
+        Timer *t = TIMER(u);
+        int r;
+
+        assert(t);
+        assert(mask != 0);
+
+        if (t->state != TIMER_DEAD)
+                return -EBUSY;
+
+        if (!IN_SET(mask, EXEC_CLEAN_STATE))
+                return -EUNATCH;
+
+        r = timer_setup_persistent(t);
+        if (r < 0)
+                return r;
+
+        if (!t->stamp_path)
+                return -EUNATCH;
+
+        if (unlink(t->stamp_path) && errno != ENOENT)
+                return log_unit_error_errno(u, errno, "Failed to clean stamp file of timer: %m");
+
+        return 0;
+}
+
+static int timer_can_clean(Unit *u, ExecCleanMask *ret) {
+        Timer *t = TIMER(u);
+
+        assert(t);
+
+        *ret = t->persistent ? EXEC_CLEAN_STATE : 0;
+        return 0;
+}
+
 static const char* const timer_base_table[_TIMER_BASE_MAX] = {
         [TIMER_ACTIVE] = "OnActiveSec",
         [TIMER_BOOT] = "OnBootSec",
@@ -872,6 +907,9 @@ const UnitVTable timer_vtable = {
         .start = timer_start,
         .stop = timer_stop,
 
+        .clean = timer_clean,
+        .can_clean = timer_can_clean,
+
         .serialize = timer_serialize,
         .deserialize_item = timer_deserialize_item,
 
index 3b6b240d361f70e5b0cfcf8701cfada3b3da4592..d1bf2e64c915df861469655e396b0e6636c4121f 100644 (file)
@@ -353,6 +353,11 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
         Unit *u;
         void *v;
         int r;
+        static const UnitDependency directions[] = {
+                UNIT_BEFORE,
+                UNIT_AFTER,
+        };
+        size_t d;
 
         assert(tr);
         assert(j);
@@ -441,25 +446,33 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
         j->marker = from ? from : j;
         j->generation = generation;
 
-        /* We assume that the dependencies are bidirectional, and
-         * hence can ignore UNIT_AFTER */
-        HASHMAP_FOREACH_KEY(v, u, j->unit->dependencies[UNIT_BEFORE], i) {
-                Job *o;
-
-                /* Is there a job for this unit? */
-                o = hashmap_get(tr->jobs, u);
-                if (!o) {
-                        /* Ok, there is no job for this in the
-                         * transaction, but maybe there is already one
-                         * running? */
-                        o = u->job;
-                        if (!o)
+        /* Actual ordering of jobs depends on the unit ordering dependency and job types. We need to traverse
+         * the graph over 'before' edges in the actual job execution order. We traverse over both unit
+         * ordering dependencies and we test with job_compare() whether it is the 'before' edge in the job
+         * execution ordering. */
+        for (d = 0; d < ELEMENTSOF(directions); d++) {
+                HASHMAP_FOREACH_KEY(v, u, j->unit->dependencies[directions[d]], i) {
+                        Job *o;
+
+                        /* Is there a job for this unit? */
+                        o = hashmap_get(tr->jobs, u);
+                        if (!o) {
+                                /* Ok, there is no job for this in the
+                                 * transaction, but maybe there is already one
+                                 * running? */
+                                o = u->job;
+                                if (!o)
+                                        continue;
+                        }
+
+                        /* Cut traversing if the job j is not really *before* o. */
+                        if (job_compare(j, o, directions[d]) >= 0)
                                 continue;
-                }
 
-                r = transaction_verify_order_one(tr, o, j, generation, e);
-                if (r < 0)
-                        return r;
+                        r = transaction_verify_order_one(tr, o, j, generation, e);
+                        if (r < 0)
+                                return r;
+                }
         }
 
         /* Ok, let's backtrack, and remember that this entry is not on
index a6a95f05204bbb78ea998a6cdfdf6b434ba95621..9b913ec74ee6fab0dc8390870940eb14f39890fd 100644 (file)
@@ -1753,6 +1753,8 @@ int unit_start(Unit *u) {
         state = unit_active_state(u);
         if (UNIT_IS_ACTIVE_OR_RELOADING(state))
                 return -EALREADY;
+        if (state == UNIT_MAINTENANCE)
+                return -EAGAIN;
 
         /* Units that aren't loaded cannot be started */
         if (u->load_state != UNIT_LOADED)
@@ -5753,6 +5755,53 @@ int unit_test_trigger_loaded(Unit *u) {
         return 0;
 }
 
+int unit_clean(Unit *u, ExecCleanMask mask) {
+        UnitActiveState state;
+
+        assert(u);
+
+        /* Special return values:
+         *
+         *   -EOPNOTSUPP â†’ cleaning not supported for this unit type
+         *   -EUNATCH    â†’ cleaning not defined for this resource type
+         *   -EBUSY      â†’ unit currently can't be cleaned since it's running or not properly loaded, or has
+         *                 a job queued or similar
+         */
+
+        if (!UNIT_VTABLE(u)->clean)
+                return -EOPNOTSUPP;
+
+        if (mask == 0)
+                return -EUNATCH;
+
+        if (u->load_state != UNIT_LOADED)
+                return -EBUSY;
+
+        if (u->job)
+                return -EBUSY;
+
+        state = unit_active_state(u);
+        if (!IN_SET(state, UNIT_INACTIVE))
+                return -EBUSY;
+
+        return UNIT_VTABLE(u)->clean(u, mask);
+}
+
+int unit_can_clean(Unit *u, ExecCleanMask *ret) {
+        assert(u);
+
+        if (!UNIT_VTABLE(u)->clean ||
+            u->load_state != UNIT_LOADED) {
+                *ret = 0;
+                return 0;
+        }
+
+        /* When the clean() method is set, can_clean() really should be set too */
+        assert(UNIT_VTABLE(u)->can_clean);
+
+        return UNIT_VTABLE(u)->can_clean(u, ret);
+}
+
 static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
         [COLLECT_INACTIVE] = "inactive",
         [COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",
index 02c7a563926b74bf995a2e2b0ae907592ff8bcf3..603c20a9047ccc86d06809d3059cef8036d5aba5 100644 (file)
@@ -475,6 +475,12 @@ typedef struct UnitVTable {
 
         int (*kill)(Unit *u, KillWho w, int signo, sd_bus_error *error);
 
+        /* Clear out the various runtime/state/cache/logs/configuration data */
+        int (*clean)(Unit *u, ExecCleanMask m);
+
+        /* Return which kind of data can be cleaned */
+        int (*can_clean)(Unit *u, ExecCleanMask *ret);
+
         bool (*can_reload)(Unit *u);
 
         /* Write all data that cannot be restored from other sources
@@ -854,6 +860,9 @@ int unit_failure_action_exit_status(Unit *u);
 
 int unit_test_trigger_loaded(Unit *u);
 
+int unit_clean(Unit *u, ExecCleanMask mask);
+int unit_can_clean(Unit *u, ExecCleanMask *ret_mask);
+
 /* Macros which append UNIT= or USER_UNIT= to the message */
 
 #define log_unit_full(unit, level, error, ...)                          \
index 6308cda61014ca15a211594c70771ff7480648d1..f81ae3b78870d72df10a7a80576e83fa1563aebb 100644 (file)
@@ -396,7 +396,7 @@ static int save_external_coredump(
                 _cleanup_free_ char *fn_compressed = NULL, *tmp_compressed = NULL;
                 _cleanup_close_ int fd_compressed = -1;
 
-                fn_compressed = strappend(fn, COMPRESSED_EXT);
+                fn_compressed = strjoin(fn, COMPRESSED_EXT);
                 if (!fn_compressed) {
                         log_oom();
                         goto uncompressed;
index 92314b667ed3193e0ebf647077ab79f2f0f8d548..127b3c5380c8187452064703816eb5c776f65f4a 100644 (file)
@@ -583,12 +583,12 @@ static int add_proc_cmdline_devices(void) {
                         continue;
 
                 if (!d->name) {
-                        d->name = strappend("luks-", d->uuid);
+                        d->name = strjoin("luks-", d->uuid);
                         if (!d->name)
                                 return log_oom();
                 }
 
-                device = strappend("UUID=", d->uuid);
+                device = strjoin("UUID=", d->uuid);
                 if (!device)
                         return log_oom();
 
index 09220dc98c7310e3c9b68482cafe39e6a0f23cba..e73dde32b883b9423ac0abd550a9638680cd201f 100644 (file)
@@ -131,7 +131,7 @@ static int generate_wants_symlinks(void) {
                 if (!p)
                         return log_oom();
 
-                f = strappend(SYSTEM_DATA_UNIT_PATH "/", *u);
+                f = path_join(SYSTEM_DATA_UNIT_PATH, *u);
                 if (!f)
                         return log_oom();
 
index 8610ab67058462e3c1dba9a757c1bf302d177155..4563daf3ad0187a14c93ae77a72b34d2eef41996 100644 (file)
@@ -6,6 +6,7 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "generator.h"
@@ -94,7 +95,7 @@ static int verify_tty(const char *name) {
 
         errno = 0;
         if (isatty(fd) <= 0)
-                return errno > 0 ? -errno : -EIO;
+                return errno_or_else(EIO);
 
         return 0;
 }
index f6e37670b90624c5b516194a15202209fb006f3c..49149b59be5df457af238399053b67b15d4a070c 100644 (file)
@@ -132,7 +132,7 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, bool requir
         if (device) {
                 char *ret;
 
-                ret = strappend("/dev/mapper/", id);
+                ret = path_join("/dev/mapper", id);
                 if (!ret)
                         return log_oom();
 
index c35ef556624071b8d7aadd3990ff49ca93dff9e2..75cba5827ce0db38df76ce9be4018f9a5d93ea3d 100644 (file)
@@ -253,11 +253,11 @@ static char* context_fallback_icon_name(Context *c) {
         assert(c);
 
         if (!isempty(c->data[PROP_CHASSIS]))
-                return strappend("computer-", c->data[PROP_CHASSIS]);
+                return strjoin("computer-", c->data[PROP_CHASSIS]);
 
         chassis = fallback_chassis();
         if (chassis)
-                return strappend("computer-", chassis);
+                return strjoin("computer-", chassis);
 
         return strdup("computer");
 }
index 881bba0eebc5b1ac6b2d036aa13e979caf089459..456179454869a724362fc5c450ef2f93c116d70d 100644 (file)
@@ -584,7 +584,7 @@ int pull_job_begin(PullJob *j) {
                 if (!cc)
                         return -ENOMEM;
 
-                hdr = strappend("If-None-Match: ", cc);
+                hdr = strjoin("If-None-Match: ", cc);
                 if (!hdr)
                         return -ENOMEM;
 
index 3e1ca5b1ba233b140e59aaa543613b9d90462ab7..764b3c217e3227f8802692b321538b85ae4d52e7 100644 (file)
@@ -1101,19 +1101,19 @@ static int add_matches(sd_journal *j, char **args) {
                                         if (!comm)
                                                 return log_oom();
 
-                                        t = strappend("_COMM=", comm);
+                                        t = strjoin("_COMM=", comm);
                                         if (!t)
                                                 return log_oom();
 
                                         /* Append _EXE only if the interpreter is not a link.
                                            Otherwise, it might be outdated often. */
                                         if (lstat(interpreter, &st) == 0 && !S_ISLNK(st.st_mode)) {
-                                                t2 = strappend("_EXE=", interpreter);
+                                                t2 = strjoin("_EXE=", interpreter);
                                                 if (!t2)
                                                         return log_oom();
                                         }
                                 } else {
-                                        t = strappend("_EXE=", p);
+                                        t = strjoin("_EXE=", p);
                                         if (!t)
                                                 return log_oom();
                                 }
index ce82102eeda625df9b761c339da48d88c1990ed0..366298758c65f006bb7fce0d9e6911f8c01fe920 100644 (file)
@@ -217,7 +217,7 @@ void dev_kmsg_record(Server *s, char *p, size_t l) {
                         char *b;
 
                         if (sd_device_get_devname(d, &g) >= 0) {
-                                b = strappend("_UDEV_DEVNODE=", g);
+                                b = strjoin("_UDEV_DEVNODE=", g);
                                 if (b) {
                                         iovec[n++] = IOVEC_MAKE_STRING(b);
                                         z++;
@@ -225,7 +225,7 @@ void dev_kmsg_record(Server *s, char *p, size_t l) {
                         }
 
                         if (sd_device_get_sysname(d, &g) >= 0) {
-                                b = strappend("_UDEV_SYSNAME=", g);
+                                b = strjoin("_UDEV_SYSNAME=", g);
                                 if (b) {
                                         iovec[n++] = IOVEC_MAKE_STRING(b);
                                         z++;
@@ -238,7 +238,7 @@ void dev_kmsg_record(Server *s, char *p, size_t l) {
                                 if (j >= N_IOVEC_UDEV_FIELDS)
                                         break;
 
-                                b = strappend("_UDEV_DEVLINK=", g);
+                                b = strjoin("_UDEV_DEVLINK=", g);
                                 if (b) {
                                         iovec[n++] = IOVEC_MAKE_STRING(b);
                                         z++;
@@ -271,13 +271,13 @@ void dev_kmsg_record(Server *s, char *p, size_t l) {
                         goto finish;
 
                 if (identifier) {
-                        syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier);
+                        syslog_identifier = strjoin("SYSLOG_IDENTIFIER=", identifier);
                         if (syslog_identifier)
                                 iovec[n++] = IOVEC_MAKE_STRING(syslog_identifier);
                 }
 
                 if (pid) {
-                        syslog_pid = strappend("SYSLOG_PID=", pid);
+                        syslog_pid = strjoin("SYSLOG_PID=", pid);
                         if (syslog_pid)
                                 iovec[n++] = IOVEC_MAKE_STRING(syslog_pid);
                 }
index ce0d9ce8c985e030255ad64ca9da81228002c009..a0c2dcd2d040e02f8c34ed04f08ab3b40cc1841f 100644 (file)
@@ -737,7 +737,7 @@ static void server_cache_hostname(Server *s) {
         if (!t)
                 return;
 
-        x = strappend("_HOSTNAME=", t);
+        x = strjoin("_HOSTNAME=", t);
         if (!x)
                 return;
 
index 24d4ac30b2d8ab19deda01cc1cda2d41933c6d9c..afebadeccc77f53a59ade846ede8cb9a4ae22d51 100644 (file)
@@ -294,7 +294,7 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea
         }
 
         if (s->identifier) {
-                syslog_identifier = strappend("SYSLOG_IDENTIFIER=", s->identifier);
+                syslog_identifier = strjoin("SYSLOG_IDENTIFIER=", s->identifier);
                 if (syslog_identifier)
                         iovec[n++] = IOVEC_MAKE_STRING(syslog_identifier);
         }
@@ -311,7 +311,7 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea
                 iovec[n++] = IOVEC_MAKE_STRING(c);
         }
 
-        message = strappend("MESSAGE=", p);
+        message = strjoin("MESSAGE=", p);
         if (message)
                 iovec[n++] = IOVEC_MAKE_STRING(message);
 
@@ -649,7 +649,7 @@ static int stdout_stream_load(StdoutStream *stream, const char *fname) {
         assert(fname);
 
         if (!stream->state_file) {
-                stream->state_file = strappend("/run/systemd/journal/streams/", fname);
+                stream->state_file = path_join("/run/systemd/journal/streams", fname);
                 if (!stream->state_file)
                         return log_oom();
         }
index de9d23a003cb359d2308636809cce4b8e45d9276..50500222ad9bd50ccc37c516d580106ef91b6c5e 100644 (file)
@@ -10,6 +10,7 @@
 #include "journal-file.h"
 #include "journal-internal.h"
 #include "macro.h"
+#include "path-util.h"
 #include "string-util.h"
 
 int main(int argc, char *argv[]) {
@@ -23,7 +24,7 @@ int main(int argc, char *argv[]) {
         assert_se(mkdtemp(dn));
         (void) chattr_path(dn, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
 
-        fn = strappend(dn, "/test.journal");
+        fn = path_join(dn, "test.journal");
 
         r = journal_file_open(-1, fn, O_CREAT|O_RDWR, 0644, false, 0, false, NULL, NULL, NULL, NULL, &new_journal);
         assert_se(r >= 0);
index 296579116c52e1c001934fb6e464ee6393ce2fae..4a29b3bea8ecba1ececc7270bc2c77b606994460 100644 (file)
@@ -27,6 +27,8 @@
 #define BUS_ERROR_NO_SUCH_DYNAMIC_USER "org.freedesktop.systemd1.NoSuchDynamicUser"
 #define BUS_ERROR_NOT_REFERENCED "org.freedesktop.systemd1.NotReferenced"
 #define BUS_ERROR_DISK_FULL "org.freedesktop.systemd1.DiskFull"
+#define BUS_ERROR_NOTHING_TO_CLEAN "org.freedesktop.systemd1.NothingToClean"
+#define BUS_ERROR_UNIT_BUSY "org.freedesktop.systemd1.UnitBusy"
 
 #define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine"
 #define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage"
index f817cf0a850d49ba959339eb33fc0dbf8043b1fc..b7ca79bb583f5b999c2fba00a45936574ca6ea93 100644 (file)
@@ -803,9 +803,12 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r
 
 int bus_add_match_internal(
                 sd_bus *bus,
-                const char *match) {
+                const char *match,
+                uint64_t *ret_counter) {
 
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         const char *e;
+        int r;
 
         assert(bus);
 
@@ -814,16 +817,24 @@ int bus_add_match_internal(
 
         e = append_eavesdrop(bus, match);
 
-        return sd_bus_call_method(
+        r = sd_bus_call_method(
                         bus,
                         "org.freedesktop.DBus",
                         "/org/freedesktop/DBus",
                         "org.freedesktop.DBus",
                         "AddMatch",
                         NULL,
-                        NULL,
+                        &reply,
                         "s",
                         e);
+        if (r < 0)
+                return r;
+
+        /* If the caller asked for it, return the read counter of the reply */
+        if (ret_counter)
+                *ret_counter = reply->read_counter;
+
+        return r;
 }
 
 int bus_add_match_internal_async(
index 3fb52b67c6ad399aaead6661e3d1a628f0b788db..eb1ae75c147e784bf5d74879ed54d01c63c9491b 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "sd-bus.h"
 
-int bus_add_match_internal(sd_bus *bus, const char *match);
+int bus_add_match_internal(sd_bus *bus, const char *match, uint64_t *ret_counter);
 int bus_add_match_internal_async(sd_bus *bus, sd_bus_slot **ret, const char *match, sd_bus_message_handler_t callback, void *userdata);
 
 int bus_remove_match_internal(sd_bus *bus, const char *match);
index 386187412b786a4b45a46696a35a42332a139209..ac1a954546cb62f6dd4b0aee99c78ee8d9979337 100644 (file)
@@ -180,7 +180,7 @@ static int errno_to_bus_error_name_new(int error, char **ret) {
         if (!name)
                 return 0;
 
-        n = strappend("System.Error.", name);
+        n = strjoin("System.Error.", name);
         if (!n)
                 return -ENOMEM;
 
index 39610c5d450052a6acf38eb5f276d6800d05b913..6d2c1e84051a8ac0df1f365b69a6ca973c7957a7 100644 (file)
@@ -44,6 +44,11 @@ struct match_callback {
 
         unsigned last_iteration;
 
+        /* Don't dispatch this slot with with messages that arrived in any iteration before or at the this
+         * one. We use this to ensure that matches don't apply "retroactively" and thus can confuse the
+         * caller: matches will only match incoming messages from the moment on the match was installed. */
+        uint64_t after;
+
         char *match_string;
 
         struct bus_match_node *match_node;
@@ -226,6 +231,7 @@ struct sd_bus {
         size_t wqueue_allocated;
 
         uint64_t cookie;
+        uint64_t read_counter; /* A counter for each incoming msg */
 
         char *unique_name;
         uint64_t unique_id;
index 14204eeb6b2d35ea614515d6467d69df9c393c9f..57ce8cca0439bf1dce0669ccbc5608c601aee0ef 100644 (file)
@@ -287,8 +287,16 @@ int bus_match_run(
         case BUS_MATCH_LEAF:
 
                 if (bus) {
-                        if (node->leaf.callback->last_iteration == bus->iteration_counter)
-                                return 0;
+                        /* Don't run this match as long as the AddMatch() call is not complete yet.
+                         *
+                         * Don't run this match unless the 'after' counter has been reached.
+                         *
+                         * Don't run this match more than once per iteration */
+
+                        if (node->leaf.callback->install_slot ||
+                            m->read_counter <= node->leaf.callback->after ||
+                            node->leaf.callback->last_iteration == bus->iteration_counter)
+                                return bus_match_run(bus, node->next, m);
 
                         node->leaf.callback->last_iteration = bus->iteration_counter;
                 }
index a7c4f81c4bac4f0879aa894c13f299d8250e9637..ced0bb3d34a91ea1764b70669ba5376648806d03 100644 (file)
@@ -128,6 +128,8 @@ struct sd_bus_message {
 
         size_t header_offsets[_BUS_MESSAGE_HEADER_MAX];
         unsigned n_header_offsets;
+
+        uint64_t read_counter;
 };
 
 static inline bool BUS_MESSAGE_NEED_BSWAP(sd_bus_message *m) {
index 9221b33d487d4ab6978da48993c7b6751e44d36f..3541c411a8b42b4ff49808906dbc601454696d83 100644 (file)
@@ -1146,6 +1146,7 @@ static int bus_socket_make_message(sd_bus *bus, size_t size) {
         bus->n_fds = 0;
 
         if (t) {
+                t->read_counter = ++bus->read_counter;
                 bus->rqueue[bus->rqueue_size++] = bus_message_ref_queued(t, bus);
                 sd_bus_message_unref(t);
         }
index 978a5056103a46a1dcbc67dc8b46d6ad122343c0..303dcea106ff2f3a63950d75f3b9d074ebcf1328 100644 (file)
@@ -484,6 +484,7 @@ static int synthesize_connected_signal(sd_bus *bus) {
                 return r;
 
         bus_message_set_sender_local(bus, m);
+        m->read_counter = ++bus->read_counter;
 
         r = bus_seal_synthetic_message(bus, m);
         if (r < 0)
@@ -2422,6 +2423,8 @@ static int process_timeout(sd_bus *bus) {
         if (r < 0)
                 return r;
 
+        m->read_counter = ++bus->read_counter;
+
         r = bus_seal_synthetic_message(bus, m);
         if (r < 0)
                 return r;
@@ -2524,6 +2527,7 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) {
                 synthetic_reply->realtime = m->realtime;
                 synthetic_reply->monotonic = m->monotonic;
                 synthetic_reply->seqnum = m->seqnum;
+                synthetic_reply->read_counter = m->read_counter;
 
                 r = bus_seal_synthetic_message(bus, synthetic_reply);
                 if (r < 0)
@@ -2866,6 +2870,8 @@ static int process_closing_reply_callback(sd_bus *bus, struct reply_callback *c)
         if (r < 0)
                 return r;
 
+        m->read_counter = ++bus->read_counter;
+
         r = bus_seal_synthetic_message(bus, m);
         if (r < 0)
                 return r;
@@ -2930,6 +2936,7 @@ static int process_closing(sd_bus *bus, sd_bus_message **ret) {
                 return r;
 
         bus_message_set_sender_local(bus, m);
+        m->read_counter = ++bus->read_counter;
 
         r = bus_seal_synthetic_message(bus, m);
         if (r < 0)
@@ -3239,8 +3246,6 @@ static int add_match_callback(
                 bus->current_slot = match_slot->match_callback.install_slot;
                 bus->current_handler = add_match_callback;
                 bus->current_userdata = userdata;
-
-                match_slot->match_callback.install_slot = sd_bus_slot_unref(match_slot->match_callback.install_slot);
         } else {
                 if (failed) /* Generic failure handling: destroy the connection */
                         bus_enter_closing(sd_bus_message_get_bus(m));
@@ -3248,6 +3253,9 @@ static int add_match_callback(
                 r = 1;
         }
 
+        /* We don't need the install method reply slot anymore, let's free it */
+        match_slot->match_callback.install_slot = sd_bus_slot_unref(match_slot->match_callback.install_slot);
+
         if (failed && match_slot->floating)
                 bus_slot_disconnect(match_slot, true);
 
@@ -3319,7 +3327,7 @@ static int bus_add_match_full(
                                  * then make it floating. */
                                 r = sd_bus_slot_set_floating(s->match_callback.install_slot, true);
                         } else
-                                r = bus_add_match_internal(bus, s->match_callback.match_string);
+                                r = bus_add_match_internal(bus, s->match_callback.match_string, &s->match_callback.after);
                         if (r < 0)
                                 goto finish;
 
index 0dc368e6e25f69d27df834216373629a3a89321a..ef22e190e4fa77908d66641e127f131e5ba8e889 100644 (file)
@@ -314,7 +314,7 @@ static int file_of_seat(const char *seat, char **_p) {
                 if (!filename_is_valid(seat))
                         return -EINVAL;
 
-                p = strappend("/run/systemd/seats/", seat);
+                p = path_join("/run/systemd/seats", seat);
         } else {
                 _cleanup_free_ char *buf = NULL;
 
@@ -322,9 +322,8 @@ static int file_of_seat(const char *seat, char **_p) {
                 if (r < 0)
                         return r;
 
-                p = strappend("/run/systemd/seats/", buf);
+                p = path_join("/run/systemd/seats", buf);
         }
-
         if (!p)
                 return -ENOMEM;
 
@@ -427,7 +426,7 @@ static int file_of_session(const char *session, char **_p) {
                 if (!session_id_valid(session))
                         return -EINVAL;
 
-                p = strappend("/run/systemd/sessions/", session);
+                p = path_join("/run/systemd/sessions", session);
         } else {
                 _cleanup_free_ char *buf = NULL;
 
@@ -435,7 +434,7 @@ static int file_of_session(const char *session, char **_p) {
                 if (r < 0)
                         return r;
 
-                p = strappend("/run/systemd/sessions/", buf);
+                p = path_join("/run/systemd/sessions", buf);
         }
 
         if (!p)
index 78504063234a631caf91bb26a208d66f0129ce2d..5323844c0876473f66687ae983ee2489b55a0d53 100644 (file)
@@ -611,7 +611,7 @@ int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t s
         if (data)
                 memcpy(data, attr_data, size);
 
-        return 0;
+        return r;
 }
 
 int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) {
index 450e298f2cac605c33e52f6cd8814cfc3f88a635..5e4d8a94a0a31eed7c028b7edea12d53d21d1492 100644 (file)
@@ -663,7 +663,7 @@ static const NLTypeSystem rtnl_route_type_system = {
 
 static const NLType rtnl_neigh_types[] = {
         [NDA_DST]               = { .type = NETLINK_TYPE_IN_ADDR },
-        [NDA_LLADDR]            = { .type = NETLINK_TYPE_ETHER_ADDR },
+        [NDA_LLADDR]            = { /* struct ether_addr or struct in_addr */ },
         [NDA_CACHEINFO]         = { .type = NETLINK_TYPE_CACHE_INFO, .size = sizeof(struct nda_cacheinfo) },
         [NDA_PROBES]            = { .type = NETLINK_TYPE_U32 },
         [NDA_VLAN]              = { .type = NETLINK_TYPE_U16 },
index 2dfde4ca7172d1c07e26afe49a600b47dd2057a6..ad56ddb580a0495a183305bdcb17620f760ce785 100644 (file)
@@ -83,7 +83,7 @@ static int from_user_dir(const char *field, char **buffer, const char **ret) {
         if (r < 0)
                 return r;
 
-        fn = strappend(c, "/user-dirs.dirs");
+        fn = path_join(c, "user-dirs.dirs");
         if (!fn)
                 return -ENOMEM;
 
@@ -141,7 +141,7 @@ static int from_user_dir(const char *field, char **buffer, const char **ret) {
                         if (r < 0)
                                 return r;
 
-                        cc = strappend(h, p+5);
+                        cc = path_join(h, p+5);
                         if (!cc)
                                 return -ENOMEM;
 
@@ -179,7 +179,7 @@ fallback:
                 if (r < 0)
                         return r;
 
-                cc = strappend(h, "/Desktop");
+                cc = path_join(h, "Desktop");
                 if (!cc)
                         return -ENOMEM;
 
index 139451483941bf71554ea782a81ce76b0da5c3e4..f8c36c94f5dbf3c3beb74b29bcb2e7c7dcff8a87 100644 (file)
@@ -353,7 +353,7 @@ int vconsole_write_data(Context *c) {
                 _cleanup_free_ char *s = NULL;
                 char **u;
 
-                s = strappend("KEYMAP=", c->vc_keymap);
+                s = strjoin("KEYMAP=", c->vc_keymap);
                 if (!s)
                         return -ENOMEM;
 
@@ -370,7 +370,7 @@ int vconsole_write_data(Context *c) {
                 _cleanup_free_ char *s = NULL;
                 char **u;
 
-                s = strappend("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
+                s = strjoin("KEYMAP_TOGGLE=", c->vc_keymap_toggle);
                 if (!s)
                         return -ENOMEM;
 
index d2f4e60f98106041f9bdff517b754d22596cde77..2e13e4aed6501d331f82b22b0d13813912b09646 100644 (file)
@@ -222,7 +222,7 @@ int devnode_acl_all(const char *seat,
                         if (cunescape(dent->d_name, UNESCAPE_RELAX, &unescaped_devname) < 0)
                                 return -ENOMEM;
 
-                        n = strappend("/dev/", unescaped_devname);
+                        n = path_join("/dev", unescaped_devname);
                         if (!n)
                                 return -ENOMEM;
 
index dacd3b3d9c0c0ce1c0e70a83f8194f238cd2e616..f631ba7219ceb7ad0f7c9a9e3c0ce0eaa2d97a79 100644 (file)
@@ -17,6 +17,7 @@
 #include "cgroup-util.h"
 #include "conf-parser.h"
 #include "device-util.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "limits-util.h"
 #include "logind.h"
@@ -191,7 +192,7 @@ int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
         errno = 0;
         p = getpwuid(uid);
         if (!p)
-                return errno > 0 ? -errno : -ENOENT;
+                return errno_or_else(ENOENT);
 
         return manager_add_user(m, uid, p->pw_gid, p->pw_name, p->pw_dir, _user);
 }
index 0e8925ab9449a29d7facea68a354e657345163e3..b7c6e00e95f10002d2bb65cd1bc3110084d58b39 100644 (file)
@@ -1242,7 +1242,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu
         errno = 0;
         pw = getpwuid(uid);
         if (!pw)
-                return errno > 0 ? -errno : -ENOENT;
+                return errno_or_else(ENOENT);
 
         r = bus_verify_polkit_async(
                         message,
@@ -1322,7 +1322,7 @@ static int trigger_device(Manager *m, sd_device *d) {
                 if (r < 0)
                         return r;
 
-                t = strappend(p, "/uevent");
+                t = path_join(p, "uevent");
                 if (!t)
                         return -ENOMEM;
 
index d963706dcef98b227fd29d4c3e8a5b5d616b7636..d32e29910129a1d8c4f7ef29107af44bc132065a 100644 (file)
@@ -17,6 +17,7 @@
 #include "logind-inhibit.h"
 #include "mkdir.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "string-table.h"
 #include "string-util.h"
 #include "tmpfile-util.h"
@@ -32,7 +33,7 @@ Inhibitor* inhibitor_new(Manager *m, const char* id) {
         if (!i)
                 return NULL;
 
-        i->state_file = strappend("/run/systemd/inhibit/", id);
+        i->state_file = path_join("/run/systemd/inhibit", id);
         if (!i->state_file)
                 return mfree(i);
 
index c33a0e0ad43c7eefe140261e26f5c7260c0c845c..3c589296115dc4cfb6022735ef6fcad1505a977e 100644 (file)
@@ -304,7 +304,7 @@ char *seat_bus_path(Seat *s) {
         if (!t)
                 return NULL;
 
-        return strappend("/org/freedesktop/login1/seat/", t);
+        return strjoin("/org/freedesktop/login1/seat/", t);
 }
 
 int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
index c3ad5f9b5b00c6af1ea4e8cee00ce3e708b700f8..9577ff86f40b851aa6187661304842bf954ef22b 100644 (file)
@@ -19,6 +19,7 @@
 #include "logind-session-dbus.h"
 #include "mkdir.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
 #include "terminal-util.h"
@@ -44,7 +45,7 @@ int seat_new(Seat** ret, Manager *m, const char *id) {
                 .manager = m,
         };
 
-        s->state_file = strappend("/run/systemd/seats/", id);
+        s->state_file = path_join("/run/systemd/seats", id);
         if (!s->state_file)
                 return -ENOMEM;
 
index c297f62cdf9936811be02a17064c627b778f38a3..fc194a3e72c20e416efbc48472e963a19024b0e1 100644 (file)
@@ -634,7 +634,7 @@ char *session_bus_path(Session *s) {
         if (!t)
                 return NULL;
 
-        return strappend("/org/freedesktop/login1/session/", t);
+        return strjoin("/org/freedesktop/login1/session/", t);
 }
 
 int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
index 17700c6921901e766d83b0ef3bedce9022635092..f78453736d20ffaa2bb1babad75d4920a109672f 100644 (file)
@@ -67,7 +67,7 @@ int session_new(Session **ret, Manager *m, const char *id) {
                 .tty_validity = _TTY_VALIDITY_INVALID,
         };
 
-        s->state_file = strappend("/run/systemd/sessions/", id);
+        s->state_file = path_join("/run/systemd/sessions", id);
         if (!s->state_file)
                 return -ENOMEM;
 
@@ -899,7 +899,7 @@ static int get_tty_atime(const char *tty, usec_t *atime) {
         assert(atime);
 
         if (!path_is_absolute(tty)) {
-                p = strappend("/dev/", tty);
+                p = path_join("/dev", tty);
                 if (!p)
                         return -ENOMEM;
 
index a41d83b8970ad29c3a637705cd4fbb2d637f9220..9104e023e8e58239a58ec4a5a732784d28916313 100644 (file)
@@ -109,7 +109,7 @@ static int show_sysfs_one(
                 if (++(*i_dev) < n_dev) {
                         _cleanup_free_ char *p = NULL;
 
-                        p = strappend(prefix, lookahead < n_dev ? special_glyph(SPECIAL_GLYPH_TREE_VERTICAL) : "  ");
+                        p = strjoin(prefix, lookahead < n_dev ? special_glyph(SPECIAL_GLYPH_TREE_VERTICAL) : "  ");
                         if (!p)
                                 return -ENOMEM;
 
index 7e7f0d51bfb0db5ea8a2adaf82b9d626788a6442..b45355d86fce7d322c2ee9bf8f33682b791d6aa8 100644 (file)
@@ -458,7 +458,7 @@ char *image_bus_path(const char *name) {
         if (!e)
                 return NULL;
 
-        return strappend("/org/freedesktop/machine1/image/", e);
+        return strjoin("/org/freedesktop/machine1/image/", e);
 }
 
 int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
index b89e6046d93a679a8f35544a9ec6a22616184b72..0d58b5eb8b6f4f6cb65702a9d6ff59a98ebf11a2 100644 (file)
@@ -1383,7 +1383,7 @@ char *machine_bus_path(Machine *m) {
         if (!e)
                 return NULL;
 
-        return strappend("/org/freedesktop/machine1/machine/", e);
+        return strjoin("/org/freedesktop/machine1/machine/", e);
 }
 
 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
index ef8ccd98fb249053a199fbf54185cc874aa4b05b..4d07a7893a9ef188f1f19e26d3ea5e932454eb4d 100644 (file)
@@ -11,6 +11,7 @@
 #include "bus-error.h"
 #include "bus-util.h"
 #include "env-file.h"
+#include "errno-util.h"
 #include "escape.h"
 #include "extract-word.h"
 #include "fd-util.h"
@@ -21,6 +22,7 @@
 #include "machine.h"
 #include "mkdir.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "process-util.h"
 #include "serialize.h"
 #include "special.h"
@@ -52,7 +54,7 @@ Machine* machine_new(Manager *manager, MachineClass class, const char *name) {
                 goto fail;
 
         if (class != MACHINE_HOST) {
-                m->state_file = strappend("/run/systemd/machines/", m->name);
+                m->state_file = path_join("/run/systemd/machines", m->name);
                 if (!m->state_file)
                         goto fail;
         }
@@ -620,7 +622,7 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
         k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
         if (k != 3) {
                 if (ferror(f))
-                        return -errno;
+                        return errno_or_else(EIO);
 
                 return -EBADMSG;
         }
@@ -651,7 +653,7 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
         k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
         if (k != 3) {
                 if (ferror(f))
-                        return -errno;
+                        return errno_or_else(EIO);
 
                 return -EBADMSG;
         }
index fea9cc2633d7e50f7ca37dc0e35042672f13c793..8031dafe158bcc3980136cc842d532cf7ca4aef8 100644 (file)
@@ -11,6 +11,7 @@
 #include "bus-common-errors.h"
 #include "bus-util.h"
 #include "cgroup-util.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "format-util.h"
@@ -625,7 +626,7 @@ static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) {
         errno = 0;
         n = fread(&success, 1, sizeof(success), f);
         if (n != sizeof(success))
-                return ret < 0 ? ret : (errno != 0 ? -errno : -EIO);
+                return ret < 0 ? ret : errno_or_else(EIO);
 
         if (ret < 0) {
                 _cleanup_free_ char *name = NULL;
@@ -669,7 +670,7 @@ static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) {
                 errno = 0;
                 n = fread(&size, 1, sizeof(size), f);
                 if (n != sizeof(size))
-                        return errno != 0 ? -errno : -EIO;
+                        return errno_or_else(EIO);
 
                 r = sd_bus_message_append(reply, "(st)", name, size);
                 if (r < 0)
@@ -914,8 +915,8 @@ static int method_map_from_machine_user(sd_bus_message *message, void *userdata,
                 if (k < 0 && feof(f))
                         break;
                 if (k != 3) {
-                        if (ferror(f) && errno > 0)
-                                return -errno;
+                        if (ferror(f))
+                                return errno_or_else(EIO);
 
                         return -EIO;
                 }
@@ -972,8 +973,8 @@ static int method_map_to_machine_user(sd_bus_message *message, void *userdata, s
                         if (k < 0 && feof(f))
                                 break;
                         if (k != 3) {
-                                if (ferror(f) && errno > 0)
-                                        return -errno;
+                                if (ferror(f))
+                                        return errno_or_else(EIO);
 
                                 return -EIO;
                         }
@@ -1036,8 +1037,8 @@ static int method_map_from_machine_group(sd_bus_message *message, void *groupdat
                 if (k < 0 && feof(f))
                         break;
                 if (k != 3) {
-                        if (ferror(f) && errno > 0)
-                                return -errno;
+                        if (ferror(f))
+                                return errno_or_else(EIO);
 
                         return -EIO;
                 }
@@ -1094,8 +1095,8 @@ static int method_map_to_machine_group(sd_bus_message *message, void *groupdata,
                         if (k < 0 && feof(f))
                                 break;
                         if (k != 3) {
-                                if (ferror(f) && errno > 0)
-                                        return -errno;
+                                if (ferror(f))
+                                        return errno_or_else(EIO);
 
                                 return -EIO;
                         }
index 33f7b3058d5ed8dcfb3df660db17499eb8c2c657..8641d18026be4a9d494770796ede9f93ce4afe1d 100644 (file)
@@ -70,6 +70,7 @@ Tunnel.IPv6FlowLabel,                     config_parse_ipv6_flowlabel,
 Tunnel.CopyDSCP,                          config_parse_bool,                         0,                             offsetof(Tunnel, copy_dscp)
 Tunnel.EncapsulationLimit,                config_parse_encap_limit,                  0,                             offsetof(Tunnel, encap_limit)
 Tunnel.Independent,                       config_parse_bool,                         0,                             offsetof(Tunnel, independent)
+Tunnel.AssignToLoopback,                  config_parse_bool,                         0,                             offsetof(Tunnel, assign_to_loopback)
 Tunnel.AllowLocalRemote,                  config_parse_tristate,                     0,                             offsetof(Tunnel, allow_localremote)
 Tunnel.FooOverUDP,                        config_parse_bool,                         0,                             offsetof(Tunnel, fou_tunnel)
 Tunnel.FOUDestinationPort,                config_parse_ip_port,                      0,                             offsetof(Tunnel, fou_destination_port)
index a59d18d5d90188e201bf4f6267ac724b4250e190..c2525408bacc24ef49f719a2944a71f0b4fdcadd 100644 (file)
@@ -44,10 +44,9 @@ static int netdev_ipip_sit_fill_message_create(NetDev *netdev, Link *link, sd_ne
 
         assert(m);
         assert(t);
-        assert(t->family == AF_INET);
 
-        if (link) {
-                r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
+        if (link || t->assign_to_loopback) {
+                r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link ? link->ifindex : LOOPBACK_IFINDEX);
                 if (r < 0)
                         return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
         }
@@ -136,10 +135,9 @@ static int netdev_gre_erspan_fill_message_create(NetDev *netdev, Link *link, sd_
         }
 
         assert(t);
-        assert(t->family == AF_INET);
 
-        if (link) {
-                r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link->ifindex);
+        if (link || t->assign_to_loopback) {
+                r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link ? link->ifindex : LOOPBACK_IFINDEX);
                 if (r < 0)
                         return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK attribute: %m");
         }
@@ -239,11 +237,10 @@ static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netl
                 t = IP6GRETAP(netdev);
 
         assert(t);
-        assert(t->family == AF_INET6);
         assert(m);
 
-        if (link) {
-                r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link->ifindex);
+        if (link || t->assign_to_loopback) {
+                r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link ? link->ifindex : LOOPBACK_IFINDEX);
                 if (r < 0)
                         return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK attribute: %m");
         }
@@ -287,11 +284,9 @@ static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_netlink
                 t = VTI6(netdev);
 
         assert(t);
-        assert((netdev->kind == NETDEV_KIND_VTI && t->family == AF_INET) ||
-               (netdev->kind == NETDEV_KIND_VTI6 && t->family == AF_INET6));
 
-        if (link) {
-                r = sd_netlink_message_append_u32(m, IFLA_VTI_LINK, link->ifindex);
+        if (link || t->assign_to_loopback) {
+                r = sd_netlink_message_append_u32(m, IFLA_VTI_LINK, link ? link->ifindex : LOOPBACK_IFINDEX);
                 if (r < 0)
                         return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_LINK attribute: %m");
         }
@@ -330,10 +325,9 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl
         assert(netdev);
         assert(m);
         assert(t);
-        assert(t->family == AF_INET6);
 
-        if (link) {
-                r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
+        if (link || t->assign_to_loopback) {
+                r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link ? link->ifindex : LOOPBACK_IFINDEX);
                 if (r < 0)
                         return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
         }
@@ -435,26 +429,20 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
 
         assert(t);
 
-        if (IN_SET(netdev->kind, NETDEV_KIND_VTI, NETDEV_KIND_IPIP, NETDEV_KIND_SIT, NETDEV_KIND_GRE)) {
-                if (t->family == AF_UNSPEC)
-                        t->family = AF_INET;
-                if (t->family != AF_INET)
-                        return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
-                                                      "vti/ipip/sit/gre tunnel without a local/remote IPv4 address configured in %s. Ignoring", filename);
-        }
+        if (IN_SET(netdev->kind, NETDEV_KIND_VTI, NETDEV_KIND_IPIP, NETDEV_KIND_SIT, NETDEV_KIND_GRE) &&
+            !IN_SET(t->family, AF_UNSPEC, AF_INET))
+                return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
+                                              "vti/ipip/sit/gre tunnel without a local/remote IPv4 address configured in %s. Ignoring", filename);
 
         if (IN_SET(netdev->kind, NETDEV_KIND_GRETAP, NETDEV_KIND_ERSPAN) &&
             (t->family != AF_INET || in_addr_is_null(t->family, &t->remote)))
                 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
                                               "gretap/erspan tunnel without a remote IPv4 address configured in %s. Ignoring", filename);
 
-        if (IN_SET(netdev->kind, NETDEV_KIND_VTI6, NETDEV_KIND_IP6TNL, NETDEV_KIND_IP6GRE)) {
-                if (t->family == AF_UNSPEC)
-                        t->family = AF_INET6;
-                if (t->family != AF_INET6)
-                        return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
-                                                      "vti6/ip6tnl/ip6gre tunnel without a local/remote IPv6 address configured in %s. Ignoring", filename);
-        }
+        if ((IN_SET(netdev->kind, NETDEV_KIND_VTI6, NETDEV_KIND_IP6TNL) && t->family != AF_INET6) ||
+            (netdev->kind == NETDEV_KIND_IP6GRE && !IN_SET(t->family, AF_UNSPEC, AF_INET6)))
+                return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
+                                              "vti6/ip6tnl/ip6gre tunnel without a local/remote IPv6 address configured in %s. Ignoring", filename);
 
         if (netdev->kind == NETDEV_KIND_IP6GRETAP &&
             (t->family != AF_INET6 || in_addr_is_null(t->family, &t->remote)))
@@ -473,6 +461,10 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
         if (netdev->kind == NETDEV_KIND_ERSPAN && (t->erspan_index >= (1 << 20) || t->erspan_index == 0))
                 return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), "Invalid erspan index %d. Ignoring", t->erspan_index);
 
+        /* netlink_message_append_in_addr_union() is used for vti/vti6. So, t->family cannot be AF_UNSPEC. */
+        if (netdev->kind == NETDEV_KIND_VTI)
+                t->family = AF_INET;
+
         return 0;
 }
 
index 3637e4f377256c2655aa1daf21a927d070fb38bc..681e80b0159d752011d74f6b36a82f4c472e996d 100644 (file)
@@ -51,6 +51,7 @@ typedef struct Tunnel {
         bool copy_dscp;
         bool independent;
         bool fou_tunnel;
+        bool assign_to_loopback;
 
         uint16_t encap_src_port;
         uint16_t fou_destination_port;
index c60b0b1ab7e30a19df25c1665909c858e354d144..afedb4b42474e58322b9c56af894fbf5db650a12 100644 (file)
@@ -4,24 +4,17 @@
 #include "netdev/xfrm.h"
 
 static int xfrm_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *message) {
-        int if_idx, r;
         Xfrm *x;
+        int r;
 
         assert(netdev);
         assert(message);
 
         x = XFRM(netdev);
 
-        if (x->independent)
-                if_idx = LOOPBACK_IFINDEX;
-        else {
-                assert(link);
-                if (link->ifindex == 0)
-                        return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(ENODEV), "Could not get interface index: %m");
-                if_idx = link->ifindex;
-        }
+        assert(link || x->independent);
 
-        r = sd_netlink_message_append_u32(message, IFLA_XFRM_LINK, if_idx);
+        r = sd_netlink_message_append_u32(message, IFLA_XFRM_LINK, link ? link->ifindex : LOOPBACK_IFINDEX);
         if (r < 0)
                 return log_netdev_error_errno(netdev, r, "Could not append IFLA_XFRM_LINK: %m");
 
index 2a748b8599f7d9f5a98c20cb74dc4ae6fe4804d0..9d755c96d6fad51e699864b4dbe5b30b6e64e263 100644 (file)
@@ -519,7 +519,7 @@ static int get_gateway_description(
                 if (!in_addr_equal(fam, &gw, gateway))
                         continue;
 
-                r = sd_netlink_message_read_ether_addr(m, NDA_LLADDR, &mac);
+                r = sd_netlink_message_read(m, NDA_LLADDR, sizeof(mac), &mac);
                 if (r < 0)
                         continue;
 
index 743f2d8ec23b6f9f5008e13601ad77db749dcc14..81edb8c7aaa3694a36bdf248a421dff914d6efed 100644 (file)
@@ -57,6 +57,72 @@ int config_parse_dhcp(
         return 0;
 }
 
+int config_parse_dhcp_use_dns(
+                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) {
+
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse UseDNS=%s, ignoring assignment: %m", rvalue);
+                return 0;
+        }
+
+        network->dhcp_use_dns = r;
+        network->dhcp6_use_dns = r;
+
+        return 0;
+}
+
+int config_parse_dhcp_use_ntp(
+                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) {
+
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to parse UseNTP=%s, ignoring assignment: %m", rvalue);
+                return 0;
+        }
+
+        network->dhcp_use_ntp = r;
+        network->dhcp6_use_ntp = r;
+
+        return 0;
+}
+
 int config_parse_section_route_table(
                 const char *unit,
                 const char *filename,
index 0e6e051599f383b66bf34762beec87cacafde2b0..c5af0beadaff9028dc0293b666d0e5aadaaaa15b 100644 (file)
@@ -28,6 +28,8 @@ const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_;
 DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_;
 
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_dns);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_domains);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_ntp);
 CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
 CONFIG_PARSER_PROTOTYPE(config_parse_section_route_table);
index c0d776f464b75b5b541e944e9fd06f90229dfa38..7ed91cebd12e6518cf9c972c4f1760b9054eeb4a 100644 (file)
@@ -75,8 +75,8 @@ static int route_scope_from_address(const Route *route, const struct in_addr *se
         assert(route);
         assert(self_addr);
 
-        if (in_addr_is_localhost(AF_INET, &route->dst) ||
-            (self_addr->s_addr && route->dst.in.s_addr == self_addr->s_addr))
+        if (in4_addr_is_localhost(&route->dst.in) ||
+            (!in4_addr_is_null(self_addr) && in4_addr_equal(&route->dst.in, self_addr)))
                 return RT_SCOPE_HOST;
         else if (in4_addr_is_null(&route->gw.in))
                 return RT_SCOPE_LINK;
@@ -153,8 +153,8 @@ static int link_set_dhcp_routes(Link *link) {
                 r = route_configure(route, link, dhcp4_route_handler);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Could not set host route: %m");
-
-                link->dhcp4_messages++;
+                if (r > 0)
+                        link->dhcp4_messages++;
         }
 
         r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
@@ -192,8 +192,8 @@ static int link_set_dhcp_routes(Link *link) {
                 r = route_configure(route_gw, link, dhcp4_route_handler);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Could not set host route: %m");
-
-                link->dhcp4_messages++;
+                if (r > 0)
+                        link->dhcp4_messages++;
 
                 r = route_new(&route);
                 if (r < 0)
@@ -209,8 +209,8 @@ static int link_set_dhcp_routes(Link *link) {
                 r = route_configure(route, link, dhcp4_route_handler);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Could not set routes: %m");
-
-                link->dhcp4_messages++;
+                if (r > 0)
+                        link->dhcp4_messages++;
         }
 
         return 0;
@@ -460,8 +460,8 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
                 link_enter_failed(link);
                 return 1;
         }
-
-        manager_rtnl_process_address(rtnl, m, link->manager);
+        if (r >= 0)
+                manager_rtnl_process_address(rtnl, m, link->manager);
 
         r = link_set_dhcp_routes(link);
         if (r < 0) {
index 286b9071ae7c91e6678afd58ce23faf7d482df94..0a73ffc848d911668cd2f758afca33617181703e 100644 (file)
@@ -408,10 +408,13 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
                 log_link_error_errno(link, r, "Could not set DHCPv6 address: %m");
 
                 link_enter_failed(link);
-
-        } else if (r >= 0)
+                return 1;
+        }
+        if (r >= 0)
                 manager_rtnl_process_address(rtnl, m, link->manager);
 
+        link_request_set_routes(link);
+
         return 1;
 }
 
index e3333698f8aea9766e739a582a73b5791eac3aed..5b7468c103d44077bc821b248eb8c31feac979c3 100644 (file)
@@ -39,7 +39,6 @@ static int fdb_entry_new_static(
 
         _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
         _cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL;
-        _cleanup_free_ struct ether_addr *mac_addr = NULL;
         int r;
 
         assert(network);
@@ -63,11 +62,6 @@ static int fdb_entry_new_static(
         if (network->n_static_fdb_entries >= STATIC_FDB_ENTRIES_PER_NETWORK_MAX)
                 return -E2BIG;
 
-        /* allocate space for MAC address. */
-        mac_addr = new0(struct ether_addr, 1);
-        if (!mac_addr)
-                return -ENOMEM;
-
         /* allocate space for and FDB entry. */
         fdb_entry = new(FdbEntry, 1);
         if (!fdb_entry)
@@ -76,7 +70,6 @@ static int fdb_entry_new_static(
         /* init FDB structure. */
         *fdb_entry = (FdbEntry) {
                 .network = network,
-                .mac_addr = TAKE_PTR(mac_addr),
                 .vni = VXLAN_VID_MAX + 1,
                 .fdb_ntf_flags = NEIGHBOR_CACHE_ENTRY_FLAGS_SELF,
         };
@@ -143,7 +136,7 @@ int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
         if (r < 0)
                 return rtnl_log_create_error(r);
 
-        r = sd_netlink_message_append_ether_addr(req, NDA_LLADDR, fdb_entry->mac_addr);
+        r = sd_netlink_message_append_data(req, NDA_LLADDR, &fdb_entry->mac_addr, sizeof(fdb_entry->mac_addr));
         if (r < 0)
                 return rtnl_log_create_error(r);
 
@@ -192,7 +185,6 @@ void fdb_entry_free(FdbEntry *fdb_entry) {
         }
 
         network_config_section_free(fdb_entry->section);
-        free(fdb_entry->mac_addr);
         free(fdb_entry);
 }
 
@@ -223,17 +215,9 @@ int config_parse_fdb_hwaddr(
         if (r < 0)
                 return log_oom();
 
-        /* read in the MAC address for the FDB table. */
-        r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
-                   &fdb_entry->mac_addr->ether_addr_octet[0],
-                   &fdb_entry->mac_addr->ether_addr_octet[1],
-                   &fdb_entry->mac_addr->ether_addr_octet[2],
-                   &fdb_entry->mac_addr->ether_addr_octet[3],
-                   &fdb_entry->mac_addr->ether_addr_octet[4],
-                   &fdb_entry->mac_addr->ether_addr_octet[5]);
-
-        if (r != ETHER_ADDR_LEN) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue);
+        r = ether_addr_from_string(rvalue, &fdb_entry->mac_addr);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
index bcdd8ce3cb241ab21ed262352c5e31f002c4d9a3..5e24ad6aee258d342371a7da2748abcf25b75f59 100644 (file)
@@ -35,7 +35,7 @@ struct FdbEntry {
         int family;
         uint16_t vlan_id;
 
-        struct ether_addr *mac_addr;
+        struct ether_addr mac_addr;
         union in_addr_union destination_addr;
         NeighborCacheEntryFlags fdb_ntf_flags;
 
index d206589bc4f5630c8b091b450450747bdb5a59f7..59f3f1dd31fb1058811d078393c22d890320fbcf 100644 (file)
@@ -3474,7 +3474,7 @@ int link_save(Link *link) {
                                         space = true;
                 }
 
-                if (link->network->dhcp_use_dns && dhcp6_lease) {
+                if (link->network->dhcp6_use_dns && dhcp6_lease) {
                         struct in6_addr *in6_addrs;
 
                         r = sd_dhcp6_lease_get_dns(dhcp6_lease, &in6_addrs);
@@ -3517,7 +3517,7 @@ int link_save(Link *link) {
                                         space = true;
                 }
 
-                if (link->network->dhcp_use_ntp && dhcp6_lease) {
+                if (link->network->dhcp6_use_ntp && dhcp6_lease) {
                         struct in6_addr *in6_addrs;
                         char **hosts;
 
index 33b7ec1d6c33f7266f274e10d3d48058c4206fb9..ec3bb47ea69aa92701d5b3dda7c15eed04ad61f2 100644 (file)
@@ -298,7 +298,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
 
         r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex);
         if (r == -ENODATA) {
-                log_debug("rtnl: received route without ifindex, ignoring");
+                log_debug("rtnl: received route message without ifindex, ignoring");
                 return 0;
         } else if (r < 0) {
                 log_warning_errno(r, "rtnl: could not get ifindex from route message, ignoring: %m");
@@ -313,7 +313,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
                 /* when enumerating we might be out of sync, but we will
                  * get the route again, so just ignore it */
                 if (!m->enumerating)
-                        log_warning("rtnl: received route for link (%d) we do not know about, ignoring", ifindex);
+                        log_warning("rtnl: received route message for link (%d) we do not know about, ignoring", ifindex);
                 return 0;
         }
 
@@ -391,43 +391,43 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
 
         r = sd_rtnl_message_route_get_dst_prefixlen(message, &dst_prefixlen);
         if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route with invalid destination prefixlen, ignoring: %m");
+                log_link_warning_errno(link, r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m");
                 return 0;
         }
 
         r = sd_rtnl_message_route_get_src_prefixlen(message, &src_prefixlen);
         if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route with invalid source prefixlen, ignoring: %m");
+                log_link_warning_errno(link, r, "rtnl: received route message with invalid source prefixlen, ignoring: %m");
                 return 0;
         }
 
         r = sd_rtnl_message_route_get_scope(message, &scope);
         if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route with invalid scope, ignoring: %m");
+                log_link_warning_errno(link, r, "rtnl: received route message with invalid scope, ignoring: %m");
                 return 0;
         }
 
         r = sd_rtnl_message_route_get_tos(message, &tos);
         if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route with invalid tos, ignoring: %m");
+                log_link_warning_errno(link, r, "rtnl: received route message with invalid tos, ignoring: %m");
                 return 0;
         }
 
         r = sd_rtnl_message_route_get_type(message, &rt_type);
         if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route with invalid type, ignoring: %m");
+                log_link_warning_errno(link, r, "rtnl: received route message with invalid type, ignoring: %m");
                 return 0;
         }
 
         r = sd_rtnl_message_route_get_table(message, &table);
         if (r < 0) {
-                log_link_warning_errno(link, r, "rtnl: received route with invalid table, ignoring: %m");
+                log_link_warning_errno(link, r, "rtnl: received route message with invalid table, ignoring: %m");
                 return 0;
         }
 
         r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &priority);
         if (r < 0 && r != -ENODATA) {
-                log_link_warning_errno(link, r, "rtnl: received route with invalid priority, ignoring: %m");
+                log_link_warning_errno(link, r, "rtnl: received route message with invalid priority, ignoring: %m");
                 return 0;
         }
 
@@ -436,6 +436,8 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
         if (DEBUG_LOGGING) {
                 _cleanup_free_ char *buf_dst = NULL, *buf_dst_prefixlen = NULL,
                         *buf_src = NULL, *buf_gw = NULL, *buf_prefsrc = NULL;
+                char buf_scope[ROUTE_SCOPE_STR_MAX], buf_table[ROUTE_TABLE_STR_MAX],
+                        buf_protocol[ROUTE_PROTOCOL_STR_MAX];
 
                 if (!in_addr_is_null(family, &dst)) {
                         (void) in_addr_to_string(family, &dst, &buf_dst);
@@ -449,10 +451,14 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
                         (void) in_addr_to_string(family, &prefsrc, &buf_prefsrc);
 
                 log_link_debug(link,
-                               "%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s",
+                               "%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
                                type == RTM_DELROUTE ? "Forgetting" : route ? "Updating remembered" : "Remembering",
                                strna(buf_dst), strempty(buf_dst_prefixlen),
-                               strna(buf_src), strna(buf_gw), strna(buf_prefsrc));
+                               strna(buf_src), strna(buf_gw), strna(buf_prefsrc),
+                               format_route_scope(scope, buf_scope, sizeof buf_scope),
+                               format_route_table(table, buf_table, sizeof buf_table),
+                               format_route_protocol(protocol, buf_protocol, sizeof buf_protocol),
+                               strna(route_type_to_string(rt_type)));
         }
 
         switch (type) {
index 78f89e026e4f035e35398145472abbcfb35c0a4f..39b66d8f6d7a24ed5fa70d3692d0303290b3e482 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "missing_network.h"
 #include "networkd-dhcp6.h"
+#include "networkd-manager.h"
 #include "networkd-ndisc.h"
 #include "networkd-route.h"
 #include "strv.h"
@@ -18,7 +19,7 @@
 #define NDISC_RDNSS_MAX 64U
 #define NDISC_PREFIX_LFT_MIN 7200U
 
-static int ndisc_netlink_message_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int ndisc_netlink_route_message_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
         assert(link);
@@ -32,6 +33,30 @@ static int ndisc_netlink_message_handler(sd_netlink *rtnl, sd_netlink_message *m
 
         if (link->ndisc_messages == 0) {
                 link->ndisc_configured = true;
+                link_request_set_routes(link);
+                link_check_ready(link);
+        }
+
+        return 1;
+}
+
+static int ndisc_netlink_address_message_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(link);
+        assert(link->ndisc_messages > 0);
+
+        link->ndisc_messages--;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -EEXIST)
+                log_link_error_errno(link, r, "Could not set NDisc route or address: %m");
+        else if (r >= 0)
+                manager_rtnl_process_address(rtnl, m, link->manager);
+
+        if (link->ndisc_messages == 0) {
+                link->ndisc_configured = true;
+                link_request_set_routes(link);
                 link_check_ready(link);
         }
 
@@ -116,7 +141,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         route->lifetime = time_now + lifetime * USEC_PER_SEC;
         route->mtu = mtu;
 
-        r = route_configure(route, link, ndisc_netlink_message_handler);
+        r = route_configure(route, link, ndisc_netlink_route_message_handler);
         if (r < 0) {
                 log_link_warning_errno(link, r, "Could not set default route: %m");
                 link_enter_failed(link);
@@ -204,7 +229,7 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
         if (address->cinfo.ifa_valid == 0)
                 return 0;
 
-        r = address_configure(address, link, ndisc_netlink_message_handler, true);
+        r = address_configure(address, link, ndisc_netlink_address_message_handler, true);
         if (r < 0) {
                 log_link_warning_errno(link, r, "Could not set SLAAC address: %m");
                 link_enter_failed(link);
@@ -254,7 +279,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get prefix address: %m");
 
-        r = route_configure(route, link, ndisc_netlink_message_handler);
+        r = route_configure(route, link, ndisc_netlink_route_message_handler);
         if (r < 0) {
                 log_link_warning_errno(link, r, "Could not set prefix route: %m");
                 link_enter_failed(link);
@@ -315,7 +340,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get route address: %m");
 
-        r = route_configure(route, link, ndisc_netlink_message_handler);
+        r = route_configure(route, link, ndisc_netlink_route_message_handler);
         if (r < 0) {
                 log_link_warning_errno(link, r, "Could not set additional route: %m");
                 link_enter_failed(link);
index d0275fdd3eafac55f6958a37043cb0141c8821a6..47839df13c1d328b0b72ea1c3bf3dc46b7f5feb5 100644 (file)
@@ -59,6 +59,7 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned
         *neighbor = (Neighbor) {
                 .network = network,
                 .family = AF_UNSPEC,
+                .lladdr_type = _NEIGHBOR_LLADDR_INVALID,
         };
 
         LIST_APPEND(neighbors, network->neighbors, neighbor);
@@ -115,11 +116,6 @@ int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_hand
         assert(link->manager);
         assert(link->manager->rtnl);
 
-        if (neighbor->family == AF_UNSPEC)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Neighbor without Address= configured");
-        if (!neighbor->mac_configured)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Neighbor without MACAddress= configured");
-
         r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH,
                                           link->ifindex, neighbor->family);
         if (r < 0)
@@ -133,7 +129,10 @@ int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_hand
         if (r < 0)
                 return log_error_errno(r, "Could not set flags: %m");
 
-        r = sd_netlink_message_append_ether_addr(req, NDA_LLADDR, &neighbor->mac);
+        if (neighbor->lladdr_type == NEIGHBOR_LLADDR_MAC)
+                r = sd_netlink_message_append_data(req, NDA_LLADDR, &neighbor->lladdr.mac, sizeof(neighbor->lladdr.mac));
+        else
+                r = sd_netlink_message_append_data(req, NDA_LLADDR, &neighbor->lladdr.ip.in, sizeof(neighbor->lladdr.ip.in));
         if (r < 0)
                 return log_error_errno(r, "Could not append NDA_LLADDR attribute: %m");
 
@@ -152,16 +151,36 @@ int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_hand
         return 0;
 }
 
-int config_parse_neighbor_address(const char *unit,
-                                  const char *filename,
-                                  unsigned line,
-                                  const char *section,
-                                  unsigned section_line,
-                                  const char *lvalue,
-                                  int ltype,
-                                  const char *rvalue,
-                                  void *data,
-                                  void *userdata) {
+int neighbor_section_verify(Neighbor *neighbor) {
+        if (section_is_invalid(neighbor->section))
+                return -EINVAL;
+
+        if (neighbor->family == AF_UNSPEC)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: Neighbor section without Address= configured. "
+                                         "Ignoring [Neighbor] section from line %u.",
+                                         neighbor->section->filename, neighbor->section->line);
+
+        if (neighbor->lladdr_type < 0)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: Neighbor section without LinkLayerAddress= configured. "
+                                         "Ignoring [Neighbor] section from line %u.",
+                                         neighbor->section->filename, neighbor->section->line);
+
+        return 0;
+}
+
+int config_parse_neighbor_address(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
 
         Network *network = userdata;
         _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
@@ -188,16 +207,60 @@ int config_parse_neighbor_address(const char *unit,
         return 0;
 }
 
-int config_parse_neighbor_hwaddr(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) {
+int config_parse_neighbor_lladdr(
+                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) {
+
+        Network *network = userdata;
+        _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = neighbor_new_static(network, filename, section_line, &n);
+        if (r < 0)
+                return r;
+
+        r = ether_addr_from_string(rvalue, &n->lladdr.mac);
+        if (r >= 0)
+                n->lladdr_type = NEIGHBOR_LLADDR_MAC;
+        else {
+                r = in_addr_from_string(AF_INET, rvalue, &n->lladdr.ip);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor LinkLayerAddress= is invalid, ignoring assignment: %s", rvalue);
+                        return 0;
+                }
+                n->lladdr_type = NEIGHBOR_LLADDR_IP;
+        }
+
+        TAKE_PTR(n);
+
+        return 0;
+}
+
+int config_parse_neighbor_hwaddr(
+                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) {
 
         Network *network = userdata;
         _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
@@ -213,13 +276,13 @@ int config_parse_neighbor_hwaddr(const char *unit,
         if (r < 0)
                 return r;
 
-        r = ether_addr_from_string(rvalue, &n->mac);
+        r = ether_addr_from_string(rvalue, &n->lladdr.mac);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor MACAddress is invalid, ignoring assignment: %s", rvalue);
+                log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor MACAddress= is invalid, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
-        n->mac_configured = true;
+        n->lladdr_type = NEIGHBOR_LLADDR_MAC;
         TAKE_PTR(n);
 
         return 0;
index f591f0b03f3f97a85710026d1d3263e26d10db30..1405ac25e2f8ee151546fc3de327df8805dfcdb1 100644 (file)
@@ -15,6 +15,13 @@ typedef struct Neighbor Neighbor;
 #include "networkd-network.h"
 #include "networkd-util.h"
 
+typedef enum {
+        NEIGHBOR_LLADDR_MAC,
+        NEIGHBOR_LLADDR_IP,
+        _NEIGHBOR_LLADDR_MAX,
+        _NEIGHBOR_LLADDR_INVALID = -1,
+} NeighborLLAddressType;
+
 struct Neighbor {
         Network *network;
         Link *link;
@@ -22,8 +29,11 @@ struct Neighbor {
 
         int family;
         union in_addr_union in_addr;
-        bool mac_configured;
-        struct ether_addr mac;
+        union {
+                struct ether_addr mac;
+                union in_addr_union ip;
+        } lladdr;
+        NeighborLLAddressType lladdr_type;
 
         LIST_FIELDS(Neighbor, neighbors);
 };
@@ -34,5 +44,8 @@ DEFINE_NETWORK_SECTION_FUNCTIONS(Neighbor, neighbor_free);
 
 int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback);
 
+int neighbor_section_verify(Neighbor *neighbor);
+
 CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_address);
 CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_hwaddr);
+CONFIG_PARSER_PROTOTYPE(config_parse_neighbor_lladdr);
index 5a2f3f4ea9f640520e5b2de00143069908c656dc..3501ffdd104636f8c05fb4a201dc0f5b50c3844e 100644 (file)
@@ -108,7 +108,8 @@ Address.Scope,                          config_parse_address_scope,
 IPv6AddressLabel.Prefix,                config_parse_address_label_prefix,               0,                             0
 IPv6AddressLabel.Label,                 config_parse_address_label,                      0,                             0
 Neighbor.Address,                       config_parse_neighbor_address,                   0,                             0
-Neighbor.MACAddress,                    config_parse_neighbor_hwaddr,                    0,                             0
+Neighbor.LinkLayerAddress,              config_parse_neighbor_lladdr,                    0,                             0
+Neighbor.MACAddress,                    config_parse_neighbor_hwaddr,                    0,                             0 /* deprecated */
 RoutingPolicyRule.TypeOfService,        config_parse_routing_policy_rule_tos,            0,                             0
 RoutingPolicyRule.Priority,             config_parse_routing_policy_rule_priority,       0,                             0
 RoutingPolicyRule.Table,                config_parse_routing_policy_rule_table,          0,                             0
@@ -139,32 +140,33 @@ Route.InitialAdvertisedReceiveWindow,   config_parse_tcp_window,
 Route.QuickAck,                         config_parse_quickack,                           0,                             0
 Route.FastOpenNoCookie,                 config_parse_fast_open_no_cookie,                0,                             0
 Route.TTLPropagate,                     config_parse_route_ttl_propagate,                0,                             0
-DHCP.ClientIdentifier,                  config_parse_dhcp_client_identifier,             0,                             offsetof(Network, dhcp_client_identifier)
-DHCP.UseDNS,                            config_parse_bool,                               0,                             offsetof(Network, dhcp_use_dns)
-DHCP.UseNTP,                            config_parse_bool,                               0,                             offsetof(Network, dhcp_use_ntp)
-DHCP.UseMTU,                            config_parse_bool,                               0,                             offsetof(Network, dhcp_use_mtu)
-DHCP.UseHostname,                       config_parse_bool,                               0,                             offsetof(Network, dhcp_use_hostname)
-DHCP.UseDomains,                        config_parse_dhcp_use_domains,                   0,                             offsetof(Network, dhcp_use_domains)
-DHCP.UseRoutes,                         config_parse_bool,                               0,                             offsetof(Network, dhcp_use_routes)
-DHCP.Anonymize,                         config_parse_bool,                               0,                             offsetof(Network, dhcp_anonymize)
-DHCP.SendHostname,                      config_parse_bool,                               0,                             offsetof(Network, dhcp_send_hostname)
-DHCP.Hostname,                          config_parse_hostname,                           0,                             offsetof(Network, dhcp_hostname)
-DHCP.RequestBroadcast,                  config_parse_bool,                               0,                             offsetof(Network, dhcp_broadcast)
-DHCP.CriticalConnection,                config_parse_tristate,                           0,                             offsetof(Network, dhcp_critical) /* deprecated */
-DHCP.VendorClassIdentifier,             config_parse_string,                             0,                             offsetof(Network, dhcp_vendor_class_identifier)
-DHCP.MaxAttempts,                       config_parse_dhcp_max_attempts,                  0,                             0
-DHCP.UserClass,                         config_parse_dhcp_user_class,                    0,                             offsetof(Network, dhcp_user_class)
-DHCP.DUIDType,                          config_parse_duid_type,                          0,                             offsetof(Network, duid)
-DHCP.DUIDRawData,                       config_parse_duid_rawdata,                       0,                             offsetof(Network, duid)
-DHCP.RouteMetric,                       config_parse_unsigned,                           0,                             offsetof(Network, dhcp_route_metric)
-DHCP.RouteTable,                        config_parse_section_route_table,                0,                             0
-DHCP.UseTimezone,                       config_parse_bool,                               0,                             offsetof(Network, dhcp_use_timezone)
-DHCP.IAID,                              config_parse_iaid,                               0,                             0
-DHCP.ListenPort,                        config_parse_uint16,                             0,                             offsetof(Network, dhcp_client_port)
-DHCP.SendRelease,                       config_parse_bool,                               0,                             offsetof(Network, dhcp_send_release)
-DHCP.RapidCommit,                       config_parse_bool,                               0,                             offsetof(Network, rapid_commit)
-DHCP.BlackList,                         config_parse_dhcp_black_listed_ip_address,       0,                             0
-DHCP.ForceDHCPv6PDOtherInformation,     config_parse_bool,                               0,                             offsetof(Network, dhcp6_force_pd_other_information)
+DHCPv4.ClientIdentifier,                config_parse_dhcp_client_identifier,             0,                             offsetof(Network, dhcp_client_identifier)
+DHCPv4.UseDNS,                          config_parse_bool,                               0,                             offsetof(Network, dhcp_use_dns)
+DHCPv4.UseNTP,                          config_parse_bool,                               0,                             offsetof(Network, dhcp_use_ntp)
+DHCPv4.UseMTU,                          config_parse_bool,                               0,                             offsetof(Network, dhcp_use_mtu)
+DHCPv4.UseHostname,                     config_parse_bool,                               0,                             offsetof(Network, dhcp_use_hostname)
+DHCPv4.UseDomains,                      config_parse_dhcp_use_domains,                   0,                             offsetof(Network, dhcp_use_domains)
+DHCPv4.UseRoutes,                       config_parse_bool,                               0,                             offsetof(Network, dhcp_use_routes)
+DHCPv4.Anonymize,                       config_parse_bool,                               0,                             offsetof(Network, dhcp_anonymize)
+DHCPv4.SendHostname,                    config_parse_bool,                               0,                             offsetof(Network, dhcp_send_hostname)
+DHCPv4.Hostname,                        config_parse_hostname,                           0,                             offsetof(Network, dhcp_hostname)
+DHCPv4.RequestBroadcast,                config_parse_bool,                               0,                             offsetof(Network, dhcp_broadcast)
+DHCPv4.VendorClassIdentifier,           config_parse_string,                             0,                             offsetof(Network, dhcp_vendor_class_identifier)
+DHCPv4.MaxAttempts,                     config_parse_dhcp_max_attempts,                  0,                             0
+DHCPv4.UserClass,                       config_parse_dhcp_user_class,                    0,                             offsetof(Network, dhcp_user_class)
+DHCPv4.DUIDType,                        config_parse_duid_type,                          0,                             offsetof(Network, duid)
+DHCPv4.DUIDRawData,                     config_parse_duid_rawdata,                       0,                             offsetof(Network, duid)
+DHCPv4.RouteMetric,                     config_parse_unsigned,                           0,                             offsetof(Network, dhcp_route_metric)
+DHCPv4.RouteTable,                      config_parse_section_route_table,                0,                             0
+DHCPv4.UseTimezone,                     config_parse_bool,                               0,                             offsetof(Network, dhcp_use_timezone)
+DHCPv4.IAID,                            config_parse_iaid,                               0,                             0
+DHCPv4.ListenPort,                      config_parse_uint16,                             0,                             offsetof(Network, dhcp_client_port)
+DHCPv4.SendRelease,                     config_parse_bool,                               0,                             offsetof(Network, dhcp_send_release)
+DHCPv4.BlackList,                       config_parse_dhcp_black_listed_ip_address,       0,                             0
+DHCPv6.UseDNS,                          config_parse_bool,                               0,                             offsetof(Network, dhcp6_use_dns)
+DHCPv6.UseNTP,                          config_parse_bool,                               0,                             offsetof(Network, dhcp6_use_ntp)
+DHCPv6.RapidCommit,                     config_parse_bool,                               0,                             offsetof(Network, rapid_commit)
+DHCPv6.ForceDHCPv6PDOtherInformation,   config_parse_bool,                               0,                             offsetof(Network, dhcp6_force_pd_other_information)
 IPv6AcceptRA.UseAutonomousPrefix,       config_parse_bool,                               0,                             offsetof(Network, ipv6_accept_ra_use_autonomous_prefix)
 IPv6AcceptRA.UseOnLinkPrefix,           config_parse_bool,                               0,                             offsetof(Network, ipv6_accept_ra_use_onlink_prefix)
 IPv6AcceptRA.UseDNS,                    config_parse_bool,                               0,                             offsetof(Network, ipv6_accept_ra_use_dns)
@@ -225,9 +227,32 @@ CAN.RestartSec,                         config_parse_sec,
 CAN.TripleSampling,                     config_parse_tristate,                           0,                             offsetof(Network, can_triple_sampling)
 /* backwards compatibility: do not add new entries to this section */
 Network.IPv4LL,                         config_parse_ipv4ll,                             0,                             offsetof(Network, link_local)
-DHCPv4.UseDNS,                          config_parse_bool,                               0,                             offsetof(Network, dhcp_use_dns)
-DHCPv4.UseMTU,                          config_parse_bool,                               0,                             offsetof(Network, dhcp_use_mtu)
-DHCPv4.UseHostname,                     config_parse_bool,                               0,                             offsetof(Network, dhcp_use_hostname)
+DHCP.ClientIdentifier,                  config_parse_dhcp_client_identifier,             0,                             offsetof(Network, dhcp_client_identifier)
+DHCP.UseDNS,                            config_parse_dhcp_use_dns,                       0,                             0
+DHCP.UseNTP,                            config_parse_dhcp_use_ntp,                       0,                             0
+DHCP.UseMTU,                            config_parse_bool,                               0,                             offsetof(Network, dhcp_use_mtu)
+DHCP.UseHostname,                       config_parse_bool,                               0,                             offsetof(Network, dhcp_use_hostname)
+DHCP.UseDomains,                        config_parse_dhcp_use_domains,                   0,                             offsetof(Network, dhcp_use_domains)
 DHCP.UseDomainName,                     config_parse_dhcp_use_domains,                   0,                             offsetof(Network, dhcp_use_domains)
+DHCP.UseRoutes,                         config_parse_bool,                               0,                             offsetof(Network, dhcp_use_routes)
+DHCP.Anonymize,                         config_parse_bool,                               0,                             offsetof(Network, dhcp_anonymize)
+DHCP.SendHostname,                      config_parse_bool,                               0,                             offsetof(Network, dhcp_send_hostname)
+DHCP.Hostname,                          config_parse_hostname,                           0,                             offsetof(Network, dhcp_hostname)
+DHCP.RequestBroadcast,                  config_parse_bool,                               0,                             offsetof(Network, dhcp_broadcast)
+DHCP.CriticalConnection,                config_parse_tristate,                           0,                             offsetof(Network, dhcp_critical)
+DHCP.VendorClassIdentifier,             config_parse_string,                             0,                             offsetof(Network, dhcp_vendor_class_identifier)
+DHCP.MaxAttempts,                       config_parse_dhcp_max_attempts,                  0,                             0
+DHCP.UserClass,                         config_parse_dhcp_user_class,                    0,                             offsetof(Network, dhcp_user_class)
+DHCP.DUIDType,                          config_parse_duid_type,                          0,                             offsetof(Network, duid)
+DHCP.DUIDRawData,                       config_parse_duid_rawdata,                       0,                             offsetof(Network, duid)
+DHCP.RouteMetric,                       config_parse_unsigned,                           0,                             offsetof(Network, dhcp_route_metric)
+DHCP.RouteTable,                        config_parse_section_route_table,                0,                             0
+DHCP.UseTimezone,                       config_parse_bool,                               0,                             offsetof(Network, dhcp_use_timezone)
+DHCP.IAID,                              config_parse_iaid,                               0,                             0
+DHCP.ListenPort,                        config_parse_uint16,                             0,                             offsetof(Network, dhcp_client_port)
+DHCP.SendRelease,                       config_parse_bool,                               0,                             offsetof(Network, dhcp_send_release)
+DHCP.BlackList,                         config_parse_dhcp_black_listed_ip_address,       0,                             0
+DHCP.RapidCommit,                       config_parse_bool,                               0,                             offsetof(Network, rapid_commit)
+DHCP.ForceDHCPv6PDOtherInformation,     config_parse_bool,                               0,                             offsetof(Network, dhcp6_force_pd_other_information)
 DHCPv4.UseDomainName,                   config_parse_dhcp_use_domains,                   0,                             offsetof(Network, dhcp_use_domains)
-DHCPv4.CriticalConnection,              config_parse_bool,                               0,                             offsetof(Network, dhcp_critical)
+DHCPv4.CriticalConnection,              config_parse_tristate,                           0,                             offsetof(Network, dhcp_critical)
index 3f1753049ceadabb18014edc52bdd89fa67cb453..9d3c383378e6d59494c6770cedc3a3e6c8b3fe58 100644 (file)
@@ -269,7 +269,7 @@ int network_verify(Network *network) {
                         fdb_entry_free(fdb);
 
         LIST_FOREACH_SAFE(neighbors, neighbor, neighbor_next, network->neighbors)
-                if (section_is_invalid(neighbor->section))
+                if (neighbor_section_verify(neighbor) < 0)
                         neighbor_free(neighbor);
 
         LIST_FOREACH_SAFE(labels, label, label_next, network->address_labels)
@@ -361,6 +361,9 @@ int network_load_one(Manager *manager, const char *filename) {
                 .dhcp_use_timezone = false,
                 .rapid_commit = true,
 
+                .dhcp6_use_ntp = true,
+                .dhcp6_use_dns = true,
+
                 .dhcp_server_emit_dns = true,
                 .dhcp_server_emit_ntp = true,
                 .dhcp_server_emit_router = true,
@@ -426,6 +429,7 @@ int network_load_one(Manager *manager, const char *filename) {
                               "Route\0"
                               "DHCP\0"
                               "DHCPv4\0" /* compat */
+                              "DHCPv6\0"
                               "DHCPServer\0"
                               "IPv6AcceptRA\0"
                               "IPv6NDPProxyAddress\0"
index a16ec7413132332601bd9ae340fded3a5d226b21..82fad492a8f30b24db055888f964febbfbabc833 100644 (file)
@@ -102,6 +102,10 @@ struct Network {
         DHCPUseDomains dhcp_use_domains;
         Set *dhcp_black_listed_ip;
 
+        /* DHCPv6 Client support*/
+        bool dhcp6_use_dns;
+        bool dhcp6_use_ntp;
+
         /* DHCP Server Support */
         bool dhcp_server;
         bool dhcp_server_emit_dns;
index f79d5b2057071560cd4157e73418217581de6148..ddc0fe279f4f7836a35cd395d4c21ac5ace800d1 100644 (file)
@@ -12,7 +12,9 @@
 #include "networkd-route.h"
 #include "parse-util.h"
 #include "set.h"
+#include "string-table.h"
 #include "string-util.h"
+#include "strxcpyx.h"
 #include "sysctl-util.h"
 #include "util.h"
 
@@ -412,6 +414,29 @@ int route_remove(Route *route, Link *link,
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not create RTM_DELROUTE message: %m");
 
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *dst = NULL, *dst_prefixlen = NULL, *src = NULL, *gw = NULL, *prefsrc = NULL;
+                char scope[ROUTE_SCOPE_STR_MAX], table[ROUTE_TABLE_STR_MAX], protocol[ROUTE_PROTOCOL_STR_MAX];
+
+                if (!in_addr_is_null(route->family, &route->dst)) {
+                        (void) in_addr_to_string(route->family, &route->dst, &dst);
+                        (void) asprintf(&dst_prefixlen, "/%u", route->dst_prefixlen);
+                }
+                if (!in_addr_is_null(route->family, &route->src))
+                        (void) in_addr_to_string(route->family, &route->src, &src);
+                if (!in_addr_is_null(route->family, &route->gw))
+                        (void) in_addr_to_string(route->family, &route->gw, &gw);
+                if (!in_addr_is_null(route->family, &route->prefsrc))
+                        (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
+
+                log_link_debug(link, "Removing route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
+                               strna(dst), strempty(dst_prefixlen), strna(src), strna(gw), strna(prefsrc),
+                               format_route_scope(route->scope, scope, sizeof(scope)),
+                               format_route_table(route->table, table, sizeof(table)),
+                               format_route_protocol(route->protocol, protocol, sizeof(protocol)),
+                               strna(route_type_to_string(route->type)));
+        }
+
         if (in_addr_is_null(route->family, &route->gw) == 0) {
                 r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->family, &route->gw);
                 if (r < 0)
@@ -513,6 +538,7 @@ int route_configure(
 
         if (DEBUG_LOGGING) {
                 _cleanup_free_ char *dst = NULL, *dst_prefixlen = NULL, *src = NULL, *gw = NULL, *prefsrc = NULL;
+                char scope[ROUTE_SCOPE_STR_MAX], table[ROUTE_TABLE_STR_MAX], protocol[ROUTE_PROTOCOL_STR_MAX];
 
                 if (!in_addr_is_null(route->family, &route->dst)) {
                         (void) in_addr_to_string(route->family, &route->dst, &dst);
@@ -525,8 +551,12 @@ int route_configure(
                 if (!in_addr_is_null(route->family, &route->prefsrc))
                         (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
 
-                log_link_debug(link, "Configuring route: dst: %s%s, src: %s, gw: %s, prefsrc: %s",
-                               strna(dst), strempty(dst_prefixlen), strna(src), strna(gw), strna(prefsrc));
+                log_link_debug(link, "Configuring route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
+                               strna(dst), strempty(dst_prefixlen), strna(src), strna(gw), strna(prefsrc),
+                               format_route_scope(route->scope, scope, sizeof(scope)),
+                               format_route_table(route->table, table, sizeof(table)),
+                               format_route_protocol(route->protocol, protocol, sizeof(protocol)),
+                               strna(route_type_to_string(route->type)));
         }
 
         r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
@@ -718,6 +748,8 @@ int network_add_ipv4ll_route(Network *network) {
         n->family = AF_INET;
         n->dst_prefixlen = 16;
         n->scope = RT_SCOPE_LINK;
+        n->scope_set = true;
+        n->table_set = true;
         n->priority = IPV4LL_ROUTE_METRIC;
         n->protocol = RTPROT_STATIC;
 
@@ -749,6 +781,88 @@ int network_add_default_route_on_device(Network *network) {
         return 0;
 }
 
+static const char * const route_type_table[__RTN_MAX] = {
+        [RTN_UNICAST]     = "unicast",
+        [RTN_LOCAL]       = "local",
+        [RTN_BROADCAST]   = "broadcast",
+        [RTN_ANYCAST]     = "anycast",
+        [RTN_MULTICAST]   = "multicast",
+        [RTN_BLACKHOLE]   = "blackhole",
+        [RTN_UNREACHABLE] = "unreachable",
+        [RTN_PROHIBIT]    = "prohibit",
+        [RTN_THROW]       = "throw",
+        [RTN_NAT]         = "nat",
+        [RTN_XRESOLVE]    = "xresolve",
+};
+
+assert_cc(__RTN_MAX <= UCHAR_MAX);
+DEFINE_STRING_TABLE_LOOKUP(route_type, int);
+
+static const char * const route_scope_table[] = {
+        [RT_SCOPE_UNIVERSE] = "global",
+        [RT_SCOPE_SITE]     = "site",
+        [RT_SCOPE_LINK]     = "link",
+        [RT_SCOPE_HOST]     = "host",
+        [RT_SCOPE_NOWHERE]  = "nowhere",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_scope, int);
+
+const char *format_route_scope(int scope, char *buf, size_t size) {
+        const char *s;
+        char *p = buf;
+
+        s = route_scope_to_string(scope);
+        if (s)
+                strpcpy(&p, size, s);
+        else
+                strpcpyf(&p, size, "%d", scope);
+
+        return buf;
+}
+
+static const char * const route_table_table[] = {
+        [RT_TABLE_DEFAULT] = "default",
+        [RT_TABLE_MAIN]    = "main",
+        [RT_TABLE_LOCAL]   = "local",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table, int);
+
+const char *format_route_table(int table, char *buf, size_t size) {
+        const char *s;
+        char *p = buf;
+
+        s = route_table_to_string(table);
+        if (s)
+                strpcpy(&p, size, s);
+        else
+                strpcpyf(&p, size, "%d", table);
+
+        return buf;
+}
+
+static const char * const route_protocol_table[] = {
+        [RTPROT_KERNEL] = "kernel",
+        [RTPROT_BOOT]   = "boot",
+        [RTPROT_STATIC] = "static",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_protocol, int);
+
+const char *format_route_protocol(int protocol, char *buf, size_t size) {
+        const char *s;
+        char *p = buf;
+
+        s = route_protocol_to_string(protocol);
+        if (s)
+                strpcpy(&p, size, s);
+        else
+                strpcpyf(&p, size, "%d", protocol);
+
+        return buf;
+}
+
 int config_parse_gateway(
                 const char *unit,
                 const char *filename,
@@ -948,17 +1062,14 @@ int config_parse_route_scope(
         if (r < 0)
                 return r;
 
-        if (streq(rvalue, "host"))
-                n->scope = RT_SCOPE_HOST;
-        else if (streq(rvalue, "link"))
-                n->scope = RT_SCOPE_LINK;
-        else if (streq(rvalue, "global"))
-                n->scope = RT_SCOPE_UNIVERSE;
-        else {
+        r = route_scope_from_string(rvalue);
+        if (r < 0) {
                 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue);
                 return 0;
         }
 
+        n->scope = r;
+        n->scope_set = true;
         TAKE_PTR(n);
         return 0;
 }
@@ -989,13 +1100,19 @@ int config_parse_route_table(
         if (r < 0)
                 return r;
 
-        r = safe_atou32(rvalue, &n->table);
-        if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
-                           "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
-                return 0;
+        r = route_table_from_string(rvalue);
+        if (r >= 0)
+                n->table = r;
+        else {
+                r = safe_atou32(rvalue, &n->table);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r,
+                                   "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
+                        return 0;
+                }
         }
 
+        n->table_set = true;
         TAKE_PTR(n);
         return 0;
 }
@@ -1094,12 +1211,9 @@ int config_parse_route_protocol(
         if (r < 0)
                 return r;
 
-        if (streq(rvalue, "kernel"))
-                n->protocol = RTPROT_KERNEL;
-        else if (streq(rvalue, "boot"))
-                n->protocol = RTPROT_BOOT;
-        else if (streq(rvalue, "static"))
-                n->protocol = RTPROT_STATIC;
+        r = route_protocol_from_string(rvalue);
+        if (r >= 0)
+                n->protocol = r;
         else {
                 r = safe_atou8(rvalue , &n->protocol);
                 if (r < 0) {
@@ -1127,28 +1241,21 @@ int config_parse_route_type(
 
         Network *network = userdata;
         _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
-        int r;
+        int t, r;
 
         r = route_new_static(network, filename, section_line, &n);
         if (r < 0)
                 return r;
 
-        if (streq(rvalue, "unicast"))
-                n->type = RTN_UNICAST;
-        else if (streq(rvalue, "blackhole"))
-                n->type = RTN_BLACKHOLE;
-        else if (streq(rvalue, "unreachable"))
-                n->type = RTN_UNREACHABLE;
-        else if (streq(rvalue, "prohibit"))
-                n->type = RTN_PROHIBIT;
-        else if (streq(rvalue, "throw"))
-                n->type = RTN_THROW;
-        else {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+        t = route_type_from_string(rvalue);
+        if (t < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, 0,
                            "Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
                 return 0;
         }
 
+        n->type = (unsigned char) t;
+
         TAKE_PTR(n);
         return 0;
 }
@@ -1366,6 +1473,18 @@ int route_section_verify(Route *route, Network *network) {
                                          route->section->filename, route->section->line);
         }
 
+        if (route->family != AF_INET6) {
+                if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
+                        route->table = RT_TABLE_LOCAL;
+
+                if (!route->scope_set) {
+                        if (IN_SET(route->type, RTN_LOCAL, RTN_NAT))
+                                route->scope = RT_SCOPE_HOST;
+                        else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST))
+                                route->scope = RT_SCOPE_LINK;
+                }
+        }
+
         if (network->n_static_addresses == 0 &&
             in_addr_is_null(route->family, &route->gw) == 0 &&
             route->gateway_onlink < 0) {
index 3c0ac896aea5a26961c11dea9cae0a6cd331fb2d..86a7a82617fd8e6c6175031fdcca9f77cd0cd4be 100644 (file)
@@ -2,6 +2,7 @@
 #pragma once
 
 #include "conf-parser.h"
+#include "macro.h"
 
 typedef struct Route Route;
 typedef struct NetworkConfigSection NetworkConfigSection;
@@ -23,11 +24,13 @@ struct Route {
         unsigned char dst_prefixlen;
         unsigned char src_prefixlen;
         unsigned char scope;
+        bool scope_set;
         unsigned char protocol;  /* RTPROT_* */
         unsigned char type; /* RTN_* */
         unsigned char tos;
         uint32_t priority; /* note that ip(8) calls this 'metric' */
         uint32_t table;
+        bool table_set;
         uint32_t mtu;
         uint32_t initcwnd;
         uint32_t initrwnd;
@@ -65,6 +68,18 @@ DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free);
 int network_add_ipv4ll_route(Network *network);
 int network_add_default_route_on_device(Network *network);
 
+const char* route_type_to_string(int t) _const_;
+int route_type_from_string(const char *s) _pure_;
+
+#define ROUTE_SCOPE_STR_MAX CONST_MAX(DECIMAL_STR_MAX(int), STRLEN("nowhere") + 1)
+const char *format_route_scope(int scope, char *buf, size_t size);
+
+#define ROUTE_TABLE_STR_MAX CONST_MAX(DECIMAL_STR_MAX(int), STRLEN("default") + 1)
+const char *format_route_table(int table, char *buf, size_t size);
+
+#define ROUTE_PROTOCOL_STR_MAX CONST_MAX(DECIMAL_STR_MAX(int), STRLEN("kernel") + 1)
+const char *format_route_protocol(int protocol, char *buf, size_t size);
+
 CONFIG_PARSER_PROTOTYPE(config_parse_gateway);
 CONFIG_PARSER_PROTOTYPE(config_parse_preferred_src);
 CONFIG_PARSER_PROTOTYPE(config_parse_destination);
index 750529bbbd39985ebf6a7d79d7e76337c538bba0..c2e7683bcf7ea7e247e669bc722f638d37b68183 100644 (file)
@@ -166,7 +166,7 @@ static int run(int argc, char* argv[]) {
                 our_env[i++] = (char*) "READY=1";
 
         if (arg_status) {
-                status = strappend("STATUS=", arg_status);
+                status = strjoin("STATUS=", arg_status);
                 if (!status)
                         return log_oom();
 
index d09e12346fc6aa2bbe4bd55d5f2f3313f82dfe00..214cf5749b20a3d2d41775d3e1f9bc07b540f757 100644 (file)
@@ -489,7 +489,7 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to add netlink interface index: %m");
 
-                n = strappend("mv-", *i);
+                n = strjoin("mv-", *i);
                 if (!n)
                         return log_oom();
 
@@ -564,7 +564,7 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to add netlink interface index: %m");
 
-                n = strappend("iv-", *i);
+                n = strjoin("iv-", *i);
                 if (!n)
                         return log_oom();
 
index 9ff37c6dbdd7661a043ca4d37cc1f9205d023039..3a99736813798c57c09c94e8ae14119480c8b719 100644 (file)
@@ -505,7 +505,7 @@ int config_parse_network_zone(
         assert(lvalue);
         assert(rvalue);
 
-        j = strappend("vz-", rvalue);
+        j = strjoin("vz-", rvalue);
         if (!ifname_valid(j)) {
                 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid network zone name, ignoring: %s", rvalue);
                 return 0;
index a563c026dd6edb9646c876002c94f21b27d22c0c..117a637699084ef5b454314c89b25b30a09cee5c 100644 (file)
@@ -725,7 +725,7 @@ static int parse_argv(int argc, char *argv[]) {
                 case ARG_NETWORK_ZONE: {
                         char *j;
 
-                        j = strappend("vz-", optarg);
+                        j = strjoin("vz-", optarg);
                         if (!j)
                                 return log_oom();
 
index 94ec8be896ee4eb411d1c677e06f021f08dde24e..f44d0b8343155a02b449b81306949a55ad5ccfc2 100644 (file)
@@ -1281,7 +1281,7 @@ static int read_domain_one(sd_bus_message *m, bool with_ifindex, char **ret) {
         }
 
         if (route_only)
-                str = strappend("~", domain);
+                str = strjoin("~", domain);
         else
                 str = strdup(domain);
         if (!str)
index 0ee7340ffc6b36437de9d66ec0fb717c4a612fe6..e5693bf72d41e49c9ccf781ee38b43887c7e06a2 100644 (file)
@@ -161,7 +161,7 @@ int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl, int ifindex
         if (!soa->soa.mname)
                 return -ENOMEM;
 
-        soa->soa.rname = strappend("root.", name);
+        soa->soa.rname = strjoin("root.", name);
         if (!soa->soa.rname)
                 return -ENOMEM;
 
index 997cb986e610878af4a9eea3024835b9ed7eb345..fc375a88215037759b33b3dc9114a1f4bd62804a 100644 (file)
@@ -2182,7 +2182,7 @@ static int dnssec_test_positive_wildcard_nsec(
                         return -EBADMSG;
 
                 /* Replace the label we stripped off with an asterisk */
-                wc = strappend("*.", name);
+                wc = strjoin("*.", name);
                 if (!wc)
                         return -ENOMEM;
 
index 02153b929fb9e19fa78d1dbb47616d74a1333ebc..d65c780791cf3b0007eb741727447dd3a491a82d 100644 (file)
@@ -1500,7 +1500,7 @@ void manager_cleanup_saved_user(Manager *m) {
                 continue;
 
         rm:
-                p = strappend("/run/systemd/resolve/netif/", de->d_name);
+                p = path_join("/run/systemd/resolve/netif", de->d_name);
                 if (!p) {
                         log_oom();
                         return;
index 23c5a12454ca301ab1f765f3da1b51f3cd73df04..aeba2ebfd399ea1a9be14c070acbb862fc6e1104 100644 (file)
@@ -241,7 +241,7 @@ static int dump_processes(
 
                         special = special_glyph(more ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE);
 
-                        pp = strappend(prefix, special);
+                        pp = strjoin(prefix, special);
                         if (!pp)
                                 return -ENOMEM;
 
index 6a7358e3015f4be19aa4cb221a914cf70f28ab31..b1945ceb8d62079f8c131a5323d9066a9f18582f 100644 (file)
@@ -313,7 +313,7 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
         if (!is_ex_prop && (flags & (EXEC_COMMAND_NO_ENV_EXPAND|EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC))) {
                 /* Upgrade the ExecXYZ= property to ExecXYZEx= for convenience */
                 is_ex_prop = true;
-                upgraded_name = strappend(field, "Ex");
+                upgraded_name = strjoin(field, "Ex");
                 if (!upgraded_name)
                         return log_oom();
         }
index b3b05872e9590f316cb11001497321d758901136..8e301250bcbb58b3152b09bbc1369c4b40f118af 100644 (file)
@@ -990,7 +990,7 @@ int bus_message_print_all_properties(
                                 return log_oom();
                 }
 
-                name_with_equal = strappend(name, "=");
+                name_with_equal = strjoin(name, "=");
                 if (!name_with_equal)
                         return log_oom();
 
diff --git a/src/shared/bus-wait-for-units.c b/src/shared/bus-wait-for-units.c
new file mode 100644 (file)
index 0000000..d07f491
--- /dev/null
@@ -0,0 +1,434 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bus-util.h"
+#include "bus-wait-for-units.h"
+#include "hashmap.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-def.h"
+
+typedef struct WaitForItem {
+        BusWaitForUnits *parent;
+
+        BusWaitForUnitsFlags flags;
+
+        char *bus_path;
+
+        sd_bus_slot *slot_get_all;
+        sd_bus_slot *slot_properties_changed;
+
+        bus_wait_for_units_unit_callback unit_callback;
+        void *userdata;
+
+        char *active_state;
+        uint32_t job_id;
+        char *clean_result;
+} WaitForItem;
+
+typedef struct BusWaitForUnits {
+        sd_bus *bus;
+        sd_bus_slot *slot_disconnected;
+
+        Hashmap *items;
+
+        bus_wait_for_units_ready_callback ready_callback;
+        void *userdata;
+
+        WaitForItem *current;
+
+        BusWaitForUnitsState state;
+        bool has_failed:1;
+} BusWaitForUnits;
+
+static WaitForItem *wait_for_item_free(WaitForItem *item) {
+        int r;
+
+        if (!item)
+                return NULL;
+
+        if (item->parent) {
+                if (FLAGS_SET(item->flags, BUS_WAIT_REFFED) && item->bus_path && item->parent->bus) {
+                        r = sd_bus_call_method_async(
+                                        item->parent->bus,
+                                        NULL,
+                                        "org.freedesktop.systemd1",
+                                        item->bus_path,
+                                        "org.freedesktop.systemd1.Unit",
+                                        "Unref",
+                                        NULL,
+                                        NULL,
+                                        NULL);
+                        if (r < 0)
+                                log_debug_errno(r, "Failed to drop reference to unit %s, ignoring: %m", item->bus_path);
+                }
+
+                assert_se(hashmap_remove(item->parent->items, item->bus_path) == item);
+
+                if (item->parent->current == item)
+                        item->parent->current = NULL;
+        }
+
+        sd_bus_slot_unref(item->slot_properties_changed);
+        sd_bus_slot_unref(item->slot_get_all);
+
+        free(item->bus_path);
+        free(item->active_state);
+        free(item->clean_result);
+
+        return mfree(item);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(WaitForItem*, wait_for_item_free);
+
+static void bus_wait_for_units_clear(BusWaitForUnits *d) {
+        WaitForItem *item;
+
+        assert(d);
+
+        d->slot_disconnected = sd_bus_slot_unref(d->slot_disconnected);
+        d->bus = sd_bus_unref(d->bus);
+
+        while ((item = hashmap_first(d->items))) {
+                d->current = item;
+
+                item->unit_callback(d, item->bus_path, false, item->userdata);
+                wait_for_item_free(item);
+        }
+
+        d->items = hashmap_free(d->items);
+}
+
+static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+        BusWaitForUnits *d = userdata;
+
+        assert(m);
+        assert(d);
+
+        log_error("Warning! D-Bus connection terminated.");
+
+        bus_wait_for_units_clear(d);
+
+        if (d->ready_callback)
+                d->ready_callback(d, false, d->userdata);
+        else /* If no ready callback is specified close the connection so that the event loop exits */
+                sd_bus_close(sd_bus_message_get_bus(m));
+
+        return 0;
+}
+
+int bus_wait_for_units_new(sd_bus *bus, BusWaitForUnits **ret) {
+        _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *d = NULL;
+        int r;
+
+        assert(bus);
+        assert(ret);
+
+        d = new(BusWaitForUnits, 1);
+        if (!d)
+                return -ENOMEM;
+
+        *d = (BusWaitForUnits) {
+                .state = BUS_WAIT_SUCCESS,
+                .bus = sd_bus_ref(bus),
+        };
+
+        r = sd_bus_match_signal_async(
+                        bus,
+                        &d->slot_disconnected,
+                        "org.freedesktop.DBus.Local",
+                        NULL,
+                        "org.freedesktop.DBus.Local",
+                        "Disconnected",
+                        match_disconnected, NULL, d);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(d);
+        return 0;
+}
+
+BusWaitForUnits* bus_wait_for_units_free(BusWaitForUnits *d) {
+        if (!d)
+                return NULL;
+
+        bus_wait_for_units_clear(d);
+        sd_bus_slot_unref(d->slot_disconnected);
+        sd_bus_unref(d->bus);
+
+        return mfree(d);
+}
+
+static bool bus_wait_for_units_is_ready(BusWaitForUnits *d) {
+        assert(d);
+
+        if (!d->bus) /* Disconnected? */
+                return true;
+
+        return hashmap_isempty(d->items);
+}
+
+void bus_wait_for_units_set_ready_callback(BusWaitForUnits *d, bus_wait_for_units_ready_callback callback, void *userdata) {
+        assert(d);
+
+        d->ready_callback = callback;
+        d->userdata = userdata;
+}
+
+static void bus_wait_for_units_check_ready(BusWaitForUnits *d) {
+        assert(d);
+
+        if (!bus_wait_for_units_is_ready(d))
+                return;
+
+        d->state = d->has_failed ? BUS_WAIT_FAILURE : BUS_WAIT_SUCCESS;
+
+        if (d->ready_callback)
+                d->ready_callback(d, d->state, d->userdata);
+}
+
+static void wait_for_item_check_ready(WaitForItem *item) {
+        BusWaitForUnits *d;
+
+        assert(item);
+        assert(d = item->parent);
+
+        if (FLAGS_SET(item->flags, BUS_WAIT_FOR_MAINTENANCE_END)) {
+
+                if (item->clean_result && !streq(item->clean_result, "success"))
+                        d->has_failed = true;
+
+                if (!item->active_state || streq(item->active_state, "maintenance"))
+                        return;
+        }
+
+        if (FLAGS_SET(item->flags, BUS_WAIT_NO_JOB) && item->job_id != 0)
+                return;
+
+        if (FLAGS_SET(item->flags, BUS_WAIT_FOR_INACTIVE)) {
+
+                if (streq_ptr(item->active_state, "failed"))
+                        d->has_failed = true;
+                else if (!streq_ptr(item->active_state, "inactive"))
+                        return;
+        }
+
+        if (item->unit_callback) {
+                d->current = item;
+                item->unit_callback(d, item->bus_path, true, item->userdata);
+        }
+
+        wait_for_item_free(item);
+
+        bus_wait_for_units_check_ready(d);
+}
+
+static int property_map_job(
+                sd_bus *bus,
+                const char *member,
+                sd_bus_message *m,
+                sd_bus_error *error,
+                void *userdata) {
+
+        WaitForItem *item = userdata;
+        const char *path;
+        uint32_t id;
+        int r;
+
+        assert(item);
+
+        r = sd_bus_message_read(m, "(uo)", &id, &path);
+        if (r < 0)
+                return r;
+
+        item->job_id = id;
+        return 0;
+}
+
+static int wait_for_item_parse_properties(WaitForItem *item, sd_bus_message *m) {
+
+        static const struct bus_properties_map map[] = {
+                { "ActiveState", "s",    NULL,             offsetof(WaitForItem, active_state) },
+                { "Job",         "(uo)", property_map_job, 0                                   },
+                { "CleanResult", "s",    NULL,             offsetof(WaitForItem, clean_result) },
+                {}
+        };
+
+        int r;
+
+        assert(item);
+        assert(m);
+
+        r = bus_message_map_all_properties(m, map, BUS_MAP_STRDUP, NULL, item);
+        if (r < 0)
+                return r;
+
+        wait_for_item_check_ready(item);
+        return 0;
+}
+
+static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+        WaitForItem *item = userdata;
+        const char *interface;
+        int r;
+
+        assert(item);
+
+        r = sd_bus_message_read(m, "s", &interface);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to parse PropertiesChanged signal: %m");
+                return 0;
+        }
+
+        if (!streq(interface, "org.freedesktop.systemd1.Unit"))
+                return 0;
+
+        r = wait_for_item_parse_properties(item, m);
+        if (r < 0)
+                log_debug_errno(r, "Failed to process PropertiesChanged signal: %m");
+
+        return 0;
+}
+
+static int on_get_all_properties(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+        WaitForItem *item = userdata;
+        int r;
+
+        assert(item);
+
+        if (sd_bus_error_is_set(error)) {
+                BusWaitForUnits *d = item->parent;
+
+                d->has_failed = true;
+
+                log_debug_errno(sd_bus_error_get_errno(error), "GetAll() failed for %s: %s",
+                                item->bus_path, error->message);
+
+                d->current = item;
+                item->unit_callback(d, item->bus_path, false, item->userdata);
+                wait_for_item_free(item);
+
+                bus_wait_for_units_check_ready(d);
+                return 0;
+        }
+
+        r = wait_for_item_parse_properties(item, m);
+        if (r < 0)
+                log_debug_errno(r, "Failed to process GetAll method reply: %m");
+
+        return 0;
+}
+
+int bus_wait_for_units_add_unit(
+                BusWaitForUnits *d,
+                const char *unit,
+                BusWaitForUnitsFlags flags,
+                bus_wait_for_units_unit_callback callback,
+                void *userdata) {
+
+        _cleanup_(wait_for_item_freep) WaitForItem *item = NULL;
+        int r;
+
+        assert(d);
+        assert(unit);
+
+        assert(flags != 0);
+
+        r = hashmap_ensure_allocated(&d->items, &string_hash_ops);
+        if (r < 0)
+                return r;
+
+        item = new(WaitForItem, 1);
+        if (!item)
+                return -ENOMEM;
+
+        *item = (WaitForItem) {
+                .flags = flags,
+                .bus_path = unit_dbus_path_from_name(unit),
+                .unit_callback = callback,
+                .userdata = userdata,
+                .job_id = UINT32_MAX,
+        };
+
+        if (!item->bus_path)
+                return -ENOMEM;
+
+        if (!FLAGS_SET(item->flags, BUS_WAIT_REFFED)) {
+                r = sd_bus_call_method_async(
+                                d->bus,
+                                NULL,
+                                "org.freedesktop.systemd1",
+                                item->bus_path,
+                                "org.freedesktop.systemd1.Unit",
+                                "Ref",
+                                NULL,
+                                NULL,
+                                NULL);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to add reference to unit %s: %m", unit);
+
+
+                item->flags |= BUS_WAIT_REFFED;
+        }
+
+        r = sd_bus_match_signal_async(
+                        d->bus,
+                        &item->slot_properties_changed,
+                        "org.freedesktop.systemd1",
+                        item->bus_path,
+                        "org.freedesktop.DBus.Properties",
+                        "PropertiesChanged",
+                        on_properties_changed,
+                        NULL,
+                        item);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to request match for PropertiesChanged signal: %m");
+
+        r = sd_bus_call_method_async(
+                        d->bus,
+                        &item->slot_get_all,
+                        "org.freedesktop.systemd1",
+                        item->bus_path,
+                        "org.freedesktop.DBus.Properties",
+                        "GetAll",
+                        on_get_all_properties,
+                        item,
+                        "s", FLAGS_SET(item->flags, BUS_WAIT_FOR_MAINTENANCE_END) ? NULL : "org.freedesktop.systemd1.Unit");
+        if (r < 0)
+                return log_debug_errno(r, "Failed to request properties of unit %s: %m", unit);
+
+        r = hashmap_put(d->items, item->bus_path, item);
+        if (r < 0)
+                return r;
+
+        d->state = BUS_WAIT_RUNNING;
+        item->parent = d;
+        TAKE_PTR(item);
+        return 0;
+}
+
+int bus_wait_for_units_run(BusWaitForUnits *d) {
+        int r;
+
+        assert(d);
+
+        while (d->state == BUS_WAIT_RUNNING) {
+
+                r = sd_bus_process(d->bus, NULL);
+                if (r < 0)
+                        return r;
+                if (r > 0)
+                        continue;
+
+                r = sd_bus_wait(d->bus, (uint64_t) -1);
+                if (r < 0)
+                        return r;
+        }
+
+        return d->state;
+}
+
+BusWaitForUnitsState bus_wait_for_units_state(BusWaitForUnits *d) {
+        assert(d);
+
+        return d->state;
+}
diff --git a/src/shared/bus-wait-for-units.h b/src/shared/bus-wait-for-units.h
new file mode 100644 (file)
index 0000000..a20f3d8
--- /dev/null
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "macro.h"
+#include "sd-bus.h"
+
+typedef struct BusWaitForUnits BusWaitForUnits;
+
+typedef enum BusWaitForUnitsState {
+        BUS_WAIT_SUCCESS,    /* Nothing to wait for anymore and nothing failed */
+        BUS_WAIT_FAILURE,    /* dito, but something failed */
+        BUS_WAIT_RUNNING,    /* Still something to wait for */
+        _BUS_WAIT_FOR_UNITS_STATE_MAX,
+        _BUS_WAIT_FOR_UNITS_STATE_INVALID = -1,
+} BusWaitForUnitsState;
+
+typedef enum BusWaitForUnitsFlags {
+        BUS_WAIT_FOR_MAINTENANCE_END = 1 << 0, /* Wait until the unit is no longer in maintenance state */
+        BUS_WAIT_FOR_INACTIVE        = 1 << 1, /* Wait until the unit is back in inactive or dead state */
+        BUS_WAIT_NO_JOB              = 1 << 2, /* Wait until there's no more job pending */
+        BUS_WAIT_REFFED              = 1 << 3, /* The unit is already reffed with RefUnit() */
+} BusWaitForUnitsFlags;
+
+typedef void (*bus_wait_for_units_ready_callback)(BusWaitForUnits *d, BusWaitForUnitsState state, void *userdata);
+typedef void (*bus_wait_for_units_unit_callback)(BusWaitForUnits *d, const char *unit_path, bool good, void *userdata);
+
+int bus_wait_for_units_new(sd_bus *bus, BusWaitForUnits **ret);
+BusWaitForUnits* bus_wait_for_units_free(BusWaitForUnits *d);
+
+BusWaitForUnitsState bus_wait_for_units_state(BusWaitForUnits *d);
+void bus_wait_for_units_set_ready_callback(BusWaitForUnits *d, bus_wait_for_units_ready_callback callback, void *userdata);
+int bus_wait_for_units_add_unit(BusWaitForUnits *d, const char *unit, BusWaitForUnitsFlags flags, bus_wait_for_units_unit_callback callback, void *userdata);
+int bus_wait_for_units_run(BusWaitForUnits *d);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForUnits*, bus_wait_for_units_free);
index 465e38c4fbf2a5e3f6d52836e950e7c590710992..732bc752afdad22fcab6d68742bbf6aea10e71b8 100644 (file)
@@ -165,7 +165,7 @@ int show_cgroup_by_path(
                         printf("%s%s%s\n", prefix, special_glyph(SPECIAL_GLYPH_TREE_BRANCH), cg_unescape(basename(last)));
 
                         if (!p1) {
-                                p1 = strappend(prefix, special_glyph(SPECIAL_GLYPH_TREE_VERTICAL));
+                                p1 = strjoin(prefix, special_glyph(SPECIAL_GLYPH_TREE_VERTICAL));
                                 if (!p1)
                                         return -ENOMEM;
                         }
@@ -187,7 +187,7 @@ int show_cgroup_by_path(
                 printf("%s%s%s\n", prefix, special_glyph(SPECIAL_GLYPH_TREE_RIGHT), cg_unescape(basename(last)));
 
                 if (!p2) {
-                        p2 = strappend(prefix, "  ");
+                        p2 = strjoin(prefix, "  ");
                         if (!p2)
                                 return -ENOMEM;
                 }
index 030e12e4ee2e688aa07167838348b26973491e32..a54714169e0485cc0e160f2b95399dd89c58679d 100644 (file)
@@ -60,7 +60,7 @@ int probe_filesystem(const char *node, char **ret_fstype) {
         errno = 0;
         b = blkid_new_probe_from_filename(node);
         if (!b)
-                return -errno ?: -ENOMEM;
+                return errno_or_else(ENOMEM);
 
         blkid_probe_enable_superblocks(b, 1);
         blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
@@ -76,7 +76,7 @@ int probe_filesystem(const char *node, char **ret_fstype) {
                 return -EUCLEAN;
         }
         if (r != 0)
-                return -errno ?: -EIO;
+                return errno_or_else(EIO);
 
         (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
 
@@ -333,7 +333,7 @@ int dissect_image(
         errno = 0;
         r = blkid_probe_set_device(b, fd, 0, 0);
         if (r != 0)
-                return -errno ?: -ENOMEM;
+                return errno_or_else(ENOMEM);
 
         if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
                 /* Look for file system superblocks, unless we only shall look for GPT partition tables */
@@ -349,7 +349,7 @@ int dissect_image(
         if (IN_SET(r, -2, 1))
                 return log_debug_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to identify any partition table.");
         if (r != 0)
-                return -errno ?: -EIO;
+                return errno_or_else(EIO);
 
         m = new0(DissectedImage, 1);
         if (!m)
@@ -415,7 +415,7 @@ int dissect_image(
         errno = 0;
         pl = blkid_probe_get_partitions(b);
         if (!pl)
-                return -errno ?: -ENOMEM;
+                return errno_or_else(ENOMEM);
 
         r = loop_wait_for_partitions_to_appear(fd, d, blkid_partlist_numof_partitions(pl), flags, &e);
         if (r < 0)
index 7273fde1860fb8d9072a35093ede0bbd9faf858b..5b571681f28469fb518ac871b1ab552824d34a27 100644 (file)
@@ -156,15 +156,15 @@ int generator_write_fsck_deps(
         if (path_equal(where, "/")) {
                 const char *lnk;
 
-                lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/"SPECIAL_FSCK_ROOT_SERVICE);
+                lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/" SPECIAL_FSCK_ROOT_SERVICE);
 
                 (void) mkdir_parents(lnk, 0755);
-                if (symlink(SYSTEM_DATA_UNIT_PATH "/"SPECIAL_FSCK_ROOT_SERVICE, lnk) < 0)
+                if (symlink(SYSTEM_DATA_UNIT_PATH "/" SPECIAL_FSCK_ROOT_SERVICE, lnk) < 0)
                         return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
 
         } else {
                 _cleanup_free_ char *_fsck = NULL;
-                const char *fsck;
+                const char *fsck, *dep;
 
                 if (in_initrd() && path_equal(where, "/sysroot")) {
                         r = write_fsck_sysroot_service(dir, what);
@@ -172,7 +172,15 @@ int generator_write_fsck_deps(
                                 return r;
 
                         fsck = SPECIAL_FSCK_ROOT_SERVICE;
+                        dep = "Requires";
                 } else {
+                        /* When this is /usr, then let's add a Wants= dependency, otherwise a Requires=
+                         * dependency. Why? We can't possibly unmount /usr during shutdown, but if we have a
+                         * Requires= from /usr onto a fsck@.service unit and that unit is shut down, then
+                         * we'd have to unmount /usr too.  */
+
+                        dep = !in_initrd() && path_equal(where, "/usr") ? "Wants" : "Requires";
+
                         r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to create fsck service name: %m");
@@ -181,9 +189,9 @@ int generator_write_fsck_deps(
                 }
 
                 fprintf(f,
-                        "Requires=%1$s\n"
-                        "After=%1$s\n",
-                        fsck);
+                        "%1$s=%2$s\n"
+                        "After=%2$s\n",
+                        dep, fsck);
         }
 
         return 0;
index 105412bc8fd799883196735e60ef96a561664404..68ffd12f03c4da1c3638777976ca2af5f6e24256 100644 (file)
@@ -2308,7 +2308,7 @@ int unit_file_revert(
                                         has_vendor = true;
                         }
 
-                        dropin = strappend(path, ".d");
+                        dropin = strjoin(path, ".d");
                         if (!dropin)
                                 return -ENOMEM;
 
index a068049b57f1863902ed9dec1665b6102ed3275d..f1bb50cfa2c8ce84df08ac44799b77a9fabe2596 100644 (file)
@@ -279,7 +279,8 @@ static int json_variant_new(JsonVariant **ret, JsonVariantType type, size_t spac
 
         assert_return(ret, -EINVAL);
 
-        v = malloc0(offsetof(JsonVariant, value) + space);
+        v = malloc0(MAX(sizeof(JsonVariant),
+                        offsetof(JsonVariant, value) + space));
         if (!v)
                 return -ENOMEM;
 
@@ -1664,7 +1665,8 @@ static int json_variant_copy(JsonVariant **nv, JsonVariant *v) {
         default:
                 /* Everything else copy by reference */
 
-                c = malloc0(offsetof(JsonVariant, reference) + sizeof(JsonVariant*));
+                c = malloc0(MAX(sizeof(JsonVariant),
+                                offsetof(JsonVariant, reference) + sizeof(JsonVariant*)));
                 if (!c)
                         return -ENOMEM;
 
@@ -1677,7 +1679,8 @@ static int json_variant_copy(JsonVariant **nv, JsonVariant *v) {
                 return 0;
         }
 
-        c = malloc0(offsetof(JsonVariant, value) + k);
+        c = malloc0(MAX(sizeof(JsonVariant),
+                        offsetof(JsonVariant, value) + k));
         if (!c)
                 return -ENOMEM;
 
index ee086e35b47b342f63e789186234d1f757c5b635..07744b34b49c5177b37a6cef4dbeb409137e034d 100644 (file)
@@ -87,8 +87,8 @@ static char **image_settings_path(Image *image) {
 
         fn = strjoina(image->name, ".nspawn");
 
-        FOREACH_STRING(s, "/etc/systemd/nspawn/", "/run/systemd/nspawn/") {
-                l[i] = strappend(s, fn);
+        FOREACH_STRING(s, "/etc/systemd/nspawn", "/run/systemd/nspawn") {
+                l[i] = path_join(s, fn);
                 if (!l[i])
                         return NULL;
 
@@ -441,7 +441,7 @@ int image_find(ImageClass class, const char *name, Image **ret) {
                         if (errno != ENOENT)
                                 return -errno;
 
-                        raw = strappend(name, ".raw");
+                        raw = strjoin(name, ".raw");
                         if (!raw)
                                 return -ENOMEM;
 
index d2540320d8a6b696e97ce2cf97b863a669060a6f..bdd823bbd1565e42f434af8d3d76f5003b16c8c4 100644 (file)
@@ -29,6 +29,8 @@ shared_sources = files('''
         bus-util.h
         bus-wait-for-jobs.c
         bus-wait-for-jobs.h
+        bus-wait-for-units.c
+        bus-wait-for-units.h
         calendarspec.c
         calendarspec.h
         cgroup-show.c
index 6494210ee9e12c8f115cdf7d1b3b76a70219a748..f1caddb477d3cb29fd2510afc441a315f727a24b 100644 (file)
@@ -32,7 +32,7 @@ int xdg_user_runtime_dir(char **ret, const char *suffix) {
         if (!e)
                 return -ENXIO;
 
-        j = strappend(e, suffix);
+        j = strjoin(e, suffix);
         if (!j)
                 return -ENOMEM;
 
@@ -49,7 +49,7 @@ int xdg_user_config_dir(char **ret, const char *suffix) {
 
         e = getenv("XDG_CONFIG_HOME");
         if (e)
-                j = strappend(e, suffix);
+                j = strjoin(e, suffix);
         else {
                 _cleanup_free_ char *home = NULL;
 
@@ -81,7 +81,7 @@ int xdg_user_data_dir(char **ret, const char *suffix) {
 
         e = getenv("XDG_DATA_HOME");
         if (e)
-                j = strappend(e, suffix);
+                j = strjoin(e, suffix);
         else {
                 _cleanup_free_ char *home = NULL;
 
@@ -270,15 +270,15 @@ static int acquire_generator_dirs(
                 prefix = strjoina(e, "/systemd");
         }
 
-        x = strappend(prefix, "/generator");
+        x = path_join(prefix, "generator");
         if (!x)
                 return -ENOMEM;
 
-        y = strappend(prefix, "/generator.early");
+        y = path_join(prefix, "generator.early");
         if (!y)
                 return -ENOMEM;
 
-        z = strappend(prefix, "/generator.late");
+        z = path_join(prefix, "generator.late");
         if (!z)
                 return -ENOMEM;
 
index bb2643a4804194e32278d0394aaae4fc3e1ea822..9838701a3d9d9baa49c48991f6a3e667fa0a102d 100644 (file)
@@ -236,7 +236,7 @@ static int parse_argv(int argc, char *argv[]) {
                         if (path_startswith(optarg, "/proc/sys"))
                                 p = strdup(optarg);
                         else
-                                p = strappend("/proc/sys/", optarg);
+                                p = path_join("/proc/sys", optarg);
                         if (!p)
                                 return log_oom();
 
index b7de010704864eae858c655066af21900da3c347..6490fe6f0d1c4bc187e24183a3a154fe5a426ba6 100644 (file)
@@ -28,6 +28,7 @@
 #include "bus-unit-util.h"
 #include "bus-util.h"
 #include "bus-wait-for-jobs.h"
+#include "bus-wait-for-units.h"
 #include "cgroup-show.h"
 #include "cgroup-util.h"
 #include "copy.h"
@@ -162,12 +163,14 @@ static const char *arg_boot_loader_entry = NULL;
 static bool arg_now = false;
 static bool arg_jobs_before = false;
 static bool arg_jobs_after = false;
+static char **arg_clean_what = NULL;
 
 STATIC_DESTRUCTOR_REGISTER(arg_wall, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_types, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_states, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_clean_what, strv_freep);
 
 static int daemon_reload(int argc, char *argv[], void* userdata);
 static int trivial_method(int argc, char *argv[], void *userdata);
@@ -2790,158 +2793,6 @@ static const char *verb_to_job_type(const char *verb) {
        return "start";
 }
 
-typedef struct {
-        sd_bus_slot *match;
-        sd_event *event;
-        Set *unit_paths;
-        bool any_failed;
-} WaitContext;
-
-static void wait_context_free(WaitContext *c) {
-        c->match = sd_bus_slot_unref(c->match);
-        c->event = sd_event_unref(c->event);
-        c->unit_paths = set_free_free(c->unit_paths);
-}
-
-static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
-        const char *path, *interface, *active_state = NULL, *job_path = NULL;
-        WaitContext *c = userdata;
-        bool is_failed;
-        int r;
-
-        /* Called whenever we get a PropertiesChanged signal. Checks if ActiveState changed to inactive/failed.
-         *
-         * Signal parameters: (s interface, a{sv} changed_properties, as invalidated_properties) */
-
-        path = sd_bus_message_get_path(m);
-        if (!set_contains(c->unit_paths, path))
-                return 0;
-
-        r = sd_bus_message_read(m, "s", &interface);
-        if (r < 0)
-                return bus_log_parse_error(r);
-
-        if (!streq(interface, "org.freedesktop.systemd1.Unit")) /* ActiveState is on the Unit interface */
-                return 0;
-
-        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
-        if (r < 0)
-                return bus_log_parse_error(r);
-
-        for (;;) {
-                const char *s;
-
-                r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv");
-                if (r < 0)
-                        return bus_log_parse_error(r);
-                if (r == 0) /* end of array */
-                        break;
-
-                r = sd_bus_message_read(m, "s", &s); /* Property name */
-                if (r < 0)
-                        return bus_log_parse_error(r);
-
-                if (streq(s, "ActiveState")) {
-                        r = sd_bus_message_read(m, "v", "s", &active_state);
-                        if (r < 0)
-                                return bus_log_parse_error(r);
-
-                        if (job_path) /* Found everything we need */
-                                break;
-
-                } else if (streq(s, "Job")) {
-                        uint32_t job_id;
-
-                        r = sd_bus_message_read(m, "v", "(uo)", &job_id, &job_path);
-                        if (r < 0)
-                                return bus_log_parse_error(r);
-
-                        /* There's still a job pending for this unit, let's ignore this for now, and return right-away. */
-                        if (job_id != 0)
-                                return 0;
-
-                        if (active_state) /* Found everything we need */
-                                break;
-
-                } else {
-                        r = sd_bus_message_skip(m, "v"); /* Other property */
-                        if (r < 0)
-                                return bus_log_parse_error(r);
-                }
-
-                r = sd_bus_message_exit_container(m);
-                if (r < 0)
-                        return bus_log_parse_error(r);
-        }
-
-        /* If this didn't contain the ActiveState property we can't do anything */
-        if (!active_state)
-                return 0;
-
-        is_failed = streq(active_state, "failed");
-        if (streq(active_state, "inactive") || is_failed) {
-                log_debug("%s became %s, dropping from --wait tracking", path, active_state);
-                free(set_remove(c->unit_paths, path));
-                c->any_failed = c->any_failed || is_failed;
-        } else
-                log_debug("ActiveState on %s changed to %s", path, active_state);
-
-        if (set_isempty(c->unit_paths))
-                sd_event_exit(c->event, EXIT_SUCCESS);
-
-        return 0;
-}
-
-static int wait_context_watch(
-                WaitContext *wait_context,
-                sd_bus *bus,
-                const char *name) {
-
-        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_free_ char *unit_path = NULL;
-        int r;
-
-        assert(wait_context);
-        assert(name);
-
-        log_debug("Watching for property changes of %s", name);
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "RefUnit",
-                        &error,
-                        NULL,
-                        "s", name);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add reference to unit %s: %s", name, bus_error_message(&error, r));
-
-        unit_path = unit_dbus_path_from_name(name);
-        if (!unit_path)
-                return log_oom();
-
-        r = set_ensure_allocated(&wait_context->unit_paths, &string_hash_ops);
-        if (r < 0)
-                return log_oom();
-
-        r = set_put_strdup(wait_context->unit_paths, unit_path);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add unit path %s to set: %m", unit_path);
-
-        r = sd_bus_match_signal_async(bus,
-                                      &wait_context->match,
-                                      NULL,
-                                      unit_path,
-                                      "org.freedesktop.DBus.Properties",
-                                      "PropertiesChanged",
-                                      on_properties_changed, NULL, wait_context);
-        if (r < 0)
-                return log_error_errno(r, "Failed to request match for PropertiesChanged signal: %m");
-
-        return 0;
-}
-
 static int start_unit_one(
                 sd_bus *bus,
                 const char *method,    /* When using classic per-job bus methods */
@@ -2950,7 +2801,7 @@ static int start_unit_one(
                 const char *mode,
                 sd_bus_error *error,
                 BusWaitForJobs *w,
-                WaitContext *wait_context) {
+                BusWaitForUnits *wu) {
 
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         const char *path;
@@ -2962,12 +2813,6 @@ static int start_unit_one(
         assert(mode);
         assert(error);
 
-        if (wait_context) {
-                r = wait_context_watch(wait_context, bus, name);
-                if (r < 0)
-                        return r;
-        }
-
         log_debug("%s dbus call org.freedesktop.systemd1.Manager %s(%s, %s)",
                   arg_dry_run ? "Would execute" : "Executing",
                   method, name, mode);
@@ -3056,6 +2901,12 @@ static int start_unit_one(
                         return log_error_errno(r, "Failed to watch job for %s: %m", name);
         }
 
+        if (wu) {
+                r = bus_wait_for_units_add_unit(wu, name, BUS_WAIT_FOR_INACTIVE|BUS_WAIT_NO_JOB, NULL, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to watch unit %s: %m", name);
+        }
+
         return 0;
 
 fail:
@@ -3187,8 +3038,8 @@ static const char** make_extra_args(const char *extra_args[static 4]) {
 }
 
 static int start_unit(int argc, char *argv[], void *userdata) {
+        _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *wu = NULL;
         _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
-        _cleanup_(wait_context_free) WaitContext wait_context = {};
         const char *method, *job_type, *mode, *one_name, *suffix = NULL;
         _cleanup_free_ char **stopped_units = NULL; /* Do not use _cleanup_strv_free_ */
         _cleanup_strv_free_ char **names = NULL;
@@ -3276,19 +3127,15 @@ static int start_unit(int argc, char *argv[], void *userdata) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to enable subscription: %m");
 
-                r = sd_event_default(&wait_context.event);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to allocate event loop: %m");
-
-                r = sd_bus_attach_event(bus, wait_context.event, 0);
+                r = bus_wait_for_units_new(bus, &wu);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to attach bus to event loop: %m");
+                        return log_error_errno(r, "Failed to allocate unit watch context: %m");
         }
 
         STRV_FOREACH(name, names) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
 
-                r = start_unit_one(bus, method, job_type, *name, mode, &error, w, arg_wait ? &wait_context : NULL);
+                r = start_unit_one(bus, method, job_type, *name, mode, &error, w, wu);
                 if (ret == EXIT_SUCCESS && r < 0)
                         ret = translate_bus_error_to_exit_status(r, &error);
 
@@ -3313,11 +3160,11 @@ static int start_unit(int argc, char *argv[], void *userdata) {
                                 (void) check_triggering_units(bus, *name);
         }
 
-        if (ret == EXIT_SUCCESS && arg_wait && !set_isempty(wait_context.unit_paths)) {
-                r = sd_event_loop(wait_context.event);
+        if (arg_wait) {
+                r = bus_wait_for_units_run(wu);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to run event loop: %m");
-                if (wait_context.any_failed)
+                        return log_error_errno(r, "Failed to wait for units: %m");
+                if (r == BUS_WAIT_FAILURE && ret == EXIT_SUCCESS)
                         ret = EXIT_FAILURE;
         }
 
@@ -3951,6 +3798,101 @@ static int kill_unit(int argc, char *argv[], void *userdata) {
         return r;
 }
 
+static int clean_unit(int argc, char *argv[], void *userdata) {
+        _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *w = NULL;
+        _cleanup_strv_free_ char **names = NULL;
+        int r, ret = EXIT_SUCCESS;
+        char **name;
+        sd_bus *bus;
+
+        r = acquire_bus(BUS_FULL, &bus);
+        if (r < 0)
+                return r;
+
+        polkit_agent_open_maybe();
+
+        if (!arg_clean_what) {
+                arg_clean_what = strv_new("cache", "runtime");
+                if (!arg_clean_what)
+                        return log_oom();
+        }
+
+        r = expand_names(bus, strv_skip(argv, 1), NULL, &names);
+        if (r < 0)
+                return log_error_errno(r, "Failed to expand names: %m");
+
+        if (!arg_no_block) {
+                r = bus_wait_for_units_new(bus, &w);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to allocate unit waiter: %m");
+        }
+
+        STRV_FOREACH(name, names) {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+                _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+                if (w) {
+                        /* If we shall wait for the cleaning to complete, let's add a ref on the unit first */
+                        r = sd_bus_call_method(
+                                        bus,
+                                        "org.freedesktop.systemd1",
+                                        "/org/freedesktop/systemd1",
+                                        "org.freedesktop.systemd1.Manager",
+                                        "RefUnit",
+                                        &error,
+                                        NULL,
+                                        "s", *name);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to add reference to unit %s: %s", *name, bus_error_message(&error, r));
+                                if (ret == EXIT_SUCCESS)
+                                        ret = r;
+                                continue;
+                        }
+                }
+
+                r = sd_bus_message_new_method_call(
+                                bus,
+                                &m,
+                                "org.freedesktop.systemd1",
+                                "/org/freedesktop/systemd1",
+                                "org.freedesktop.systemd1.Manager",
+                                "CleanUnit");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "s", *name);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append_strv(m, arg_clean_what);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_call(bus, m, 0, &error, NULL);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to clean unit %s: %s", *name, bus_error_message(&error, r));
+                        if (ret == EXIT_SUCCESS) {
+                                ret = r;
+                                continue;
+                        }
+                }
+
+                if (w) {
+                        r = bus_wait_for_units_add_unit(w, *name, BUS_WAIT_REFFED|BUS_WAIT_FOR_MAINTENANCE_END, NULL, NULL);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to watch unit %s: %m", *name);
+                }
+        }
+
+        r = bus_wait_for_units_run(w);
+        if (r < 0)
+                return log_error_errno(r, "Failed to wait for units: %m");
+        if (r == BUS_WAIT_FAILURE)
+                ret = EXIT_FAILURE;
+
+        return ret;
+}
+
 typedef struct ExecStatusInfo {
         char *name;
 
@@ -6517,7 +6459,7 @@ static int enable_sysv_units(const char *verb, char **args) {
                 }
 
                 if (!isempty(arg_root)) {
-                        q = strappend("--root=", arg_root);
+                        q = strjoin("--root=", arg_root);
                         if (!q)
                                 return log_oom();
 
@@ -7712,6 +7654,7 @@ static int systemctl_help(void) {
                "                      When shutting down or sleeping, ignore inhibitors\n"
                "     --kill-who=WHO   Who to send signal to\n"
                "  -s --signal=SIGNAL  Which signal to send\n"
+               "     --what=RESOURCES Which types of resources to remove\n"
                "     --now            Start or stop unit in addition to enabling or disabling it\n"
                "     --dry-run        Only print what would be done\n"
                "  -q --quiet          Suppress output\n"
@@ -7760,6 +7703,8 @@ static int systemctl_help(void) {
                "                                      if supported, otherwise restart\n"
                "  isolate UNIT                        Start one unit and stop all others\n"
                "  kill UNIT...                        Send signal to processes of a unit\n"
+               "  clean UNIT...                       Clean runtime, cache, state, logs or\n"
+               "                                      or configuration of unit\n"
                "  is-active PATTERN...                Check whether units are active\n"
                "  is-failed PATTERN...                Check whether units are failed\n"
                "  status [PATTERN...|PID...]          Show runtime status of one or more units\n"
@@ -8063,6 +8008,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_NOW,
                 ARG_MESSAGE,
                 ARG_WAIT,
+                ARG_WHAT,
         };
 
         static const struct option options[] = {
@@ -8114,6 +8060,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "now",                 no_argument,       NULL, ARG_NOW                 },
                 { "message",             required_argument, NULL, ARG_MESSAGE             },
                 { "show-transaction",    no_argument,       NULL, 'T'                     },
+                { "what",                required_argument, NULL, ARG_WHAT                },
                 {}
         };
 
@@ -8466,6 +8413,38 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_show_transaction = true;
                         break;
 
+                case ARG_WHAT: {
+                        const char *p;
+
+                        if (isempty(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--what= requires arguments.");
+
+                        for (p = optarg;;) {
+                                _cleanup_free_ char *k = NULL;
+
+                                r = extract_first_word(&p, &k, ",", 0);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse directory type: %s", optarg);
+                                if (r == 0)
+                                        break;
+
+                                if (streq(k, "help")) {
+                                        puts("runtime\n"
+                                             "state\n"
+                                             "cache\n"
+                                             "logs\n"
+                                             "configuration");
+                                        return 0;
+                                }
+
+                                r = strv_consume(&arg_clean_what, TAKE_PTR(k));
+                                if (r < 0)
+                                        return log_oom();
+                        }
+
+                        break;
+                }
+
                 case '.':
                         /* Output an error mimicking getopt, and print a hint afterwards */
                         log_error("%s: invalid option -- '.'", program_invocation_name);
@@ -8912,6 +8891,7 @@ static int systemctl_main(int argc, char *argv[]) {
                 { "condrestart",           2,        VERB_ANY, VERB_ONLINE_ONLY, start_unit           }, /* For compatibility with RH */
                 { "isolate",               2,        2,        VERB_ONLINE_ONLY, start_unit           },
                 { "kill",                  2,        VERB_ANY, VERB_ONLINE_ONLY, kill_unit            },
+                { "clean",                 2,        VERB_ANY, VERB_ONLINE_ONLY, clean_unit           },
                 { "is-active",             2,        VERB_ANY, VERB_ONLINE_ONLY, check_unit_active    },
                 { "check",                 2,        VERB_ANY, VERB_ONLINE_ONLY, check_unit_active    }, /* deprecated alias of is-active */
                 { "is-failed",             2,        VERB_ANY, VERB_ONLINE_ONLY, check_unit_failed    },
index 175e4a23931813d43b0fd8bae261f897ce68a4eb..5df5743823d522c0562938193b8c00b5f41deac7 100644 (file)
@@ -642,7 +642,7 @@ static int load_sysv(SysvStub *s) {
         if (description) {
                 char *d;
 
-                d = strappend(s->has_lsb ? "LSB: " : "SYSV: ", description);
+                d = strjoin(s->has_lsb ? "LSB: " : "SYSV: ", description);
                 if (!d)
                         return log_oom();
 
index 07b681cf43c187efe63af6e404dbf5dc6c8fa215..61811311f4903c62e8f0f403be1fbde6c7644303 100644 (file)
@@ -30,7 +30,7 @@ static void setup_test_dir(char *tmp_dir, const char *files, ...) {
         while (files) {
                 _cleanup_free_ char *path;
 
-                assert_se(path = strappend(tmp_dir, files));
+                assert_se(path = path_join(tmp_dir, files));
                 (void) mkdir_parents(path, 0755);
                 assert_se(write_string_file(path, "foobar", WRITE_STRING_FILE_CREATE) >= 0);
 
index 731d599ae788753013ddef8b5ba62cd9570d920c..5f7b9e5ce8dca2fd30129736abd73aeb21042d09 100644 (file)
@@ -92,7 +92,7 @@ static void test_copy_tree(void) {
         STRV_FOREACH(p, files) {
                 _cleanup_free_ char *f;
 
-                assert_se(f = strappend(original_dir, *p));
+                assert_se(f = path_join(original_dir, *p));
 
                 assert_se(mkdir_parents(f, 0755) >= 0);
                 assert_se(write_string_file(f, "file", WRITE_STRING_FILE_CREATE) == 0);
@@ -101,8 +101,8 @@ static void test_copy_tree(void) {
         STRV_FOREACH_PAIR(link, p, links) {
                 _cleanup_free_ char *f, *l;
 
-                assert_se(f = strappend(original_dir, *p));
-                assert_se(l = strappend(original_dir, *link));
+                assert_se(f = path_join(original_dir, *p));
+                assert_se(l = path_join(original_dir, *link));
 
                 assert_se(mkdir_parents(l, 0755) >= 0);
                 assert_se(symlink(f, l) == 0);
@@ -117,7 +117,7 @@ static void test_copy_tree(void) {
                 _cleanup_free_ char *buf, *f;
                 size_t sz;
 
-                assert_se(f = strappend(copy_dir, *p));
+                assert_se(f = path_join(copy_dir, *p));
 
                 assert_se(access(f, F_OK) == 0);
                 assert_se(read_full_file(f, &buf, &sz) == 0);
index 701594fe538f45375e0a73520d28d244813f90b1..9809d408f641457ee2be26cc16a1dd28de422843 100644 (file)
@@ -9,12 +9,14 @@
 #include "rm-rf.h"
 #include "test-helper.h"
 #include "tests.h"
+#include "service.h"
 
 int main(int argc, char *argv[]) {
         _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
         _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
         _cleanup_(manager_freep) Manager *m = NULL;
-        Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL, *h = NULL, *unit_with_multiple_dashes = NULL;
+        Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL,
+             *h = NULL, *i = NULL, *a_conj = NULL, *unit_with_multiple_dashes = NULL;
         Job *j;
         int r;
 
@@ -94,6 +96,30 @@ int main(int argc, char *argv[]) {
         assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, NULL, NULL, &j) == 0);
         manager_dump_jobs(m, stdout, "\t");
 
+        printf("Load5:\n");
+        manager_clear_jobs(m);
+        assert_se(manager_load_startable_unit_or_warn(m, "i.service", NULL, &i) >= 0);
+        SERVICE(a)->state = SERVICE_RUNNING;
+        SERVICE(d)->state = SERVICE_RUNNING;
+        manager_dump_units(m, stdout, "\t");
+
+        printf("Test11: (Start/stop job ordering, execution cycle)\n");
+        assert_se(manager_add_job(m, JOB_START, i, JOB_FAIL, NULL, NULL, &j) == 0);
+        assert_se(a->job && a->job->type == JOB_STOP);
+        assert_se(d->job && d->job->type == JOB_STOP);
+        assert_se(b->job && b->job->type == JOB_START);
+        manager_dump_jobs(m, stdout, "\t");
+
+        printf("Load6:\n");
+        manager_clear_jobs(m);
+        assert_se(manager_load_startable_unit_or_warn(m, "a-conj.service", NULL, &a_conj) >= 0);
+        SERVICE(a)->state = SERVICE_DEAD;
+        manager_dump_units(m, stdout, "\t");
+
+        printf("Test12: (Trivial cycle, Unfixable)\n");
+        assert_se(manager_add_job(m, JOB_START, a_conj, JOB_REPLACE, NULL, NULL, &j) == -EDEADLK);
+        manager_dump_jobs(m, stdout, "\t");
+
         assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b));
         assert_se(!hashmap_get(b->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
         assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], c));
index 90a8d5f36c6f5f12367fde6fba65ef2da3631365..413816ceb8e35a16cfd5ac7683193174211130b4 100644 (file)
@@ -327,7 +327,7 @@ static void test_strv_resolve(void) {
         search_dirs = strv_new("/dir1", "/dir2", "/dir3");
         assert_se(search_dirs);
         STRV_FOREACH(d, search_dirs) {
-                char *p = strappend(tmp_dir, *d);
+                char *p = path_join(tmp_dir, *d);
                 assert_se(p);
                 assert_se(strv_push(&absolute_dirs, p) == 0);
         }
index d84170bc22231cec0c7f3107bc90742cd9314d68..8ea399436621939d8ad8c646cc245e100c713522 100644 (file)
@@ -250,22 +250,6 @@ static void test_strrep(void) {
         assert_se(streq(zero, ""));
 }
 
-static void test_strappend(void) {
-        _cleanup_free_ char *t1, *t2, *t3, *t4;
-
-        t1 = strappend(NULL, NULL);
-        assert_se(streq(t1, ""));
-
-        t2 = strappend(NULL, "suf");
-        assert_se(streq(t2, "suf"));
-
-        t3 = strappend("pre", NULL);
-        assert_se(streq(t3, "pre"));
-
-        t4 = strappend("pre", "suf");
-        assert_se(streq(t4, "presuf"));
-}
-
 static void test_string_has_cc(void) {
         assert_se(string_has_cc("abc\1", NULL));
         assert_se(string_has_cc("abc\x7f", NULL));
@@ -568,7 +552,6 @@ int main(int argc, char *argv[]) {
         test_strextend();
         test_strextend_with_separator();
         test_strrep();
-        test_strappend();
         test_string_has_cc();
         test_ascii_strlower();
         test_strshorten();
index fe91854ac8ca836775e381da5186bae2a1d9b34a..57bbefc0c10fc9c8ca3a2b03e20826acc748895f 100644 (file)
@@ -227,7 +227,7 @@ static int context_write_data_timezone(Context *c) {
                 return r;
         }
 
-        p = strappend("../usr/share/zoneinfo/", c->zone);
+        p = path_join("../usr/share/zoneinfo", c->zone);
         if (!p)
                 return log_oom();
 
index ef817fca4f7024157003c39b7e17d403ad72573d..3c30612af1aaea1912797a5265f1205d9ed009e7 100644 (file)
@@ -2597,7 +2597,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
 
         case CREATE_SYMLINK:
                 if (!i.argument) {
-                        i.argument = strappend("/usr/share/factory/", i.path);
+                        i.argument = path_join("/usr/share/factory", i.path);
                         if (!i.argument)
                                 return log_oom();
                 }
@@ -2613,13 +2613,22 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
 
         case COPY_FILES:
                 if (!i.argument) {
-                        i.argument = strappend("/usr/share/factory/", i.path);
+                        i.argument = path_join(arg_root, "/usr/share/factory", i.path);
                         if (!i.argument)
                                 return log_oom();
+
                 } else if (!path_is_absolute(i.argument)) {
                         *invalid_config = true;
                         log_error("[%s:%u] Source path is not absolute.", fname, line);
                         return -EBADMSG;
+
+                } else if (arg_root) {
+                        char *p;
+
+                        p = path_join(arg_root, i.argument);
+                        if (!p)
+                                return log_oom();
+                        free_and_replace(i.argument, p);
                 }
 
                 path_simplify(i.argument, false);
@@ -2713,7 +2722,6 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
                 p = path_join(arg_root, i.path);
                 if (!p)
                         return log_oom();
-
                 free_and_replace(i.path, p);
         }
 
index 843c302bf4ba1dba04336084cba99a535650d630..e17140ea0ce79926ba07be3742565285f71385bd 100644 (file)
@@ -480,7 +480,7 @@ static int show_passwords(void) {
                 if (!startswith(de->d_name, "ask."))
                         continue;
 
-                p = strappend("/run/systemd/ask-password/", de->d_name);
+                p = path_join("/run/systemd/ask-password", de->d_name);
                 if (!p)
                         return log_oom();
 
index efbb0732f1a32ed88d63da2faeaf0025800c7b64..7ef75e6f919fc05b04c652574d75478718991fd2 100644 (file)
@@ -20,6 +20,7 @@
 #include "blkid-util.h"
 #include "device-util.h"
 #include "efivars.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "gpt.h"
 #include "parse-util.h"
@@ -113,7 +114,7 @@ static int find_gpt_root(sd_device *dev, blkid_probe pr, bool test) {
         errno = 0;
         pl = blkid_probe_get_partitions(pr);
         if (!pl)
-                return -errno ?: -ENOMEM;
+                return errno_or_else(ENOMEM);
 
         nvals = blkid_partlist_numof_partitions(pl);
         for (i = 0; i < nvals; i++) {
index 9078a3c2ce21aa81abec512b3605fc10b34c2132..2c8626ffad2f06d5dd1097f5dcce43a739bb164c 100644 (file)
@@ -467,9 +467,14 @@ int info_main(int argc, char *argv[], void *userdata) {
                         return log_error_errno(r, "Unknown device \"%s\": %m",  *p);
 
                 if (arg_wait_for_initialization_timeout > 0) {
-                        r = device_wait_for_initialization(device, NULL, arg_wait_for_initialization_timeout, NULL);
+                        sd_device *d;
+
+                        r = device_wait_for_initialization(device, NULL, arg_wait_for_initialization_timeout, &d);
                         if (r < 0)
                                 return r;
+
+                        sd_device_unref(device);
+                        device = d;
                 }
 
                 if (action == ACTION_QUERY)
diff --git a/test/TEST-33-CLEAN-UNIT/Makefile b/test/TEST-33-CLEAN-UNIT/Makefile
new file mode 120000 (symlink)
index 0000000..e9f93b1
--- /dev/null
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-33-CLEAN-UNIT/test.sh b/test/TEST-33-CLEAN-UNIT/test.sh
new file mode 100755 (executable)
index 0000000..44082af
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -e
+TEST_DESCRIPTION="test CleanUnit"
+
+. $TEST_BASE_DIR/test-functions
+
+test_setup() {
+    create_empty_image
+    mkdir -p $TESTDIR/root
+    mount ${LOOPDEV}p1 $TESTDIR/root
+
+    (
+        LOG_LEVEL=5
+        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
+
+        setup_basic_environment
+
+        # mask some services that we do not want to run in these tests
+        ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+        ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+        ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+        ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+        ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+        ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
+
+        # setup the testsuite service
+        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
+[Unit]
+Description=Testsuite service
+
+[Service]
+ExecStart=/bin/bash -x /testsuite.sh
+Type=oneshot
+StandardOutput=tty
+StandardError=tty
+EOF
+        cp testsuite.sh $initdir/
+
+        setup_testsuite
+    ) || return 1
+    setup_nspawn_root
+
+    ddebug "umount $TESTDIR/root"
+    umount $TESTDIR/root
+}
+
+do_test "$@"
diff --git a/test/TEST-33-CLEAN-UNIT/testsuite.sh b/test/TEST-33-CLEAN-UNIT/testsuite.sh
new file mode 100755 (executable)
index 0000000..15aa687
--- /dev/null
@@ -0,0 +1,79 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -ex
+set -o pipefail
+
+cat > /etc/systemd/system/testservice.service <<EOF
+[Service]
+ConfigurationDirectory=testservice
+RuntimeDirectory=testservice
+StateDirectory=testservice
+CacheDirectory=testservice
+LogsDirectory=testservice
+RuntimeDirectoryPreserve=yes
+ExecStart=/bin/sleep infinity
+Type=exec
+EOF
+
+systemctl daemon-reload
+
+! test -e /etc/testservice
+! test -e /run/testservice
+! test -e /var/lib/testservice
+! test -e /var/cache/testservice
+! test -e /var/log/testservice
+
+systemctl start testservice
+
+test -d /etc/testservice
+test -d /run/testservice
+test -d /var/lib/testservice
+test -d /var/cache/testservice
+test -d /var/log/testservice
+
+! systemctl clean testservice
+
+systemctl stop testservice
+
+test -d /etc/testservice
+test -d /run/testservice
+test -d /var/lib/testservice
+test -d /var/cache/testservice
+test -d /var/log/testservice
+
+systemctl clean testservice --what=configuration
+
+! test -e /etc/testservice
+test -d /run/testservice
+test -d /var/lib/testservice
+test -d /var/cache/testservice
+test -d /var/log/testservice
+
+systemctl clean testservice
+
+! test -e /etc/testservice
+! test -e /run/testservice
+test -d /var/lib/testservice
+! test -e /var/cache/testservice
+test -d /var/log/testservice
+
+systemctl clean testservice --what=logs
+
+! test -e /etc/testservice
+! test -e /run/testservice
+test -d /var/lib/testservice
+! test -e /var/cache/testservice
+! test -e /var/log/testservice
+
+systemctl clean testservice --what=all
+
+! test -e /etc/testservice
+! test -e /run/testservice
+! test -e /var/lib/testservice
+! test -e /var/cache/testservice
+! test -e /var/log/testservice
+
+echo OK > /testok
+
+exit 0
diff --git a/test/a-conj.service b/test/a-conj.service
new file mode 100644 (file)
index 0000000..db37ae7
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=A conjugate
+Requires=a.service
+After=a.service
+Before=a.service
+
+[Service]
+ExecStart=/bin/true
index 07e54d9e44f5d0c201c47407ccc981aad4a4d7af..874c3e5f8ff22ae3521402a2e4dcdbc92d2fe915 100644 (file)
@@ -66,6 +66,7 @@ AllowLocalRemote=
 Local=
 TOS=
 Independent=
+AssignToLoopback=
 Key=
 InputKey=
 Encapsulation=
index ddafaa7840cc528b417017276b80f4cb596d66a0..69a4281542773551558416bcb61db0ba89b6a921 100644 (file)
@@ -66,6 +66,38 @@ UseTimezone=
 RouteTable=
 BlackList=
 SendRelease=
+MaxAttempts=
+[DHCPv4]
+UseDNS=
+UseDomains=
+UseRoutes=
+IAID=
+UserClass=
+UseNTP=
+UseMTU=
+UseDomainName=
+RouteMetric=
+SendHostname=
+Anonymize=
+VendorClassIdentifier=
+Hostname=
+DUIDType=
+UseHostname=
+CriticalConnection=
+DUIDRawData=
+RequestBroadcast=
+ClientIdentifier=
+ListenPort=
+UseTimezone=
+RouteTable=
+BlackList=
+SendRelease=
+MaxAttempts=
+[DHCPv6]
+UseNTP=
+UseDNS=
+RapidCommit=
+ForceDHCPv6PDOtherInformation=
 [Route]
 Destination=
 Protocol=
@@ -191,6 +223,7 @@ Managed=
 OtherInformation=
 [Neighbor]
 Address=
+LinkLayerAddress=
 MACAddress=
 [IPv6AddressLabel]
 Label=
@@ -214,10 +247,3 @@ MaxLeaseTimeSec=
 DefaultLeaseTimeSec=
 EmitTimezone=
 DNS=
-MaxAttempts=
-[DHCPv4]
-UseHostname=
-UseMTU=
-UseDomainName=
-CriticalConnection=
-UseDNS=
diff --git a/test/i.service b/test/i.service
new file mode 100644 (file)
index 0000000..938ea77
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=I
+Conflicts=a.service d.service
+Wants=b.service
+After=b.service
+
+[Service]
+ExecStart=/bin/true
index 17d0f3cddddeea790c5e14e3f37041a20993459d..8c71e726678cf420d6a5a11220a429d7d1e266e1 100644 (file)
@@ -2,6 +2,7 @@
 
 test_data_files = '''
         a.service
+        a-conj.service
         b.service
         basic.target
         c.service
@@ -26,6 +27,7 @@ test_data_files = '''
         hello-after-sleep.target
         hello.service
         hwdb/10-bad.hwdb
+        i.service
         journal-data/journal-1.txt
         journal-data/journal-2.txt
         nomem.slice
diff --git a/test/test-network/conf/25-gre-tunnel-any-any.netdev b/test/test-network/conf/25-gre-tunnel-any-any.netdev
new file mode 100644 (file)
index 0000000..3467b16
--- /dev/null
@@ -0,0 +1,9 @@
+[NetDev]
+Name=gretun96
+Kind=gre
+
+[Tunnel]
+Local=any
+Remote=any
+Key=106
+SerializeTunneledPackets=false
diff --git a/test/test-network/conf/25-ip6gre-tunnel-any-any.netdev b/test/test-network/conf/25-ip6gre-tunnel-any-any.netdev
new file mode 100644 (file)
index 0000000..519474f
--- /dev/null
@@ -0,0 +1,7 @@
+[NetDev]
+Name=ip6gretun96
+Kind=ip6gre
+
+[Tunnel]
+Local=any
+Remote=any
diff --git a/test/test-network/conf/25-ipip-tunnel-any-any.netdev b/test/test-network/conf/25-ipip-tunnel-any-any.netdev
new file mode 100644 (file)
index 0000000..8803dd1
--- /dev/null
@@ -0,0 +1,8 @@
+[NetDev]
+Name=ipiptun96
+Kind=ipip
+MTUBytes=1480
+
+[Tunnel]
+Local=any
+Remote=any
diff --git a/test/test-network/conf/25-ipip-tunnel-independent-loopback.netdev b/test/test-network/conf/25-ipip-tunnel-independent-loopback.netdev
new file mode 100644 (file)
index 0000000..9ee26c9
--- /dev/null
@@ -0,0 +1,10 @@
+[NetDev]
+Name=ipiptun99
+Kind=ipip
+MTUBytes=1480
+
+[Tunnel]
+Local=192.168.223.238
+Remote=192.169.224.239
+Independent=true
+AssignToLoopback=yes
diff --git a/test/test-network/conf/25-neighbor-ip-dummy.network b/test/test-network/conf/25-neighbor-ip-dummy.network
new file mode 100644 (file)
index 0000000..d99d972
--- /dev/null
@@ -0,0 +1,6 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Tunnel=gretun97
diff --git a/test/test-network/conf/25-neighbor-ip.network b/test/test-network/conf/25-neighbor-ip.network
new file mode 100644 (file)
index 0000000..8865ccd
--- /dev/null
@@ -0,0 +1,10 @@
+[Match]
+Name=gretun97
+
+[Network]
+IPv6AcceptRA=no
+Address=10.0.0.21
+
+[Neighbor]
+Address=10.0.0.22
+LinkLayerAddress=10.65.223.239
index 02dbd3843bf4732b8d2eff8b9ea28fdd8589be1d..94c04943b717b41173d5503db5b7204c0f57e52d 100644 (file)
@@ -6,8 +6,8 @@ IPv6AcceptRA=no
 
 [Neighbor]
 Address=192.168.10.1
-MACAddress=00:00:5e:00:02:65
+LinkLayerAddress=00:00:5e:00:02:65
 
 [Neighbor]
 Address=2004:da8:1:0::1
-MACAddress=00:00:5e:00:02:66
+LinkLayerAddress=00:00:5e:00:02:66
index 9e12480a658dd48761e0e6a9852472382077b805..9c8269e3b883ed77a8a960cc94db2f38adbb3658 100644 (file)
@@ -46,3 +46,19 @@ Destination=202.54.1.3
 [Route]
 Type=prohibit
 Destination=202.54.1.4
+
+[Route]
+Type=local
+Destination=149.10.123.1
+
+[Route]
+Type=anycast
+Destination=149.10.123.2
+
+[Route]
+Type=broadcast
+Destination=149.10.123.3
+
+[Route]
+Type=multicast
+Destination=149.10.123.4
diff --git a/test/test-network/conf/25-sit-tunnel-any-any.netdev b/test/test-network/conf/25-sit-tunnel-any-any.netdev
new file mode 100644 (file)
index 0000000..e961dcb
--- /dev/null
@@ -0,0 +1,7 @@
+[NetDev]
+Name=sittun96
+Kind=sit
+
+[Tunnel]
+Local=any
+Remote=any
diff --git a/test/test-network/conf/25-tunnel-any-any.network b/test/test-network/conf/25-tunnel-any-any.network
new file mode 100644 (file)
index 0000000..7a9e39e
--- /dev/null
@@ -0,0 +1,8 @@
+[Match]
+Name=*tun96
+
+[Network]
+IPv6AcceptRA=no
+Address=2001:db8:0:f102::19/64
+Address=10.3.2.6/16
+LinkLocalAddressing=yes
diff --git a/test/test-network/conf/25-vti-tunnel-any-any.netdev b/test/test-network/conf/25-vti-tunnel-any-any.netdev
new file mode 100644 (file)
index 0000000..3cac374
--- /dev/null
@@ -0,0 +1,7 @@
+[NetDev]
+Name=vtitun96
+Kind=vti
+
+[Tunnel]
+Local=any
+Remote=any
diff --git a/test/test-network/conf/25-xfrm-independent.netdev b/test/test-network/conf/25-xfrm-independent.netdev
new file mode 100644 (file)
index 0000000..8ed4321
--- /dev/null
@@ -0,0 +1,6 @@
+[NetDev]
+Kind=xfrm
+Name=xfrm99
+
+[Xfrm]
+Independent=yes
diff --git a/test/test-network/conf/25-xfrm.netdev b/test/test-network/conf/25-xfrm.netdev
new file mode 100644 (file)
index 0000000..81b32de
--- /dev/null
@@ -0,0 +1,3 @@
+[NetDev]
+Kind=xfrm
+Name=xfrm99
index 1f8c5b56482378c856beaaffb46a0d09a93c64c4..6aed1785258dcdc821f717c17583afe827d6642c 100644 (file)
@@ -13,3 +13,6 @@ MulticastFlood = false
 MulticastToUnicast = true
 NeighborSuppression = true
 Learning = false
+Priority = 23
+UseBPDU = true
+AllowPortToBeRoot=true
index 45ec2de999295517ebf4fe3a74868b7d05843ec7..5fccfec5d129938760b2f519b98d6f182b7e6444 100644 (file)
@@ -3,3 +3,6 @@ Name=test1
 
 [Network]
 Bridge=bridge99
+
+[Bridge]
+Priority=0
diff --git a/test/test-network/conf/dhcp-client-reassign-static-routes-ipv6.network b/test/test-network/conf/dhcp-client-reassign-static-routes-ipv6.network
new file mode 100644 (file)
index 0000000..c662e99
--- /dev/null
@@ -0,0 +1,8 @@
+[Match]
+Name=veth99
+
+[Network]
+IPv6AcceptRA=yes
+
+[Route]
+Destination=2600:0:0:1::/64
diff --git a/test/test-network/conf/dhcp-client-use-dns-ipv4-and-ra.network b/test/test-network/conf/dhcp-client-use-dns-ipv4-and-ra.network
new file mode 100644 (file)
index 0000000..ed59895
--- /dev/null
@@ -0,0 +1,11 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+
+[DHCP]
+UseDNS=yes
+
+[IPv6AcceptRA]
+UseDNS=yes
diff --git a/test/test-network/conf/dhcp-client-use-dns-ipv4.network b/test/test-network/conf/dhcp-client-use-dns-ipv4.network
new file mode 100644 (file)
index 0000000..dbd0f7f
--- /dev/null
@@ -0,0 +1,14 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+
+[DHCPv4]
+UseDNS=yes
+
+[DHCPv6]
+UseDNS=no
+
+[IPv6AcceptRA]
+UseDNS=no
diff --git a/test/test-network/conf/dhcp-client-use-dns-no.network b/test/test-network/conf/dhcp-client-use-dns-no.network
new file mode 100644 (file)
index 0000000..891efdb
--- /dev/null
@@ -0,0 +1,11 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=yes
+
+[DHCP]
+UseDNS=no
+
+[IPv6AcceptRA]
+UseDNS=no
diff --git a/test/test-network/conf/dhcp-client-use-dns-yes.network b/test/test-network/conf/dhcp-client-use-dns-yes.network
new file mode 100644 (file)
index 0000000..f38ffe3
--- /dev/null
@@ -0,0 +1,11 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=yes
+
+[DHCP]
+UseDNS=yes
+
+[IPv6AcceptRA]
+UseDNS=no
index 00bb03cab25d1c855da82d467ed92f30fd103ac5..5510b1c9b6544ebe4110255889fa58d94f8aec9c 100644 (file)
@@ -5,3 +5,4 @@ Name=dummy98
 Tunnel=gretun99
 Tunnel=gretun98
 Tunnel=gretun97
+Tunnel=gretun96
index 6d39bbd777d688beeef528af4b55df725af130be..8fbee98851ec185d1001d7d0194bbc0d2dbf9fd2 100644 (file)
@@ -5,3 +5,4 @@ Name=dummy98
 Tunnel=ip6gretun99
 Tunnel=ip6gretun98
 Tunnel=ip6gretun97
+Tunnel=ip6gretun96
index ec6c9581145143800a098fac38119af6b30ad866..ea4b3a13533604c84ec67067f565d41a84d20b00 100644 (file)
@@ -5,3 +5,4 @@ Name=dummy98
 Tunnel=ipiptun99
 Tunnel=ipiptun98
 Tunnel=ipiptun97
+Tunnel=ipiptun96
index b76bfb3bd3bfcdf8509d205fa45068c80e35f6fb..d7dfa7ea97b6338a1948d120ac9068e7c2aa0972 100644 (file)
@@ -12,6 +12,7 @@ Name=vrf99
 Name=geneve99
 Name=ipiptun99
 Name=nlmon99
+Name=xfrm99
 
 [Network]
 LinkLocalAddressing=yes
index 8d97823a5ad5eabea50486669fdcac57822a3793..79909fcd6befd2c1a1d4ff77e96cd0dea06e1d78 100644 (file)
@@ -5,3 +5,4 @@ Name=dummy98
 Tunnel=sittun99
 Tunnel=sittun98
 Tunnel=sittun97
+Tunnel=sittun96
index 1e0b8405c5c61d6c3b0e2f6678425ba2f6446e58..761362e48221daeb5493a518e9d0d2049d8ffa6b 100644 (file)
@@ -5,3 +5,4 @@ Name=dummy98
 Tunnel=vtitun99
 Tunnel=vtitun98
 Tunnel=vtitun97
+Tunnel=vtitun96
diff --git a/test/test-network/conf/xfrm.network b/test/test-network/conf/xfrm.network
new file mode 100644 (file)
index 0000000..bfb2956
--- /dev/null
@@ -0,0 +1,6 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Xfrm=xfrm99
index 92f1a5bd8dbeef5d34f407d253722c8e3307306f..e396af05dae0e9401fe12235fd2a142ffbb0787a 100755 (executable)
@@ -23,8 +23,11 @@ dnsmasq_pid_file='/run/networkd-ci/test-test-dnsmasq.pid'
 dnsmasq_log_file='/run/networkd-ci/test-dnsmasq-log-file'
 
 networkd_bin='/usr/lib/systemd/systemd-networkd'
+resolved_bin='/usr/lib/systemd/systemd-resolved'
 wait_online_bin='/usr/lib/systemd/systemd-networkd-wait-online'
 networkctl_bin='/usr/bin/networkctl'
+resolvectl_bin='/usr/bin/resolvectl'
+timedatectl_bin='/usr/bin/timedatectl'
 use_valgrind=False
 enable_debug=True
 env = {}
@@ -108,23 +111,6 @@ def expectedFailureIfLinkFileFieldIsNotSet():
 
     return f
 
-def expectedFailureIfEthtoolDoesNotSupportDriver():
-    def f(func):
-        support = False
-        rc = call('ip link add name dummy99 type dummy')
-        if rc == 0:
-            ret = run('udevadm info -w10s /sys/class/net/dummy99', stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-            if ret.returncode == 0 and 'E: ID_NET_DRIVER=dummy' in ret.stdout.rstrip():
-                support = True
-            call('ip link del dummy99')
-
-        if support:
-            return func
-        else:
-            return unittest.expectedFailure(func)
-
-    return f
-
 def setUpModule():
     os.makedirs(network_unit_file_path, exist_ok=True)
     os.makedirs(networkd_ci_path, exist_ok=True)
@@ -134,6 +120,7 @@ def setUpModule():
 
     check_output('systemctl stop systemd-networkd.socket')
     check_output('systemctl stop systemd-networkd.service')
+    check_output('systemctl stop systemd-resolved.service')
 
     drop_in = [
         '[Service]',
@@ -164,19 +151,49 @@ def setUpModule():
     with open('/run/systemd/system/systemd-networkd.service.d/00-override.conf', mode='w') as f:
         f.write('\n'.join(drop_in))
 
+    drop_in = [
+        '[Service]',
+        'Restart=no',
+        'ExecStart=',
+    ]
+    if use_valgrind:
+        drop_in += ['ExecStart=!!valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all ' + resolved_bin]
+    else:
+        drop_in += ['ExecStart=!!' + resolved_bin]
+    if enable_debug:
+        drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
+    if asan_options:
+        drop_in += ['Environment=ASAN_OPTIONS="' + asan_options + '"']
+    if lsan_options:
+        drop_in += ['Environment=LSAN_OPTIONS="' + lsan_options + '"']
+    if ubsan_options:
+        drop_in += ['Environment=UBSAN_OPTIONS="' + ubsan_options + '"']
+    if asan_options or lsan_options or ubsan_options:
+        drop_in += ['SystemCallFilter=']
+    if use_valgrind or asan_options or lsan_options or ubsan_options:
+        drop_in += ['MemoryDenyWriteExecute=no']
+
+    os.makedirs('/run/systemd/system/systemd-resolved.service.d', exist_ok=True)
+    with open('/run/systemd/system/systemd-resolved.service.d/00-override.conf', mode='w') as f:
+        f.write('\n'.join(drop_in))
+
     check_output('systemctl daemon-reload')
     print(check_output('systemctl cat systemd-networkd.service'))
+    print(check_output('systemctl cat systemd-resolved.service'))
+    check_output('systemctl restart systemd-resolved')
 
 def tearDownModule():
     shutil.rmtree(networkd_ci_path)
 
     check_output('systemctl stop systemd-networkd.service')
+    check_output('systemctl stop systemd-resolved.service')
 
     shutil.rmtree('/run/systemd/system/systemd-networkd.service.d')
+    shutil.rmtree('/run/systemd/system/systemd-resolved.service.d')
     check_output('systemctl daemon-reload')
 
     check_output('systemctl start systemd-networkd.socket')
-    check_output('systemctl start systemd-networkd.service')
+    check_output('systemctl start systemd-resolved.service')
 
 def read_link_attr(link, dev, attribute):
     with open(os.path.join(os.path.join(os.path.join('/sys/class/net/', link), dev), attribute)) as f:
@@ -430,23 +447,6 @@ class NetworkctlTests(unittest.TestCase, Utilities):
         self.assertRegex(output, r'Link File: (?:/usr)/lib/systemd/network/99-default.link')
         self.assertRegex(output, r'Network File: n/a')
 
-    @expectedFailureIfEthtoolDoesNotSupportDriver()
-    def test_udev_driver(self):
-        copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network',
-                                        '25-veth.netdev', 'netdev-link-local-addressing-yes.network')
-        start_networkd()
-
-        wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded'])
-
-        output = check_output(*networkctl_cmd, 'status', 'test1', env=env)
-        self.assertRegex(output, 'Driver: dummy')
-
-        output = check_output(*networkctl_cmd, 'status', 'veth99', env=env)
-        self.assertRegex(output, 'Driver: veth')
-
-        output = check_output(*networkctl_cmd, 'status', 'veth-peer', env=env)
-        self.assertRegex(output, 'Driver: veth')
-
     def test_delete_links(self):
         copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network',
                                         '25-veth.netdev', 'netdev-link-local-addressing-yes.network')
@@ -461,7 +461,11 @@ class NetworkctlTests(unittest.TestCase, Utilities):
 
 class NetworkdNetDevTests(unittest.TestCase, Utilities):
 
-    links =[
+    links_remove_earlier = [
+        'xfrm99',
+    ]
+
+    links = [
         '6rdtun99',
         'bond99',
         'bridge99',
@@ -479,6 +483,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         'gretun99',
         'ip6gretap98',
         'ip6gretap99',
+        'ip6gretun96',
         'ip6gretun97',
         'ip6gretun98',
         'ip6gretun99',
@@ -509,13 +514,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         'vti6tun97',
         'vti6tun98',
         'vti6tun99',
+        'vtitun96',
         'vtitun97',
         'vtitun98',
         'vtitun99',
         'vxcan99',
         'vxlan99',
         'wg98',
-        'wg99']
+        'wg99',
+    ]
 
     units = [
         '10-dropin-test.netdev',
@@ -547,18 +554,23 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         '25-geneve.netdev',
         '25-gretap-tunnel-local-any.netdev',
         '25-gretap-tunnel.netdev',
+        '25-gre-tunnel-any-any.netdev',
         '25-gre-tunnel-local-any.netdev',
         '25-gre-tunnel-remote-any.netdev',
         '25-gre-tunnel.netdev',
         '25-ip6gretap-tunnel-local-any.netdev',
         '25-ip6gretap-tunnel.netdev',
+        '25-ip6gre-tunnel-any-any.netdev',
         '25-ip6gre-tunnel-local-any.netdev',
         '25-ip6gre-tunnel-remote-any.netdev',
         '25-ip6gre-tunnel.netdev',
-        '25-ip6tnl-tunnel-remote-any.netdev',
+        '25-ip6tnl-tunnel-any-any.netdev',
         '25-ip6tnl-tunnel-local-any.netdev',
+        '25-ip6tnl-tunnel-remote-any.netdev',
         '25-ip6tnl-tunnel.netdev',
+        '25-ipip-tunnel-any-any.netdev',
         '25-ipip-tunnel-independent.netdev',
+        '25-ipip-tunnel-independent-loopback.netdev',
         '25-ipip-tunnel-local-any.netdev',
         '25-ipip-tunnel-remote-any.netdev',
         '25-ipip-tunnel.netdev',
@@ -569,6 +581,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         '25-macsec.netdev',
         '25-macsec.network',
         '25-nlmon.netdev',
+        '25-sit-tunnel-any-any.netdev',
         '25-sit-tunnel-local-any.netdev',
         '25-sit-tunnel-remote-any.netdev',
         '25-sit-tunnel.netdev',
@@ -580,9 +593,11 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         '25-vcan.netdev',
         '25-veth.netdev',
         '25-vrf.netdev',
+        '25-vti6-tunnel-any-any.netdev',
         '25-vti6-tunnel-local-any.netdev',
         '25-vti6-tunnel-remote-any.netdev',
         '25-vti6-tunnel.netdev',
+        '25-vti-tunnel-any-any.netdev',
         '25-vti-tunnel-local-any.netdev',
         '25-vti-tunnel-remote-any.netdev',
         '25-vti-tunnel.netdev',
@@ -594,6 +609,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         '25-wireguard-private-key.txt',
         '25-wireguard.netdev',
         '25-wireguard.network',
+        '25-xfrm.netdev',
+        '25-xfrm-independent.netdev',
         '6rd.network',
         'erspan.network',
         'gre.network',
@@ -614,7 +631,9 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         'vti6.network',
         'vti.network',
         'vxlan-test1.network',
-        'vxlan.network']
+        'vxlan.network',
+        'xfrm.network',
+    ]
 
     fou_ports = [
         '55555',
@@ -622,11 +641,13 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
 
     def setUp(self):
         remove_fou_ports(self.fou_ports)
+        remove_links(self.links_remove_earlier)
         remove_links(self.links)
         stop_networkd(show_logs=False)
 
     def tearDown(self):
         remove_fou_ports(self.fou_ports)
+        remove_links(self.links_remove_earlier)
         remove_links(self.links)
         remove_unit_from_networkd_path(self.units)
         stop_networkd(show_logs=True)
@@ -904,9 +925,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ipip.network',
                                         '25-ipip-tunnel.netdev', '25-tunnel.network',
                                         '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
-                                        '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
+                                        '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
+                                        '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
         start_networkd()
-        wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'dummy98:degraded'])
+        wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded'])
 
         output = check_output('ip -d link show ipiptun99')
         print(output)
@@ -917,14 +939,18 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         output = check_output('ip -d link show ipiptun97')
         print(output)
         self.assertRegex(output, 'ipip (?:ipip |)remote any local 192.168.223.238 dev dummy98')
+        output = check_output('ip -d link show ipiptun96')
+        print(output)
+        self.assertRegex(output, 'ipip (?:ipip |)remote any local any dev dummy98')
 
     def test_gre_tunnel(self):
         copy_unit_to_networkd_unit_path('12-dummy.netdev', 'gretun.network',
                                         '25-gre-tunnel.netdev', '25-tunnel.network',
                                         '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
-                                        '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
+                                        '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
+                                        '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
         start_networkd()
-        wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'dummy98:degraded'])
+        wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded'])
 
         output = check_output('ip -d link show gretun99')
         print(output)
@@ -947,12 +973,20 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'okey 0.0.0.105')
         self.assertNotRegex(output, 'iseq')
         self.assertNotRegex(output, 'oseq')
+        output = check_output('ip -d link show gretun96')
+        print(output)
+        self.assertRegex(output, 'gre remote any local any dev dummy98')
+        self.assertRegex(output, 'ikey 0.0.0.106')
+        self.assertRegex(output, 'okey 0.0.0.106')
+        self.assertNotRegex(output, 'iseq')
+        self.assertNotRegex(output, 'oseq')
 
     def test_ip6gre_tunnel(self):
         copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ip6gretun.network',
                                         '25-ip6gre-tunnel.netdev', '25-tunnel.network',
                                         '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
-                                        '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
+                                        '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
+                                        '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
         start_networkd(5)
 
         # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
@@ -961,6 +995,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         self.check_link_exists('ip6gretun99')
         self.check_link_exists('ip6gretun98')
         self.check_link_exists('ip6gretun97')
+        self.check_link_exists('ip6gretun96')
 
         output = check_output('ip -d link show ip6gretun99')
         print(output)
@@ -971,6 +1006,9 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         output = check_output('ip -d link show ip6gretun97')
         print(output)
         self.assertRegex(output, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
+        output = check_output('ip -d link show ip6gretun96')
+        print(output)
+        self.assertRegex(output, 'ip6gre remote any local any dev dummy98')
 
     def test_gretap_tunnel(self):
         copy_unit_to_networkd_unit_path('12-dummy.netdev', 'gretap.network',
@@ -1012,9 +1050,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         copy_unit_to_networkd_unit_path('12-dummy.netdev', 'vti.network',
                                         '25-vti-tunnel.netdev', '25-tunnel.network',
                                         '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
-                                        '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
+                                        '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
+                                        '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
         start_networkd()
-        wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'dummy98:degraded'])
+        wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded'])
 
         output = check_output('ip -d link show vtitun99')
         print(output)
@@ -1025,6 +1064,9 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         output = check_output('ip -d link show vtitun97')
         print(output)
         self.assertRegex(output, 'vti remote any local 10.65.223.238 dev dummy98')
+        output = check_output('ip -d link show vtitun96')
+        print(output)
+        self.assertRegex(output, 'vti remote any local any dev dummy98')
 
     def test_vti6_tunnel(self):
         copy_unit_to_networkd_unit_path('12-dummy.netdev', 'vti6.network',
@@ -1066,9 +1108,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         copy_unit_to_networkd_unit_path('12-dummy.netdev', 'sit.network',
                                         '25-sit-tunnel.netdev', '25-tunnel.network',
                                         '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
-                                        '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
+                                        '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
+                                        '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
         start_networkd()
-        wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'dummy98:degraded'])
+        wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded'])
 
         output = check_output('ip -d link show sittun99')
         print(output)
@@ -1079,6 +1122,9 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         output = check_output('ip -d link show sittun97')
         print(output)
         self.assertRegex(output, "sit (?:ip6ip |)remote any local 10.65.223.238 dev dummy98")
+        output = check_output('ip -d link show sittun96')
+        print(output)
+        self.assertRegex(output, "sit (?:ip6ip |)remote any local any dev dummy98")
 
     def test_isatap_tunnel(self):
         copy_unit_to_networkd_unit_path('12-dummy.netdev', 'isatap.network',
@@ -1130,6 +1176,30 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
 
         wait_online(['ipiptun99:carrier'])
 
+    def test_tunnel_independent_loopback(self):
+        copy_unit_to_networkd_unit_path('25-ipip-tunnel-independent-loopback.netdev', 'netdev-link-local-addressing-yes.network')
+        start_networkd()
+
+        wait_online(['ipiptun99:carrier'])
+
+    @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
+    def test_xfrm(self):
+        copy_unit_to_networkd_unit_path('12-dummy.netdev', 'xfrm.network',
+                                        '25-xfrm.netdev', 'netdev-link-local-addressing-yes.network')
+        start_networkd()
+
+        wait_online(['xfrm99:degraded', 'dummy98:degraded'])
+
+        output = check_output('ip link show dev xfrm99')
+        print(output)
+
+    @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
+    def test_xfrm_independent(self):
+        copy_unit_to_networkd_unit_path('25-xfrm-independent.netdev', 'netdev-link-local-addressing-yes.network')
+        start_networkd()
+
+        wait_online(['xfrm99:degraded'])
+
     @expectedFailureIfModuleIsNotAvailable('fou')
     def test_fou(self):
         # The following redundant check is necessary for CentOS CI.
@@ -1306,7 +1376,9 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         'bond199',
         'dummy98',
         'dummy99',
-        'test1']
+        'gretun97',
+        'test1'
+    ]
 
     units = [
         '11-dummy.netdev',
@@ -1321,8 +1393,11 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         '25-bond-active-backup-slave.netdev',
         '25-fibrule-invert.network',
         '25-fibrule-port-range.network',
+        '25-gre-tunnel-remote-any.netdev',
         '25-ipv6-address-label-section.network',
         '25-neighbor-section.network',
+        '25-neighbor-ip-dummy.network',
+        '25-neighbor-ip.network',
         '25-link-local-addressing-no.network',
         '25-link-local-addressing-yes.network',
         '25-link-section-unmanaged.network',
@@ -1477,14 +1552,18 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         start_networkd()
         wait_online(['dummy98:routable'])
 
+        print('### ip -6 route show dev dummy98')
         output = check_output('ip -6 route show dev dummy98')
         print(output)
         self.assertRegex(output, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
         self.assertRegex(output, '2001:1234:5:8f63::1 proto kernel')
 
+        print('### ip -6 route show dev dummy98 default')
         output = check_output('ip -6 route show dev dummy98 default')
+        print(output)
         self.assertRegex(output, 'default via 2001:1234:5:8fff:ff:ff:ff:ff proto static metric 1024 pref medium')
 
+        print('### ip -4 route show dev dummy98')
         output = check_output('ip -4 route show dev dummy98')
         print(output)
         self.assertRegex(output, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
@@ -1492,20 +1571,33 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, '169.254.0.0/16 proto static scope link metric 2048')
         self.assertRegex(output, '192.168.1.1 proto static initcwnd 20')
         self.assertRegex(output, '192.168.1.2 proto static initrwnd 30')
+        self.assertRegex(output, 'multicast 149.10.123.4 proto static')
 
+        print('### ip -4 route show dev dummy98 default')
         output = check_output('ip -4 route show dev dummy98 default')
+        print(output)
         self.assertRegex(output, 'default via 149.10.125.65 proto static onlink')
         self.assertRegex(output, 'default via 149.10.124.64 proto static')
         self.assertRegex(output, 'default proto static')
 
+        print('### ip -4 route show table local dev dummy98')
+        output = check_output('ip -4 route show table local dev dummy98')
+        print(output)
+        self.assertRegex(output, 'local 149.10.123.1 proto static scope host')
+        self.assertRegex(output, 'anycast 149.10.123.2 proto static scope link')
+        self.assertRegex(output, 'broadcast 149.10.123.3 proto static scope link')
+
+        print('### ip route show type blackhole')
         output = check_output('ip route show type blackhole')
         print(output)
         self.assertRegex(output, 'blackhole 202.54.1.2 proto static')
 
+        print('### ip route show type unreachable')
         output = check_output('ip route show type unreachable')
         print(output)
         self.assertRegex(output, 'unreachable 202.54.1.3 proto static')
 
+        print('### ip route show type prohibit')
         output = check_output('ip route show type prohibit')
         print(output)
         self.assertRegex(output, 'prohibit 202.54.1.4 proto static')
@@ -1550,7 +1642,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         print(output)
         self.assertRegex(output, '2004:da8:1::/64')
 
-    def test_ipv6_neighbor(self):
+    def test_neighbor_section(self):
         copy_unit_to_networkd_unit_path('25-neighbor-section.network', '12-dummy.netdev')
         start_networkd()
         wait_online(['dummy98:degraded'], timeout='40s')
@@ -1560,6 +1652,16 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, '192.168.10.1.*00:00:5e:00:02:65.*PERMANENT')
         self.assertRegex(output, '2004:da8:1::1.*00:00:5e:00:02:66.*PERMANENT')
 
+    def test_neighbor_gre(self):
+        copy_unit_to_networkd_unit_path('25-neighbor-ip.network', '25-neighbor-ip-dummy.network',
+                                        '12-dummy.netdev', '25-gre-tunnel-remote-any.netdev')
+        start_networkd()
+        wait_online(['dummy98:degraded', 'gretun97:routable'], timeout='40s')
+
+        output = check_output('ip neigh list dev gretun97')
+        print(output)
+        self.assertRegex(output, '10.0.0.22 lladdr 10.65.223.239 PERMANENT')
+
     def test_link_local_addressing(self):
         copy_unit_to_networkd_unit_path('25-link-local-addressing-yes.network', '11-dummy.netdev',
                                         '25-link-local-addressing-no.network', '12-dummy.netdev')
@@ -1922,18 +2024,24 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
 
         output = check_output('bridge -d link show dummy98')
         print(output)
-        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode'), '1')
         self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'path_cost'), '400')
+        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode'), '1')
+        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave'), '1')
         self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood'), '1')
         self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood'), '0')
-        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave'), '1')
+        # CONFIG_BRIDGE_IGMP_SNOOPING=y
+        if (os.path.exists('/sys/devices/virtual/net/bridge00/lower_dummy98/brport/multicast_to_unicast')):
+            self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast'), '1')
         if (os.path.exists('/sys/devices/virtual/net/bridge99/lower_dummy98/brport/neigh_suppress')):
             self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress'), '1')
         self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'learning'), '0')
+        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'priority'), '23')
+        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'bpdu_guard'), '1')
+        self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'root_block'), '1')
 
-        # CONFIG_BRIDGE_IGMP_SNOOPING=y
-        if (os.path.exists('/sys/devices/virtual/net/bridge00/lower_dummy98/brport/multicast_to_unicast')):
-            self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast'), '1')
+        output = check_output('bridge -d link show test1')
+        print(output)
+        self.assertEqual(read_bridge_port_attr('bridge99', 'test1', 'priority'), '0')
 
         check_output('ip address add 192.168.0.16/24 dev bridge99')
         time.sleep(1)
@@ -2117,8 +2225,14 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         'dhcp-client-keep-configuration-dhcp-on-stop.network',
         'dhcp-client-keep-configuration-dhcp.network',
         'dhcp-client-listen-port.network',
+        'dhcp-client-reassign-static-routes-ipv4.network',
+        'dhcp-client-reassign-static-routes-ipv6.network',
         'dhcp-client-route-metric.network',
         'dhcp-client-route-table.network',
+        'dhcp-client-use-dns-ipv4-and-ra.network',
+        'dhcp-client-use-dns-ipv4.network',
+        'dhcp-client-use-dns-no.network',
+        'dhcp-client-use-dns-yes.network',
         'dhcp-client-use-routes-no.network',
         'dhcp-client-vrf.network',
         'dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network',
@@ -2317,9 +2431,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         print(output)
         self.assertRegex(output, 'metric 24')
 
-    def test_dhcp_client_use_routes_no(self):
+    def test_dhcp_client_reassign_static_routes_ipv4(self):
         copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
-                                        'dhcp-client-use-routes-no.network')
+                                        'dhcp-client-reassign-static-routes-ipv4.network')
         start_networkd()
         wait_online(['veth-peer:carrier'])
         start_dnsmasq(lease_time='2m')
@@ -2336,6 +2450,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         self.assertRegex(output, r'192.168.6.0/24 proto static')
         self.assertRegex(output, r'192.168.7.0/24 proto static')
 
+        stop_dnsmasq(dnsmasq_pid_file)
+        start_dnsmasq(ipv4_range='192.168.5.210,192.168.5.220', lease_time='2m')
+
         # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
         print('Wait for the dynamic address to be renewed')
         time.sleep(125)
@@ -2349,6 +2466,37 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         self.assertRegex(output, r'192.168.6.0/24 proto static')
         self.assertRegex(output, r'192.168.7.0/24 proto static')
 
+    def test_dhcp_client_reassign_static_routes_ipv6(self):
+        copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
+                                        'dhcp-client-reassign-static-routes-ipv6.network')
+        start_networkd()
+        wait_online(['veth-peer:carrier'])
+        start_dnsmasq(lease_time='2m')
+        wait_online(['veth99:routable', 'veth-peer:routable'])
+
+        output = check_output('ip address show dev veth99 scope global')
+        print(output)
+        self.assertRegex(output, r'inet6 2600::[0-9a-f]*/128 scope global (?:noprefixroute dynamic|dynamic noprefixroute)')
+
+        output = check_output('ip -6 route show dev veth99')
+        print(output)
+        self.assertRegex(output, r'2600::/64 proto ra metric 1024')
+        self.assertRegex(output, r'2600:0:0:1::/64 proto static metric 1024 pref medium')
+
+        stop_dnsmasq(dnsmasq_pid_file)
+        start_dnsmasq(ipv6_range='2600::30,2600::40', lease_time='2m')
+
+        # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
+        print('Wait for the dynamic address to be renewed')
+        time.sleep(125)
+
+        wait_online(['veth99:routable'])
+
+        output = check_output('ip -6 route show dev veth99')
+        print(output)
+        self.assertRegex(output, r'2600::/64 proto ra metric 1024')
+        self.assertRegex(output, r'2600:0:0:1::/64 proto static metric 1024 pref medium')
+
     def test_dhcp_keep_configuration_dhcp(self):
         copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-keep-configuration-dhcp.network')
         start_networkd()
@@ -2640,12 +2788,87 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address2} metric 1024')
         self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address2} metric 1024')
 
+    def test_dhcp_client_use_dns_yes(self):
+        copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-use-dns-yes.network')
+
+        start_networkd()
+        wait_online(['veth-peer:carrier'])
+        start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
+        wait_online(['veth99:routable', 'veth-peer:routable'])
+
+        # link become 'routable' when at least one protocol provide an valid address.
+        self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+        self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (?:dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
+
+        time.sleep(3)
+        output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
+        print(output)
+        self.assertRegex(output, '192.168.5.1')
+        self.assertRegex(output, '2600::1')
+
+    def test_dhcp_client_use_dns_no(self):
+        copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-use-dns-no.network')
+
+        start_networkd()
+        wait_online(['veth-peer:carrier'])
+        start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
+        wait_online(['veth99:routable', 'veth-peer:routable'])
+
+        # link become 'routable' when at least one protocol provide an valid address.
+        self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+        self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (?:dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
+
+        time.sleep(3)
+        output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
+        print(output)
+        self.assertNotRegex(output, '192.168.5.1')
+        self.assertNotRegex(output, '2600::1')
+
+    def test_dhcp_client_use_dns_ipv4(self):
+        copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-use-dns-ipv4.network')
+
+        start_networkd()
+        wait_online(['veth-peer:carrier'])
+        start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
+        wait_online(['veth99:routable', 'veth-peer:routable'])
+
+        # link become 'routable' when at least one protocol provide an valid address.
+        self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+        self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (?:dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
+
+        time.sleep(3)
+        output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
+        print(output)
+        self.assertRegex(output, '192.168.5.1')
+        self.assertNotRegex(output, '2600::1')
+
+    def test_dhcp_client_use_dns_ipv4_and_ra(self):
+        copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-use-dns-ipv4-and-ra.network')
+
+        start_networkd()
+        wait_online(['veth-peer:carrier'])
+        start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
+        wait_online(['veth99:routable', 'veth-peer:routable'])
+
+        # link become 'routable' when at least one protocol provide an valid address.
+        self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+        self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (?:dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
+
+        time.sleep(3)
+        output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
+        print(output)
+        self.assertRegex(output, '192.168.5.1')
+        self.assertRegex(output, '2600::1')
+
 if __name__ == '__main__':
     parser = argparse.ArgumentParser()
     parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
     parser.add_argument('--networkd', help='Path to systemd-networkd', dest='networkd_bin')
+    parser.add_argument('--resolved', help='Path to systemd-resolved', dest='resolved_bin')
     parser.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest='wait_online_bin')
     parser.add_argument('--networkctl', help='Path to networkctl', dest='networkctl_bin')
+    parser.add_argument('--resolvectl', help='Path to resolvectl', dest='resolvectl_bin')
+    parser.add_argument('--timedatectl', help='Path to timedatectl', dest='timedatectl_bin')
     parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind)
     parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug)
     parser.add_argument('--asan-options', help='ASAN options', dest='asan_options')
@@ -2654,18 +2877,27 @@ if __name__ == '__main__':
     ns, args = parser.parse_known_args(namespace=unittest)
 
     if ns.build_dir:
-        if ns.networkd_bin or ns.wait_online_bin or ns.networkctl_bin:
-            print('WARNING: --networkd, --wait-online, or --networkctl options are ignored when --build-dir is specified.')
+        if ns.networkd_bin or ns.resolved_bin or ns.wait_online_bin or ns.networkctl_bin or ns.resolvectl_bin or ns.timedatectl_bin:
+            print('WARNING: --networkd, --resolved, --wait-online, --networkctl, --resolvectl, or --timedatectl options are ignored when --build-dir is specified.')
         networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
+        resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
         wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
         networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
+        resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
+        timedatectl_bin = os.path.join(ns.build_dir, 'timedatectl')
     else:
         if ns.networkd_bin:
             networkd_bin = ns.networkd_bin
+        if ns.resolved_bin:
+            resolved_bin = ns.resolved_bin
         if ns.wait_online_bin:
             wait_online_bin = ns.wait_online_bin
         if ns.networkctl_bin:
             networkctl_bin = ns.networkctl_bin
+        if ns.resolvectl_bin:
+            resolvectl_bin = ns.resolvectl_bin
+        if ns.timedatectl_bin:
+            timedatectl_bin = ns.timedatectl_bin
 
     use_valgrind = ns.use_valgrind
     enable_debug = ns.enable_debug
@@ -2675,9 +2907,13 @@ if __name__ == '__main__':
 
     if use_valgrind:
         networkctl_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', networkctl_bin]
+        resolvectl_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', resolvectl_bin]
+        timedatectl_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', timedatectl_bin]
         wait_online_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', wait_online_bin]
     else:
         networkctl_cmd = [networkctl_bin]
+        resolvectl_cmd = [resolvectl_bin]
+        timedatectl_cmd = [timedatectl_bin]
         wait_online_cmd = [wait_online_bin]
 
     if enable_debug:
index 313e6c6a0f962cba57c6bbf265990a01a4a622a5..255d1e2ba69e23d797b0232c1e6cbbb834a5eb89 100755 (executable)
@@ -16,11 +16,11 @@ cd $REPO_ROOT
 export PATH="$HOME/.local/bin/:$PATH"
 
 # We use a subset of https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#available-checks instead of "undefined"
-# because our fuzzers crash with "pointer-overflow","object-size" and "float-cast-overflow":
+# because our fuzzers crash with "pointer-overflow" and "float-cast-overflow":
 # https://github.com/systemd/systemd/pull/12771#issuecomment-502139157
 # https://github.com/systemd/systemd/pull/12812#issuecomment-502780455
 # TODO: figure out what to do about unsigned-integer-overflow: https://github.com/google/oss-fuzz/issues/910
-export SANITIZER="address -fsanitize=alignment,array-bounds,bool,bounds,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,unsigned-integer-overflow,vla-bound,vptr -fno-sanitize-recover=alignment,array-bounds,bool,bounds,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,vla-bound,vptr"
+export SANITIZER="address -fsanitize=alignment,array-bounds,bool,bounds,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,object-size,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,unsigned-integer-overflow,vla-bound,vptr -fno-sanitize-recover=alignment,array-bounds,bool,bounds,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,object-size,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,vla-bound,vptr"
 tools/oss-fuzz.sh
 
 FUZZING_TYPE=${1:-sanity}