diff options
author | Lennart Poettering <lennart@poettering.net> | 2021-04-14 15:24:08 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2021-05-25 16:03:03 +0200 |
commit | 0bc488c99ab2ed3464237607e381f4d72cd321d5 (patch) | |
tree | 5dfbbe4572ec4a3d945b3b4b50369e78a3c62a64 /src/core/unit.c | |
parent | core: add new OnSuccess= dependency type (diff) | |
download | systemd-0bc488c99ab2ed3464237607e381f4d72cd321d5.tar.xz systemd-0bc488c99ab2ed3464237607e381f4d72cd321d5.zip |
core: implement Uphold= dependency type
This is like a really strong version of Wants=, that keeps starting the
specified unit if it is ever found inactive.
This is an alternative to Restart= inside a unit, acknowledging the fact
that whether to keep restarting the unit is sometimes not a property of
the unit itself but the state of the system.
This implements a part of what #4263 requests. i.e. there's no
distinction between "always" and "opportunistic". We just dumbly
implement "always" and become active whenever we see no job queued for
an inactive unit that is supposed to be upheld.
Diffstat (limited to 'src/core/unit.c')
-rw-r--r-- | src/core/unit.c | 74 |
1 files changed, 71 insertions, 3 deletions
diff --git a/src/core/unit.c b/src/core/unit.c index 6892930d7b..f401a1457c 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -122,7 +122,7 @@ Unit* unit_new(Manager *m, size_t size) { u->last_section_private = -1; u->start_ratelimit = (RateLimit) { m->default_start_limit_interval, m->default_start_limit_burst }; - u->auto_stop_ratelimit = (RateLimit) { 10 * USEC_PER_SEC, 16 }; + u->auto_start_stop_ratelimit = (RateLimit) { 10 * USEC_PER_SEC, 16 }; for (CGroupIOAccountingMetric i = 0; i < _CGROUP_IO_ACCOUNTING_METRIC_MAX; i++) u->io_accounting_last[i] = UINT64_MAX; @@ -507,6 +507,22 @@ void unit_submit_to_stop_when_unneeded_queue(Unit *u) { u->in_stop_when_unneeded_queue = true; } +void unit_submit_to_start_when_upheld_queue(Unit *u) { + assert(u); + + if (u->in_start_when_upheld_queue) + return; + + if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u))) + return; + + if (!unit_has_dependency(u, UNIT_ATOM_START_STEADILY, NULL)) + return; + + LIST_PREPEND(start_when_upheld_queue, u->manager->start_when_upheld_queue, u); + u->in_start_when_upheld_queue = true; +} + static void unit_clear_dependencies(Unit *u) { assert(u); @@ -719,6 +735,9 @@ Unit* unit_free(Unit *u) { if (u->in_stop_when_unneeded_queue) LIST_REMOVE(stop_when_unneeded_queue, u->manager->stop_when_unneeded_queue, u); + if (u->in_start_when_upheld_queue) + LIST_REMOVE(start_when_upheld_queue, u->manager->start_when_upheld_queue, u); + safe_close(u->ip_accounting_ingress_map_fd); safe_close(u->ip_accounting_egress_map_fd); @@ -2011,6 +2030,36 @@ bool unit_is_unneeded(Unit *u) { return true; } +bool unit_is_upheld_by_active(Unit *u, Unit **ret_culprit) { + Unit *other; + + assert(u); + + /* Checks if the unit needs to be started because it currently is not running, but some other unit + * that is active declared an Uphold= dependencies on it */ + + if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) || u->job) { + if (ret_culprit) + *ret_culprit = NULL; + return false; + } + + UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_START_STEADILY) { + if (other->job) + continue; + + if (UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(other))) { + if (ret_culprit) + *ret_culprit = other; + return true; + } + } + + if (ret_culprit) + *ret_culprit = NULL; + return false; +} + static void check_unneeded_dependencies(Unit *u) { Unit *other; assert(u); @@ -2021,6 +2070,16 @@ static void check_unneeded_dependencies(Unit *u) { unit_submit_to_stop_when_unneeded_queue(other); } +static void check_uphold_dependencies(Unit *u) { + Unit *other; + assert(u); + + /* Add all units this unit depends on to the queue that processes Uphold= behaviour. */ + + UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_ADD_START_WHEN_UPHELD_QUEUE) + unit_submit_to_start_when_upheld_queue(other); +} + static void unit_check_binds_to(Unit *u) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; bool stop = false; @@ -2056,7 +2115,7 @@ static void unit_check_binds_to(Unit *u) { /* If stopping a unit fails continuously we might enter a stop * loop here, hence stop acting on the service being * unnecessary after a while. */ - if (!ratelimit_below(&u->auto_stop_ratelimit)) { + if (!ratelimit_below(&u->auto_start_stop_ratelimit)) { log_unit_warning(u, "Unit is bound to inactive unit %s, but not stopping since we tried this too often recently.", other->id); return; } @@ -2595,10 +2654,14 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag retroactively_stop_dependencies(u); } - /* stop unneeded units regardless if going down was expected or not */ + /* Stop unneeded units regardless if going down was expected or not */ if (UNIT_IS_INACTIVE_OR_FAILED(ns)) check_unneeded_dependencies(u); + /* Start uphold units regardless if going up was expected or not */ + if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) + check_uphold_dependencies(u); + if (ns != os && ns == UNIT_FAILED) { log_unit_debug(u, "Unit entered failed state."); @@ -2634,6 +2697,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag /* Maybe we finished startup and are now ready for being stopped because unneeded? */ unit_submit_to_stop_when_unneeded_queue(u); + /* Maybe someone wants us to remain up? */ + unit_submit_to_start_when_upheld_queue(u); + /* Maybe we finished startup, but something we needed has vanished? Let's die then. (This happens when * something BindsTo= to a Type=oneshot unit, as these units go directly from starting to inactive, * without ever entering started.) */ @@ -2901,11 +2967,13 @@ int unit_add_dependency( [UNIT_REQUISITE] = UNIT_REQUISITE_OF, [UNIT_BINDS_TO] = UNIT_BOUND_BY, [UNIT_PART_OF] = UNIT_CONSISTS_OF, + [UNIT_UPHOLDS] = UNIT_UPHELD_BY, [UNIT_REQUIRED_BY] = UNIT_REQUIRES, [UNIT_REQUISITE_OF] = UNIT_REQUISITE, [UNIT_WANTED_BY] = UNIT_WANTS, [UNIT_BOUND_BY] = UNIT_BINDS_TO, [UNIT_CONSISTS_OF] = UNIT_PART_OF, + [UNIT_UPHELD_BY] = UNIT_UPHOLDS, [UNIT_CONFLICTS] = UNIT_CONFLICTED_BY, [UNIT_CONFLICTED_BY] = UNIT_CONFLICTS, [UNIT_BEFORE] = UNIT_AFTER, |