]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: refresh unit cache when building a transaction if UNIT_NOT_FOUND
authorLuca Boccassi <luca.boccassi@microsoft.com>
Fri, 3 Jul 2020 17:45:19 +0000 (18:45 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 7 Jul 2020 08:09:24 +0000 (10:09 +0200)
When a command asks to load a unit directly and it is in state
UNIT_NOT_FOUND, and the cache is outdated, we refresh it and
attempto to load again.
Use the same logic when building up a transaction and a dependency in
UNIT_NOT_FOUND state is encountered.
Update the unit test to exercise this code path.

src/core/manager.c
src/core/manager.h
src/core/transaction.c
test/units/testsuite-48.sh

index 7b199f5175df7de8c7aa9c039d5e727eab70bdae..743ef6b4fc32eaed79e61222c088be088847774b 100644 (file)
@@ -1932,11 +1932,19 @@ unsigned manager_dispatch_load_queue(Manager *m) {
         return n;
 }
 
-static bool manager_unit_cache_needs_refresh(Manager *m, Unit *u) {
-        assert(m);
+bool manager_unit_file_maybe_loadable_from_cache(Unit *u) {
+        assert(u);
+
+        if (u->load_state != UNIT_NOT_FOUND)
+                return false;
+
+        if (u->manager->unit_cache_mtime == 0)
+                return false;
+
+        if (u->manager->unit_cache_mtime > u->fragment_loadtime)
+                return true;
 
-        return m->unit_cache_mtime > 0 &&
-                (m->unit_cache_mtime > u->fragment_loadtime || !lookup_paths_mtime_good(&m->lookup_paths, m->unit_cache_mtime));
+        return !lookup_paths_mtime_good(&u->manager->lookup_paths, u->manager->unit_cache_mtime);
 }
 
 int manager_load_unit_prepare(
@@ -1988,7 +1996,7 @@ int manager_load_unit_prepare(
                  * we need to try again - even if the cache is current, it might have been
                  * updated in a different context before we had a chance to retry loading
                  * this particular unit. */
-                if (ret->load_state == UNIT_NOT_FOUND && manager_unit_cache_needs_refresh(m, ret))
+                if (manager_unit_file_maybe_loadable_from_cache(ret))
                         ret->load_state = UNIT_STUB;
                 else {
                         *_ret = ret;
index 2cd0dacdb03d52559b642adc56c2ce2999ca20d5..81b0c13a955a7bcbbb56cc655ce74eaf24215e0f 100644 (file)
@@ -463,6 +463,7 @@ Unit *manager_get_unit(Manager *m, const char *name);
 
 int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j);
 
+bool manager_unit_file_maybe_loadable_from_cache(Unit *u);
 int manager_load_unit_prepare(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret);
 int manager_load_unit(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret);
 int manager_load_startable_unit_or_warn(Manager *m, const char *name, const char *path, Unit **ret);
index ef6470656ce22ec5a242a6ee2adf8c1862d8b7fe..4a57b8e3f9c32f65db09cff8f8b76f97f260f7d5 100644 (file)
@@ -954,6 +954,24 @@ int transaction_add_job_and_dependencies(
 
         if (type != JOB_STOP) {
                 r = bus_unit_validate_load_state(unit, e);
+                /* The time-based cache allows to start new units without daemon-reload,
+                 * but if they are already referenced (because of dependencies or ordering)
+                 * then we have to force a load of the fragment. As an optimization, check
+                 * first if anything in the usual paths was modified since the last time
+                 * the cache was loaded. Also check if the last time an attempt to load the
+                 * unit was made was before the most recent cache refresh, so that we know
+                 * we need to try again - even if the cache is current, it might have been
+                 * updated in a different context before we had a chance to retry loading
+                 * this particular unit.
+                 * Given building up the transaction is a synchronous operation, attempt
+                 * to load the unit immediately. */
+                if (r < 0 && manager_unit_file_maybe_loadable_from_cache(unit)) {
+                        unit->load_state = UNIT_STUB;
+                        r = unit_load(unit);
+                        if (r < 0 || unit->load_state == UNIT_STUB)
+                                unit->load_state = UNIT_NOT_FOUND;
+                        r = bus_unit_validate_load_state(unit, e);
+                }
                 if (r < 0)
                         return r;
         }
index 93e5e98e423ae0c24a1cafac05af673752387bf6..03231e71b183b3f1f76fac1d1bd641ad9b98404e 100755 (executable)
@@ -53,6 +53,33 @@ systemctl start testservice-48.service
 
 systemctl is-active testservice-48.service
 
+# Stop and remove, and try again to exercise the transaction setup code path by
+# having the target pull in the unloaded but available unit
+systemctl stop testservice-48.service testservice-48.target
+rm -f /run/systemd/system/testservice-48.service /run/systemd/system/testservice-48.target
+systemctl daemon-reload
+
+sleep 3.1
+
+cat > /run/systemd/system/testservice-48.target <<EOF
+[Unit]
+Conflicts=shutdown.target
+Wants=testservice-48.service
+EOF
+
+systemctl daemon-reload
+
+systemctl start testservice-48.target
+
+cat > /run/systemd/system/testservice-48.service <<EOF
+[Service]
+ExecStart=/bin/sleep infinity
+EOF
+
+systemctl restart testservice-48.target
+
+systemctl is-active testservice-48.service
+
 echo OK > /testok
 
 exit 0