--- src/core/dbus-unit.c | 18 +++++++- src/core/job.c | 28 ++++++++++++- src/core/job.h | 1 + src/core/load-fragment-gperf.gperf.m4 | 2 +- src/core/load-fragment.c | 4 ++ src/core/manager.c | 2 +- src/core/transaction.c | 78 +++++++++++++++++++++++++++-------- src/core/transaction.h | 1 + src/core/unit.c | 67 +++++++++++++++++++++++------- src/core/unit.h | 5 ++- 10 files changed, 169 insertions(+), 37 deletions(-)
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 7c23e1e..f40a52c 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -203,6 +203,22 @@ static int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, vo return 0; } +static int bus_unit_append_stop_when_unneeded(DBusMessageIter *i, const char *property, void *data) { + Unit *u = data; + dbus_bool_t b; + + assert(i); + assert(property); + assert(u); + + b = unit_stop_when_unneeded_state(u); + + if (!dbus_message_iter_append_basic(i, DBUS_TYPE_BOOLEAN, &b)) + return -ENOMEM; + + return 0; +} + static int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data) { Unit *u = data; dbus_bool_t b; @@ -1298,7 +1314,7 @@ const BusProperty bus_unit_properties[] = { { "CanReload", bus_unit_append_can_reload, "b", 0 }, { "CanIsolate", bus_unit_append_can_isolate, "b", 0 }, { "Job", bus_unit_append_job, "(uo)", 0 }, - { "StopWhenUnneeded", bus_property_append_bool, "b", offsetof(Unit, stop_when_unneeded) }, + { "StopWhenUnneeded", bus_unit_append_stop_when_unneeded, "b", 0 }, { "RefuseManualStart", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_start) }, { "RefuseManualStop", bus_property_append_bool, "b", offsetof(Unit, refuse_manual_stop) }, { "AllowIsolate", bus_property_append_bool, "b", offsetof(Unit, allow_isolate) }, diff --git a/src/core/job.c b/src/core/job.c index 90de550..1735e91 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -297,12 +297,14 @@ void job_dump(Job *j, FILE*f, const char *prefix) { "%s\tAction: %s -> %s\n" "%s\tState: %s\n" "%s\tForced: %s\n" - "%s\tIrreversible: %s\n", + "%s\tIrreversible: %s\n" + "%s\tTransaction Root: %s\n", prefix, j->id, prefix, j->unit->id, job_type_to_string(j->type), prefix, job_state_to_string(j->state), prefix, yes_no(j->override), - prefix, yes_no(j->irreversible)); + prefix, yes_no(j->irreversible), + prefix, yes_no(j->root)); } /* @@ -514,6 +516,21 @@ int job_run_and_invalidate(Job *j) { switch (j->type) { case JOB_START: + /* Check once again */ + if (unit_unneeded(j->unit)) { + if (j->root && j->override) { + log_debug("Setup StopWhenUnneeded=no for unit %s just before activation", + j->unit->id); + j->unit->stop_when_unneeded_runtime = false; + } else { + log_debug("Don't start unit %s as it unneeded", j->unit->id); + /* unit_notify used for dropping already started unneeded dependencies */ + unit_notify(j->unit, UNIT_ACTIVATING, UNIT_FAILED, false); + r = 0; + break; + } + } + r = unit_start(j->unit); /* If this unit cannot be started, then simply wait */ @@ -954,6 +971,7 @@ int job_serialize(Job *j, FILE *f, FDSet *fds) { fprintf(f, "job-id=%u\n", j->id); fprintf(f, "job-type=%s\n", job_type_to_string(j->type)); fprintf(f, "job-state=%s\n", job_state_to_string(j->state)); + fprintf(f, "job-root=%s\n", yes_no(j->root)); fprintf(f, "job-override=%s\n", yes_no(j->override)); fprintf(f, "job-irreversible=%s\n", yes_no(j->irreversible)); fprintf(f, "job-sent-dbus-new-signal=%s\n", yes_no(j->sent_dbus_new_signal)); @@ -1017,6 +1035,12 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) { log_debug("Failed to parse job state %s", v); else j->state = s; + } else if (streq(l, "job-root")) { + int b = parse_boolean(v); + if (b < 0) + log_debug("Failed to parse job root flag %s", v); + else + j->root = j->root || b; } else if (streq(l, "job-override")) { int b = parse_boolean(v); if (b < 0) diff --git a/src/core/job.h b/src/core/job.h index 45d0487..5cadd3f 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -163,6 +163,7 @@ struct Job { bool ignore_order:1; bool forgot_bus_clients:1; bool irreversible:1; + bool root:1; }; JobBusClient* job_bus_client_new(DBusConnection *connection, const char *name); diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 22c1761..42434dd 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -114,7 +114,7 @@ Unit.ReloadPropagatedFrom, config_parse_unit_deps, UNIT_RELOAD Unit.PropagateReloadFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0 Unit.PartOf, config_parse_unit_deps, UNIT_PART_OF, 0 Unit.RequiresMountsFor, config_parse_unit_requires_mounts_for, 0, offsetof(Unit, requires_mounts_for) -Unit.StopWhenUnneeded, config_parse_bool, 0, offsetof(Unit, stop_when_unneeded) +Unit.StopWhenUnneeded, config_parse_bool, 0, offsetof(Unit, stop_when_unneeded_unit) Unit.RefuseManualStart, config_parse_bool, 0, offsetof(Unit, refuse_manual_start) Unit.RefuseManualStop, config_parse_bool, 0, offsetof(Unit, refuse_manual_stop) Unit.AllowIsolate, config_parse_bool, 0, offsetof(Unit, allow_isolate) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 2204c67..995cfaa 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -2259,6 +2259,10 @@ static int load_from_path(Unit *u, const char *path) { u->source_mtime = 0; } + /* Syncing unit options to runtime state */ + if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) + u->stop_when_unneeded_runtime = u->stop_when_unneeded_unit; + r = 0; finish: diff --git a/src/core/manager.c b/src/core/manager.c index 5527e9d..71a061e 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -921,7 +921,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove if (!tr) return -ENOMEM; - r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, override, false, + r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, true, override, false, mode == JOB_IGNORE_DEPENDENCIES || mode == JOB_IGNORE_REQUIREMENTS, mode == JOB_IGNORE_DEPENDENCIES, e); if (r < 0) diff --git a/src/core/transaction.c b/src/core/transaction.c index 4a8d90e..a0c185b 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -739,7 +739,7 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e return 0; } -static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool override, bool *is_new) { +static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool root, bool override, bool *is_new) { Job *j, *f; assert(tr); @@ -770,6 +770,7 @@ static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, b j->matters_to_anchor = false; j->override = override; j->irreversible = tr->irreversible; + j->root = root; LIST_PREPEND(Job, transaction, f, j); @@ -821,11 +822,50 @@ static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependen } } +static bool transaction_unneeded_setup_and_check(Unit *u, JobType type, bool root, bool override) { + Iterator i; + Unit *other; + + assert(u); + + if (type != JOB_START) + return false; + + /* Follow merge if any */ + if (!unit_stop_when_unneeded_state(u)) + return false; + + SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i) + return false; + + SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) + return false; + + SET_FOREACH(other, u->dependencies[UNIT_WANTED_BY], i) + return false; + + SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i) + return false; + + /* No potential tranaction found. Dead end */ + if (root && override) { + u->stop_when_unneeded_runtime = false; + log_debug("Setup StopWhenUnneeded=no for certanly unneeded transaction for unit %s", + u->id); + return false; + } else { + log_debug("Leave StopWhenUnneeded=yes for certanly unneeded transaction for unit %s" + " and drop it", u->id); + return true; + } +} + int transaction_add_job_and_dependencies( Transaction *tr, JobType type, Unit *unit, Job *by, + bool root, bool matters, bool override, bool conflicts, @@ -875,8 +915,12 @@ int transaction_add_job_and_dependencies( return -EBADR; } - /* First add the job. */ - ret = transaction_add_one_job(tr, type, unit, override, &is_new); + /* Check, is transaction a dead end */ + if (transaction_unneeded_setup_and_check(unit, type, root, override)) + return 0; + + /* First add the root job. */ + ret = transaction_add_one_job(tr, type, unit, root, override, &is_new); if (!ret) return -ENOMEM; @@ -899,7 +943,7 @@ int transaction_add_job_and_dependencies( * add all dependencies of everybody following. */ if (unit_following_set(ret->unit, &following) > 0) { SET_FOREACH(dep, following, i) { - r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, false, override, false, false, ignore_order, e); if (r < 0) { log_warning_unit(dep->id, "Cannot add dependency job for unit %s, ignoring: %s", @@ -916,7 +960,7 @@ int transaction_add_job_and_dependencies( /* Finally, recursively add in all dependencies. */ if (type == JOB_START || type == JOB_RESTART) { SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) { - r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, true, override, false, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -927,7 +971,7 @@ int transaction_add_job_and_dependencies( } SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) { - r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, true, override, false, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -938,7 +982,7 @@ int transaction_add_job_and_dependencies( } SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) { - r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, !override, override, false, false, ignore_order, e); if (r < 0) { log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id, "Cannot add dependency job for unit %s, ignoring: %s", @@ -950,7 +994,7 @@ int transaction_add_job_and_dependencies( } SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) { - r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, false, ignore_order, e); if (r < 0) { log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id, "Cannot add dependency job for unit %s, ignoring: %s", @@ -962,7 +1006,7 @@ int transaction_add_job_and_dependencies( } SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) { - r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, false, true, override, false, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -973,7 +1017,7 @@ int transaction_add_job_and_dependencies( } SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) { - r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, false, ret, !override, override, false, false, ignore_order, e); if (r < 0) { log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id, "Cannot add dependency job for unit %s, ignoring: %s", @@ -985,7 +1029,7 @@ int transaction_add_job_and_dependencies( } SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) { - r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, true, override, true, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -996,7 +1040,7 @@ int transaction_add_job_and_dependencies( } SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) { - r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, false, override, false, false, ignore_order, e); if (r < 0) { log_warning_unit(dep->id, "Cannot add dependency job for unit %s, ignoring: %s", @@ -1012,7 +1056,7 @@ int transaction_add_job_and_dependencies( if (type == JOB_STOP || type == JOB_RESTART) { SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i) { - r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, true, override, false, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -1023,7 +1067,7 @@ int transaction_add_job_and_dependencies( } SET_FOREACH(dep, ret->unit->dependencies[UNIT_BOUND_BY], i) { - r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, true, override, false, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -1034,7 +1078,7 @@ int transaction_add_job_and_dependencies( } SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONSISTS_OF], i) { - r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, true, override, false, false, ignore_order, e); if (r < 0) { if (r != -EBADR) goto fail; @@ -1049,7 +1093,7 @@ int transaction_add_job_and_dependencies( if (type == JOB_RELOAD) { SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) { - r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e); + r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, false, override, false, false, ignore_order, e); if (r < 0) { log_warning_unit(dep->id, "Cannot add dependency reload job for unit %s, ignoring: %s", @@ -1096,7 +1140,7 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) { if (hashmap_get(tr->jobs, u)) continue; - r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL); + r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, false, true, false, false, false, false, NULL); if (r < 0) log_warning_unit(u->id, "Cannot add isolate job for unit %s, ignoring: %s", diff --git a/src/core/transaction.h b/src/core/transaction.h index 12f9194..c8b2c56 100644 --- a/src/core/transaction.h +++ b/src/core/transaction.h @@ -44,6 +44,7 @@ int transaction_add_job_and_dependencies( JobType type, Unit *unit, Job *by, + bool root, bool matters, bool override, bool conflicts, diff --git a/src/core/unit.c b/src/core/unit.c index 601be60..3a2d0a7 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -438,6 +438,19 @@ const char* unit_sub_state_to_string(Unit *u) { return UNIT_VTABLE(u)->sub_state_to_string(u); } +bool unit_stop_when_unneeded_state(Unit *u) +{ + assert(u); + + if (u->load_state == UNIT_MERGED) + return unit_stop_when_unneeded_state(unit_follow_merge(u)); + + if (UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) + return u->stop_when_unneeded_runtime; + else + return u->stop_when_unneeded_unit; +} + static void complete_move(Set **s, Set **other) { assert(s); assert(other); @@ -736,7 +749,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tOnFailureIsolate: %s\n" "%s\tIgnoreOnIsolate: %s\n" "%s\tIgnoreOnSnapshot: %s\n", - prefix, yes_no(u->stop_when_unneeded), + prefix, yes_no(unit_stop_when_unneeded_state(u)), prefix, yes_no(u->refuse_manual_start), prefix, yes_no(u->refuse_manual_stop), prefix, yes_no(u->default_dependencies), @@ -1114,6 +1127,9 @@ int unit_stop(Unit *u) { assert(u); + /* Drop stop_when_unneeded anyway */ + u->stop_when_unneeded_runtime = u->stop_when_unneeded_unit; + state = unit_active_state(u); if (UNIT_IS_INACTIVE_OR_FAILED(state)) return -EALREADY; @@ -1181,36 +1197,47 @@ bool unit_can_reload(Unit *u) { return UNIT_VTABLE(u)->can_reload(u); } -static void unit_check_unneeded(Unit *u) { +bool unit_unneeded(Unit *u) { Iterator i; Unit *other; assert(u); - /* If this service shall be shut down when unneeded then do - * so. */ - - if (!u->stop_when_unneeded) - return; + if (u->load_state == UNIT_MERGED) + return unit_unneeded(unit_follow_merge(u)); - if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) - return; + if (!unit_stop_when_unneeded_state(u)) + return false; SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i) if (unit_pending_active(other)) - return; + return false; SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i) if (unit_pending_active(other)) - return; + return false; SET_FOREACH(other, u->dependencies[UNIT_WANTED_BY], i) if (unit_pending_active(other)) - return; + return false; SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i) if (unit_pending_active(other)) - return; + return false; + + return true; +} + +static void unit_check_unneeded(Unit *u) { + + /* If this service shall be shut down when unneeded then do + * so. */ + + if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) + return; + + if (! unit_unneeded(u)) + return; log_info("Service %s is not needed anymore. Stopping.", u->id); @@ -1455,8 +1482,10 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su } /* stop unneeded units regardless if going down was expected or not */ - if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) + if (UNIT_IS_ACTIVE_OR_ACTIVATING(os) && UNIT_IS_INACTIVE_OR_DEACTIVATING(ns)) { + log_debug("Check dependencies when n_reloading: %d", u->manager->n_reloading); check_unneeded_dependencies(u); + } if (ns != os && ns == UNIT_FAILED) { log_struct_unit(LOG_NOTICE, @@ -2356,6 +2385,7 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { dual_timestamp_serialize(f, "active-exit-timestamp", &u->active_exit_timestamp); dual_timestamp_serialize(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp); dual_timestamp_serialize(f, "condition-timestamp", &u->condition_timestamp); + unit_serialize_item(u, f, "stop-when-unneeded-runtime", yes_no(u->stop_when_unneeded_runtime)); if (dual_timestamp_is_set(&u->condition_timestamp)) unit_serialize_item(u, f, "condition-result", yes_no(u->condition_result)); @@ -2485,6 +2515,15 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { u->condition_result = b; continue; + } else if (streq(l, "stop-when-unneeded-runtime")) { + int b; + + if ((b = parse_boolean(v)) < 0) + log_debug("Failed to parse StopWhenUnneeded runtime state %s", v); + else + u->stop_when_unneeded_runtime = b; + + continue; } if ((r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds)) < 0) diff --git a/src/core/unit.h b/src/core/unit.h index 9029d62..66d77ca 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -200,7 +200,8 @@ struct Unit { UnitFileState unit_file_state; /* Garbage collect us we nobody wants or requires us anymore */ - bool stop_when_unneeded; + bool stop_when_unneeded_unit; + bool stop_when_unneeded_runtime; /* Create default dependencies */ bool default_dependencies; @@ -480,10 +481,12 @@ void unit_dump(Unit *u, FILE *f, const char *prefix); bool unit_can_reload(Unit *u); bool unit_can_start(Unit *u); bool unit_can_isolate(Unit *u); +bool unit_stop_when_unneeded_state(Unit *u); int unit_start(Unit *u); int unit_stop(Unit *u); int unit_reload(Unit *u); +bool unit_unneeded(Unit *u); int unit_kill(Unit *u, KillWho w, int signo, DBusError *error); -- 1.8.1.2 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel