From f0831ed2a03fcef582660be1c3b1a9f3e267e656 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Jun 2018 16:53:22 +0200 Subject: [PATCH] core: add a new unit method "catchup()" This is very similar to the existing unit method coldplug() but is called a bit later. The idea is that that coldplug() restores the unit state from before any prior reload/restart, i.e. puts the deserialized state in effect. The catchup() call is then called a bit later, to catch up with the system state for which we missed notifications while we were reloading. This is only really useful for mount, swap and device mount points were we should be careful to generate all missing unit state change events (i.e. call unit_notify() appropriately) for everything that happened while we were reloading. --- src/core/manager.c | 30 +++++++++++++++++++++++++++++- src/core/unit.c | 11 ++++++++--- src/core/unit.h | 17 ++++++++++------- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/core/manager.c b/src/core/manager.c index a0c404dc9d..f90cc12910 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1350,7 +1350,9 @@ static void manager_coldplug(Manager *m) { assert(m); - /* Then, let's set up their initial state. */ + log_debug("Invoking unit coldplug() handlers…"); + + /* Let's place the units back into their deserialized state */ HASHMAP_FOREACH_KEY(u, k, m->units, i) { /* ignore aliases */ @@ -1363,6 +1365,26 @@ static void manager_coldplug(Manager *m) { } } +static void manager_catchup(Manager *m) { + Iterator i; + Unit *u; + char *k; + + assert(m); + + log_debug("Invoking unit catchup() handlers…"); + + /* Let's catch up on any state changes that happened while we were reloading/reexecing */ + HASHMAP_FOREACH_KEY(u, k, m->units, i) { + + /* ignore aliases */ + if (u->id != k) + continue; + + unit_catchup(u); + } +} + static void manager_build_unit_path_cache(Manager *m) { char **i; int r; @@ -1602,6 +1624,9 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { m->send_reloading_done = true; } + /* Let's finally catch up with any changes that took place while we were reloading/reexecing */ + manager_catchup(m); + return 0; } @@ -3414,6 +3439,9 @@ int manager_reload(Manager *m) { manager_recheck_journal(m); manager_recheck_dbus(m); + /* Let's finally catch up with any changes that took place while we were reloading/reexecing */ + manager_catchup(m); + /* Sync current state of bus names with our set of listening units */ q = manager_enqueue_sync_bus_names(m); if (q < 0 && r >= 0) diff --git a/src/core/unit.c b/src/core/unit.c index 7265f95c95..a82e5fd9eb 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -2298,7 +2298,6 @@ static void unit_update_on_console(Unit *u) { manager_ref_console(u->manager); else manager_unref_console(u->manager); - } void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags) { @@ -3730,8 +3729,7 @@ int unit_coldplug(Unit *u) { assert(u); - /* Make sure we don't enter a loop, when coldplugging - * recursively. */ + /* Make sure we don't enter a loop, when coldplugging recursively. */ if (u->coldplugged) return 0; @@ -3759,6 +3757,13 @@ int unit_coldplug(Unit *u) { return r; } +void unit_catchup(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->catchup) + UNIT_VTABLE(u)->catchup(u); +} + static bool fragment_mtime_newer(const char *path, usec_t mtime, bool path_masked) { struct stat st; diff --git a/src/core/unit.h b/src/core/unit.h index 32bdd10643..9d9d94dd4e 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -437,10 +437,14 @@ typedef struct UnitVTable { * UNIT_STUB if no configuration could be found. */ int (*load)(Unit *u); - /* If a lot of units got created via enumerate(), this is - * where to actually set the state and call unit_notify(). */ + /* During deserialization we only record the intended state to return to. With coldplug() we actually put the + * deserialized state in effect. This is where unit_notify() should be called to start things up. */ int (*coldplug)(Unit *u); + /* This is called shortly after all units' coldplug() call was invoked. It's supposed to catch up state changes + * we missed so far (for example because they took place while we were reloading/reexecing) */ + void (*catchup)(Unit *u); + void (*dump)(Unit *u, FILE *f, const char *prefix); int (*start)(Unit *u); @@ -531,11 +535,9 @@ typedef struct UnitVTable { /* Returns true if the unit currently needs access to the console */ bool (*needs_console)(Unit *u); - /* This is called for each unit type and should be used to - * enumerate existing devices and load them. However, - * everything that is loaded here should still stay in - * inactive state. It is the job of the coldplug() call above - * to put the units into the initial state. */ + /* This is called for each unit type and should be used to enumerate units already existing in the system + * internally and load them. However, everything that is loaded here should still stay in inactive state. It is + * the job of the coldplug() call above to put the units into the initial state. */ void (*enumerate)(Manager *m); /* Type specific cleanups. */ @@ -687,6 +689,7 @@ void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *v int unit_add_node_dependency(Unit *u, const char *what, bool wants, UnitDependency d, UnitDependencyMask mask); int unit_coldplug(Unit *u); +void unit_catchup(Unit *u); void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) _printf_(3, 0); void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t);