923a60
From f997080b4d17a40b59b398e4354b6368d9c85f69 Mon Sep 17 00:00:00 2001
923a60
From: Ivan Shapovalov <intelfx100@gmail.com>
923a60
Date: Sat, 7 Mar 2015 08:44:52 -0500
923a60
Subject: [PATCH] core: do not spawn jobs or touch other units during
923a60
 coldplugging
923a60
923a60
Because the order of coldplugging is not defined, we can reference a
923a60
not-yet-coldplugged unit and read its state while it has not yet been
923a60
set to a meaningful value.
923a60
923a60
This way, already active units may get started again.
923a60
923a60
We fix this by deferring such actions until all units have been at
923a60
least somehow coldplugged.
923a60
923a60
Fixes https://bugs.freedesktop.org/show_bug.cgi?id=88401
923a60
923a60
(cherry picked from commit 6e392c9c45643d106673c6643ac8bf4e65da13c1)
923a60
---
923a60
 src/core/automount.c |  2 +-
923a60
 src/core/busname.c   |  2 +-
923a60
 src/core/device.c    |  2 +-
923a60
 src/core/manager.c   | 35 +++++++++++++++++++++++++++++++++--
923a60
 src/core/mount.c     |  2 +-
923a60
 src/core/path.c      | 14 ++++++++++----
923a60
 src/core/scope.c     |  2 +-
923a60
 src/core/service.c   |  2 +-
923a60
 src/core/slice.c     |  2 +-
923a60
 src/core/snapshot.c  |  2 +-
923a60
 src/core/socket.c    |  2 +-
923a60
 src/core/swap.c      |  2 +-
923a60
 src/core/target.c    |  2 +-
923a60
 src/core/timer.c     | 14 ++++++++++----
923a60
 src/core/unit.c      | 25 ++++++++++++++++---------
923a60
 src/core/unit.h      | 12 +++++++++---
923a60
 16 files changed, 89 insertions(+), 33 deletions(-)
923a60
923a60
diff --git a/src/core/automount.c b/src/core/automount.c
923a60
index 9f6bd84b21..e4c79415d1 100644
923a60
--- a/src/core/automount.c
923a60
+++ b/src/core/automount.c
923a60
@@ -235,7 +235,7 @@ static void automount_set_state(Automount *a, AutomountState state) {
923a60
         unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], true);
923a60
 }
923a60
 
923a60
-static int automount_coldplug(Unit *u) {
923a60
+static int automount_coldplug(Unit *u, Hashmap *deferred_work) {
923a60
         Automount *a = AUTOMOUNT(u);
923a60
         int r;
923a60
 
923a60
diff --git a/src/core/busname.c b/src/core/busname.c
923a60
index 1d77292f9b..43d7607a30 100644
923a60
--- a/src/core/busname.c
923a60
+++ b/src/core/busname.c
923a60
@@ -335,7 +335,7 @@ static void busname_set_state(BusName *n, BusNameState state) {
923a60
         unit_notify(UNIT(n), state_translation_table[old_state], state_translation_table[state], true);
923a60
 }
923a60
 
923a60
-static int busname_coldplug(Unit *u) {
923a60
+static int busname_coldplug(Unit *u, Hashmap *deferred_work) {
923a60
         BusName *n = BUSNAME(u);
923a60
         int r;
923a60
 
923a60
diff --git a/src/core/device.c b/src/core/device.c
923a60
index 1cc103c290..4ff8827219 100644
923a60
--- a/src/core/device.c
923a60
+++ b/src/core/device.c
923a60
@@ -142,7 +142,7 @@ static void device_set_state(Device *d, DeviceState state) {
923a60
         unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], true);
923a60
 }
923a60
 
923a60
-static int device_coldplug(Unit *u) {
923a60
+static int device_coldplug(Unit *u, Hashmap *deferred_work) {
923a60
         Device *d = DEVICE(u);
923a60
 
923a60
         assert(d);
923a60
diff --git a/src/core/manager.c b/src/core/manager.c
923a60
index bc9b7ec620..203a6a0a1a 100644
923a60
--- a/src/core/manager.c
923a60
+++ b/src/core/manager.c
923a60
@@ -983,7 +983,28 @@ static int manager_coldplug(Manager *m) {
923a60
         Unit *u;
923a60
         char *k;
923a60
 
923a60
-        assert(m);
923a60
+        /*
923a60
+         * Some unit types tend to spawn jobs or check other units' state
923a60
+         * during coldplug. This is wrong because it is undefined whether the
923a60
+         * units in question have been already coldplugged (i. e. their state
923a60
+         * restored). This way, we can easily re-start an already started unit
923a60
+         * or otherwise make a wrong decision based on the unit's state.
923a60
+         *
923a60
+         * Solve this by providing a way for coldplug functions to defer
923a60
+         * such actions until after all units have been coldplugged.
923a60
+         *
923a60
+         * We store Unit* -> int(*)(Unit*).
923a60
+         *
923a60
+         * https://bugs.freedesktop.org/show_bug.cgi?id=88401
923a60
+         */
923a60
+        _cleanup_hashmap_free_ Hashmap *deferred_work = NULL;
923a60
+        int(*proc)(Unit*);
923a60
+
923a60
+        assert(m);
923a60
+
923a60
+        deferred_work = hashmap_new(&trivial_hash_ops);
923a60
+        if (!deferred_work)
923a60
+                return -ENOMEM;
923a60
 
923a60
         /* Then, let's set up their initial state. */
923a60
         HASHMAP_FOREACH_KEY(u, k, m->units, i) {
923a60
@@ -993,7 +1014,17 @@ static int manager_coldplug(Manager *m) {
923a60
                 if (u->id != k)
923a60
                         continue;
923a60
 
923a60
-                q = unit_coldplug(u);
923a60
+                q = unit_coldplug(u, deferred_work);
923a60
+                if (q < 0)
923a60
+                        r = q;
923a60
+        }
923a60
+
923a60
+        /* After coldplugging and setting up initial state of the units,
923a60
+         * let's perform operations which spawn jobs or query units' state. */
923a60
+        HASHMAP_FOREACH_KEY(proc, u, deferred_work, i) {
923a60
+                int q;
923a60
+
923a60
+                q = proc(u);
923a60
                 if (q < 0)
923a60
                         r = q;
923a60
         }
923a60
diff --git a/src/core/mount.c b/src/core/mount.c
923a60
index c971330af2..3ae0eb4621 100644
923a60
--- a/src/core/mount.c
923a60
+++ b/src/core/mount.c
923a60
@@ -617,7 +617,7 @@ static void mount_set_state(Mount *m, MountState state) {
923a60
         m->reload_result = MOUNT_SUCCESS;
923a60
 }
923a60
 
923a60
-static int mount_coldplug(Unit *u) {
923a60
+static int mount_coldplug(Unit *u, Hashmap *deferred_work) {
923a60
         Mount *m = MOUNT(u);
923a60
         MountState new_state = MOUNT_DEAD;
923a60
         int r;
923a60
diff --git a/src/core/path.c b/src/core/path.c
923a60
index e5ea79fec7..51e36fa8be 100644
923a60
--- a/src/core/path.c
923a60
+++ b/src/core/path.c
923a60
@@ -440,7 +440,12 @@ static void path_set_state(Path *p, PathState state) {
923a60
 
923a60
 static void path_enter_waiting(Path *p, bool initial, bool recheck);
923a60
 
923a60
-static int path_coldplug(Unit *u) {
923a60
+static int path_enter_waiting_coldplug(Unit *u) {
923a60
+        path_enter_waiting(PATH(u), true, true);
923a60
+        return 0;
923a60
+}
923a60
+
923a60
+static int path_coldplug(Unit *u, Hashmap *deferred_work) {
923a60
         Path *p = PATH(u);
923a60
 
923a60
         assert(p);
923a60
@@ -449,9 +454,10 @@ static int path_coldplug(Unit *u) {
923a60
         if (p->deserialized_state != p->state) {
923a60
 
923a60
                 if (p->deserialized_state == PATH_WAITING ||
923a60
-                    p->deserialized_state == PATH_RUNNING)
923a60
-                        path_enter_waiting(p, true, true);
923a60
-                else
923a60
+                    p->deserialized_state == PATH_RUNNING) {
923a60
+                        hashmap_put(deferred_work, u, &path_enter_waiting_coldplug);
923a60
+                        path_set_state(p, PATH_WAITING);
923a60
+                } else
923a60
                         path_set_state(p, p->deserialized_state);
923a60
         }
923a60
 
923a60
diff --git a/src/core/scope.c b/src/core/scope.c
923a60
index b41db7872c..ae6614fbf0 100644
923a60
--- a/src/core/scope.c
923a60
+++ b/src/core/scope.c
923a60
@@ -173,7 +173,7 @@ static int scope_load(Unit *u) {
923a60
         return scope_verify(s);
923a60
 }
923a60
 
923a60
-static int scope_coldplug(Unit *u) {
923a60
+static int scope_coldplug(Unit *u, Hashmap *deferred_work) {
923a60
         Scope *s = SCOPE(u);
923a60
         int r;
923a60
 
923a60
diff --git a/src/core/service.c b/src/core/service.c
923a60
index 15e29be149..7781b4e626 100644
923a60
--- a/src/core/service.c
923a60
+++ b/src/core/service.c
923a60
@@ -879,7 +879,7 @@ static void service_set_state(Service *s, ServiceState state) {
923a60
         s->reload_result = SERVICE_SUCCESS;
923a60
 }
923a60
 
923a60
-static int service_coldplug(Unit *u) {
923a60
+static int service_coldplug(Unit *u, Hashmap *deferred_work) {
923a60
         Service *s = SERVICE(u);
923a60
         int r;
923a60
 
923a60
diff --git a/src/core/slice.c b/src/core/slice.c
923a60
index ae9819d015..61ff9d3314 100644
923a60
--- a/src/core/slice.c
923a60
+++ b/src/core/slice.c
923a60
@@ -153,7 +153,7 @@ static int slice_load(Unit *u) {
923a60
         return slice_verify(s);
923a60
 }
923a60
 
923a60
-static int slice_coldplug(Unit *u) {
923a60
+static int slice_coldplug(Unit *u, Hashmap *deferred_work) {
923a60
         Slice *t = SLICE(u);
923a60
 
923a60
         assert(t);
923a60
diff --git a/src/core/snapshot.c b/src/core/snapshot.c
923a60
index b70c3beb60..b1d8448771 100644
923a60
--- a/src/core/snapshot.c
923a60
+++ b/src/core/snapshot.c
923a60
@@ -75,7 +75,7 @@ static int snapshot_load(Unit *u) {
923a60
         return 0;
923a60
 }
923a60
 
923a60
-static int snapshot_coldplug(Unit *u) {
923a60
+static int snapshot_coldplug(Unit *u, Hashmap *deferred_work) {
923a60
         Snapshot *s = SNAPSHOT(u);
923a60
 
923a60
         assert(s);
923a60
diff --git a/src/core/socket.c b/src/core/socket.c
923a60
index 88aae4815b..760de0203d 100644
923a60
--- a/src/core/socket.c
923a60
+++ b/src/core/socket.c
923a60
@@ -1326,7 +1326,7 @@ static void socket_set_state(Socket *s, SocketState state) {
923a60
         unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
923a60
 }
923a60
 
923a60
-static int socket_coldplug(Unit *u) {
923a60
+static int socket_coldplug(Unit *u, Hashmap *deferred_work) {
923a60
         Socket *s = SOCKET(u);
923a60
         int r;
923a60
 
923a60
diff --git a/src/core/swap.c b/src/core/swap.c
923a60
index 5c19af5d91..369abf0f53 100644
923a60
--- a/src/core/swap.c
923a60
+++ b/src/core/swap.c
923a60
@@ -513,7 +513,7 @@ static void swap_set_state(Swap *s, SwapState state) {
923a60
                         job_add_to_run_queue(UNIT(other)->job);
923a60
 }
923a60
 
923a60
-static int swap_coldplug(Unit *u) {
923a60
+static int swap_coldplug(Unit *u, Hashmap *deferred_work) {
923a60
         Swap *s = SWAP(u);
923a60
         SwapState new_state = SWAP_DEAD;
923a60
         int r;
923a60
diff --git a/src/core/target.c b/src/core/target.c
923a60
index 33fb66bc3f..2411a8e758 100644
923a60
--- a/src/core/target.c
923a60
+++ b/src/core/target.c
923a60
@@ -107,7 +107,7 @@ static int target_load(Unit *u) {
923a60
         return 0;
923a60
 }
923a60
 
923a60
-static int target_coldplug(Unit *u) {
923a60
+static int target_coldplug(Unit *u, Hashmap *deferred_work) {
923a60
         Target *t = TARGET(u);
923a60
 
923a60
         assert(t);
923a60
diff --git a/src/core/timer.c b/src/core/timer.c
923a60
index 45744c7de5..48cf9c16a8 100644
923a60
--- a/src/core/timer.c
923a60
+++ b/src/core/timer.c
923a60
@@ -268,7 +268,12 @@ static void timer_set_state(Timer *t, TimerState state) {
923a60
 
923a60
 static void timer_enter_waiting(Timer *t, bool initial);
923a60
 
923a60
-static int timer_coldplug(Unit *u) {
923a60
+static int timer_enter_waiting_coldplug(Unit *u) {
923a60
+        timer_enter_waiting(TIMER(u), false);
923a60
+        return 0;
923a60
+}
923a60
+
923a60
+static int timer_coldplug(Unit *u, Hashmap *deferred_work) {
923a60
         Timer *t = TIMER(u);
923a60
 
923a60
         assert(t);
923a60
@@ -276,9 +281,10 @@ static int timer_coldplug(Unit *u) {
923a60
 
923a60
         if (t->deserialized_state != t->state) {
923a60
 
923a60
-                if (t->deserialized_state == TIMER_WAITING)
923a60
-                        timer_enter_waiting(t, false);
923a60
-                else
923a60
+                if (t->deserialized_state == TIMER_WAITING) {
923a60
+                        hashmap_put(deferred_work, u, &timer_enter_waiting_coldplug);
923a60
+                        timer_set_state(t, TIMER_WAITING);
923a60
+                } else
923a60
                         timer_set_state(t, t->deserialized_state);
923a60
         }
923a60
 
923a60
diff --git a/src/core/unit.c b/src/core/unit.c
923a60
index a6558ee23b..565455bd63 100644
923a60
--- a/src/core/unit.c
923a60
+++ b/src/core/unit.c
923a60
@@ -2859,27 +2859,34 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
923a60
         return 0;
923a60
 }
923a60
 
923a60
-int unit_coldplug(Unit *u) {
923a60
+static int unit_add_deserialized_job_coldplug(Unit *u) {
923a60
+        int r;
923a60
+
923a60
+        r = manager_add_job(u->manager, u->deserialized_job, u, JOB_IGNORE_REQUIREMENTS, false, NULL, NULL);
923a60
+        if (r < 0)
923a60
+                return r;
923a60
+
923a60
+        u->deserialized_job = _JOB_TYPE_INVALID;
923a60
+
923a60
+        return 0;
923a60
+}
923a60
+
923a60
+int unit_coldplug(Unit *u, Hashmap *deferred_work) {
923a60
         int r;
923a60
 
923a60
         assert(u);
923a60
 
923a60
         if (UNIT_VTABLE(u)->coldplug)
923a60
-                if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0)
923a60
+                if ((r = UNIT_VTABLE(u)->coldplug(u, deferred_work)) < 0)
923a60
                         return r;
923a60
 
923a60
         if (u->job) {
923a60
                 r = job_coldplug(u->job);
923a60
                 if (r < 0)
923a60
                         return r;
923a60
-        } else if (u->deserialized_job >= 0) {
923a60
+        } else if (u->deserialized_job >= 0)
923a60
                 /* legacy */
923a60
-                r = manager_add_job(u->manager, u->deserialized_job, u, JOB_IGNORE_REQUIREMENTS, false, NULL, NULL);
923a60
-                if (r < 0)
923a60
-                        return r;
923a60
-
923a60
-                u->deserialized_job = _JOB_TYPE_INVALID;
923a60
-        }
923a60
+                hashmap_put(deferred_work, u, &unit_add_deserialized_job_coldplug);
923a60
 
923a60
         return 0;
923a60
 }
923a60
diff --git a/src/core/unit.h b/src/core/unit.h
923a60
index 291bc77a76..7ebc489c80 100644
923a60
--- a/src/core/unit.h
923a60
+++ b/src/core/unit.h
923a60
@@ -307,8 +307,14 @@ struct UnitVTable {
923a60
         int (*load)(Unit *u);
923a60
 
923a60
         /* If a lot of units got created via enumerate(), this is
923a60
-         * where to actually set the state and call unit_notify(). */
923a60
-        int (*coldplug)(Unit *u);
923a60
+         * where to actually set the state and call unit_notify().
923a60
+         *
923a60
+         * This must not reference other units (maybe implicitly through spawning
923a60
+         * jobs), because it is possible that they are not yet coldplugged.
923a60
+         * Such actions must be deferred until the end of coldplug bу adding
923a60
+         * a "Unit* -> int(*)(Unit*)" entry into the hashmap.
923a60
+         */
923a60
+        int (*coldplug)(Unit *u, Hashmap *deferred_work);
923a60
 
923a60
         void (*dump)(Unit *u, FILE *f, const char *prefix);
923a60
 
923a60
@@ -544,7 +550,7 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds);
923a60
 
923a60
 int unit_add_node_link(Unit *u, const char *what, bool wants);
923a60
 
923a60
-int unit_coldplug(Unit *u);
923a60
+int unit_coldplug(Unit *u, Hashmap *deferred_work);
923a60
 
923a60
 void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) _printf_(3, 0);
923a60