Blame SOURCES/0648-core-delay-adding-target-dependencies-until-all-unit.patch

17b0f1
From 36226a9afe96bdffce9d0697be020f2ca9d7fe6f Mon Sep 17 00:00:00 2001
17b0f1
From: Michal Sekletar <msekletar@users.noreply.github.com>
17b0f1
Date: Fri, 23 Mar 2018 15:28:06 +0100
17b0f1
Subject: [PATCH] core: delay adding target dependencies until all units are
17b0f1
 loaded and aliases resolved (#8381)
17b0f1
17b0f1
Currently we add target dependencies while we are loading units. This
17b0f1
can create ordering loops even if configuration doesn't contain any
17b0f1
loop. Take for example following configuration,
17b0f1
17b0f1
$ systemctl get-default
17b0f1
multi-user.target
17b0f1
17b0f1
$ cat /etc/systemd/system/test.service
17b0f1
[Unit]
17b0f1
After=default.target
17b0f1
17b0f1
[Service]
17b0f1
ExecStart=/bin/true
17b0f1
17b0f1
[Install]
17b0f1
WantedBy=multi-user.target
17b0f1
17b0f1
If we encounter such unit file early during manager start-up (e.g. load
17b0f1
queue is dispatched while enumerating devices due to SYSTEMD_WANTS in
17b0f1
udev rules) we would add stub unit default.target and we order it Before
17b0f1
test.service. At the same time we add implicit Before to
17b0f1
multi-user.target. Later we merge two units and we create ordering cycle
17b0f1
in the process.
17b0f1
17b0f1
To fix the issue we will now never add any target dependencies until we
17b0f1
loaded all the unit files and resolved all the aliases.
17b0f1
17b0f1
(cherry picked from commit 19496554e23ea4861ce780430052dcf86a2ffcba)
17b0f1
17b0f1
Resolves: #1368856
17b0f1
---
17b0f1
 src/core/manager.c | 40 ++++++++++++++++++++++++++++++++++++++++
17b0f1
 src/core/manager.h |  3 +++
17b0f1
 src/core/unit.c    | 46 ++++++++++++++++------------------------------
17b0f1
 src/core/unit.h    |  5 +++++
17b0f1
 4 files changed, 64 insertions(+), 30 deletions(-)
17b0f1
17b0f1
diff --git a/src/core/manager.c b/src/core/manager.c
17b0f1
index 9c406bb5bf..0466e4bb8a 100644
17b0f1
--- a/src/core/manager.c
17b0f1
+++ b/src/core/manager.c
17b0f1
@@ -1373,6 +1373,41 @@ Unit *manager_get_unit(Manager *m, const char *name) {
17b0f1
         return hashmap_get(m->units, name);
17b0f1
 }
17b0f1
 
17b0f1
+static int manager_dispatch_target_deps_queue(Manager *m) {
17b0f1
+        Unit *u;
17b0f1
+        unsigned k;
17b0f1
+        int r = 0;
17b0f1
+
17b0f1
+        static const UnitDependency deps[] = {
17b0f1
+                UNIT_REQUIRED_BY,
17b0f1
+                UNIT_REQUIRED_BY_OVERRIDABLE,
17b0f1
+                UNIT_WANTED_BY,
17b0f1
+                UNIT_BOUND_BY
17b0f1
+        };
17b0f1
+
17b0f1
+        assert(m);
17b0f1
+
17b0f1
+        while ((u = m->target_deps_queue)) {
17b0f1
+                assert(u->in_target_deps_queue);
17b0f1
+
17b0f1
+                LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u);
17b0f1
+                u->in_target_deps_queue = false;
17b0f1
+
17b0f1
+                for (k = 0; k < ELEMENTSOF(deps); k++) {
17b0f1
+                        Unit *target;
17b0f1
+                        Iterator i;
17b0f1
+
17b0f1
+                        SET_FOREACH(target, u->dependencies[deps[k]], i) {
17b0f1
+                                r = unit_add_default_target_dependency(u, target);
17b0f1
+                                if (r < 0)
17b0f1
+                                        return r;
17b0f1
+                        }
17b0f1
+                }
17b0f1
+        }
17b0f1
+
17b0f1
+        return r;
17b0f1
+}
17b0f1
+
17b0f1
 unsigned manager_dispatch_load_queue(Manager *m) {
17b0f1
         Unit *u;
17b0f1
         unsigned n = 0;
17b0f1
@@ -1396,6 +1431,11 @@ unsigned manager_dispatch_load_queue(Manager *m) {
17b0f1
         }
17b0f1
 
17b0f1
         m->dispatching_load_queue = false;
17b0f1
+
17b0f1
+        /* Dispatch the units waiting for their target dependencies to be added now, as all targets that we know about
17b0f1
+         * should be loaded and have aliases resolved */
17b0f1
+        (void) manager_dispatch_target_deps_queue(m);
17b0f1
+
17b0f1
         return n;
17b0f1
 }
17b0f1
 
17b0f1
diff --git a/src/core/manager.h b/src/core/manager.h
17b0f1
index 90d2d982e1..b0e4cad1fc 100644
17b0f1
--- a/src/core/manager.h
17b0f1
+++ b/src/core/manager.h
17b0f1
@@ -114,6 +114,9 @@ struct Manager {
17b0f1
         /* Units that should be realized */
17b0f1
         LIST_HEAD(Unit, cgroup_queue);
17b0f1
 
17b0f1
+        /* Target units whose default target dependencies haven't been set yet */
17b0f1
+        LIST_HEAD(Unit, target_deps_queue);
17b0f1
+
17b0f1
         sd_event *event;
17b0f1
 
17b0f1
         /* We use two hash tables here, since the same PID might be
17b0f1
diff --git a/src/core/unit.c b/src/core/unit.c
17b0f1
index 22d9beed76..cfddce34d4 100644
17b0f1
--- a/src/core/unit.c
17b0f1
+++ b/src/core/unit.c
17b0f1
@@ -519,6 +519,9 @@ void unit_free(Unit *u) {
17b0f1
                 u->manager->n_in_gc_queue--;
17b0f1
         }
17b0f1
 
17b0f1
+        if (u->in_target_deps_queue)
17b0f1
+                LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u);
17b0f1
+
17b0f1
         if (u->in_cgroup_queue)
17b0f1
                 LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u);
17b0f1
 
17b0f1
@@ -1065,6 +1068,18 @@ int unit_load_fragment_and_dropin_optional(Unit *u) {
17b0f1
         return 0;
17b0f1
 }
17b0f1
 
17b0f1
+void unit_add_to_target_deps_queue(Unit *u) {
17b0f1
+        Manager *m = u->manager;
17b0f1
+
17b0f1
+        assert(u);
17b0f1
+
17b0f1
+        if (u->in_target_deps_queue)
17b0f1
+                return;
17b0f1
+
17b0f1
+        LIST_PREPEND(target_deps_queue, m->target_deps_queue, u);
17b0f1
+        u->in_target_deps_queue = true;
17b0f1
+}
17b0f1
+
17b0f1
 int unit_add_default_target_dependency(Unit *u, Unit *target) {
17b0f1
         assert(u);
17b0f1
         assert(target);
17b0f1
@@ -1091,32 +1106,6 @@ int unit_add_default_target_dependency(Unit *u, Unit *target) {
17b0f1
         return unit_add_dependency(target, UNIT_AFTER, u, true);
17b0f1
 }
17b0f1
 
17b0f1
-static int unit_add_target_dependencies(Unit *u) {
17b0f1
-
17b0f1
-        static const UnitDependency deps[] = {
17b0f1
-                UNIT_REQUIRED_BY,
17b0f1
-                UNIT_REQUIRED_BY_OVERRIDABLE,
17b0f1
-                UNIT_WANTED_BY,
17b0f1
-                UNIT_BOUND_BY
17b0f1
-        };
17b0f1
-
17b0f1
-        Unit *target;
17b0f1
-        Iterator i;
17b0f1
-        unsigned k;
17b0f1
-        int r = 0;
17b0f1
-
17b0f1
-        assert(u);
17b0f1
-
17b0f1
-        for (k = 0; k < ELEMENTSOF(deps); k++)
17b0f1
-                SET_FOREACH(target, u->dependencies[deps[k]], i) {
17b0f1
-                        r = unit_add_default_target_dependency(u, target);
17b0f1
-                        if (r < 0)
17b0f1
-                                return r;
17b0f1
-                }
17b0f1
-
17b0f1
-        return r;
17b0f1
-}
17b0f1
-
17b0f1
 static int unit_add_slice_dependencies(Unit *u) {
17b0f1
         assert(u);
17b0f1
 
17b0f1
@@ -1217,10 +1206,7 @@ int unit_load(Unit *u) {
17b0f1
         }
17b0f1
 
17b0f1
         if (u->load_state == UNIT_LOADED) {
17b0f1
-
17b0f1
-                r = unit_add_target_dependencies(u);
17b0f1
-                if (r < 0)
17b0f1
-                        goto fail;
17b0f1
+                unit_add_to_target_deps_queue(u);
17b0f1
 
17b0f1
                 r = unit_add_slice_dependencies(u);
17b0f1
                 if (r < 0)
17b0f1
diff --git a/src/core/unit.h b/src/core/unit.h
17b0f1
index 480e2e95f1..dfec9cea01 100644
17b0f1
--- a/src/core/unit.h
17b0f1
+++ b/src/core/unit.h
17b0f1
@@ -162,6 +162,9 @@ struct Unit {
17b0f1
         /* CGroup realize members queue */
17b0f1
         LIST_FIELDS(Unit, cgroup_queue);
17b0f1
 
17b0f1
+        /* Target dependencies queue */
17b0f1
+        LIST_FIELDS(Unit, target_deps_queue);
17b0f1
+
17b0f1
         /* PIDs we keep an eye on. Note that a unit might have many
17b0f1
          * more, but these are the ones we care enough about to
17b0f1
          * process SIGCHLD for */
17b0f1
@@ -228,6 +231,7 @@ struct Unit {
17b0f1
         bool in_cleanup_queue:1;
17b0f1
         bool in_gc_queue:1;
17b0f1
         bool in_cgroup_queue:1;
17b0f1
+        bool in_target_deps_queue:1;
17b0f1
 
17b0f1
         bool sent_dbus_new_signal:1;
17b0f1
 
17b0f1
@@ -498,6 +502,7 @@ void unit_add_to_load_queue(Unit *u);
17b0f1
 void unit_add_to_dbus_queue(Unit *u);
17b0f1
 void unit_add_to_cleanup_queue(Unit *u);
17b0f1
 void unit_add_to_gc_queue(Unit *u);
17b0f1
+void unit_add_to_target_deps_queue(Unit *u);
17b0f1
 
17b0f1
 int unit_merge(Unit *u, Unit *other);
17b0f1
 int unit_merge_by_name(Unit *u, const char *other);