diff options
author | Luca Boccassi <luca.boccassi@microsoft.com> | 2020-07-03 18:45:19 +0100 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2020-07-07 10:09:24 +0200 |
commit | cda667722c2218cf1a0185284d2a87f8a25f1b2d (patch) | |
tree | 0e97f4ea9ed8373ddaef00c7cd4fa866e1ceb8c2 | |
parent | 77ecc1aaa54947d4babd57110e0dee0457f7ba4e (diff) | |
download | systemd-cda667722c2218cf1a0185284d2a87f8a25f1b2d.tar.gz |
core: refresh unit cache when building a transaction if UNIT_NOT_FOUND
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.
-rw-r--r-- | src/core/manager.c | 18 | ||||
-rw-r--r-- | src/core/manager.h | 1 | ||||
-rw-r--r-- | src/core/transaction.c | 18 | ||||
-rwxr-xr-x | test/units/testsuite-48.sh | 27 |
4 files changed, 59 insertions, 5 deletions
diff --git a/src/core/manager.c b/src/core/manager.c index 7b199f5175..743ef6b4fc 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -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; diff --git a/src/core/manager.h b/src/core/manager.h index 2cd0dacdb0..81b0c13a95 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -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); diff --git a/src/core/transaction.c b/src/core/transaction.c index ef6470656c..4a57b8e3f9 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -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; } diff --git a/test/units/testsuite-48.sh b/test/units/testsuite-48.sh index 93e5e98e42..03231e71b1 100755 --- a/test/units/testsuite-48.sh +++ b/test/units/testsuite-48.sh @@ -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 |