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