unit: replace StopRetroactively= by BindTo= dependencies

The property StopRetroactively= needs to be per-dependency, not
per-unit, in order to properly express dependencies between .mount units
and its .device and fsck .service units. If the .device unit is
unplugged the mount should go away, but if the fsck process terminates
the .mount should stay.
This commit is contained in:
Lennart Poettering 2010-10-28 23:18:47 +02:00
parent 941a4041bd
commit b81884e746
11 changed files with 73 additions and 51 deletions

2
TODO
View File

@ -70,7 +70,7 @@
* only add quotacheck deps to .mount units which mention grpquota/usrquota in the mount flags
* Introduce weaker Conflicts.
* Introduce weaker Conflicts. get rid of ignore_dependency_failure
External:

View File

@ -281,7 +281,6 @@
services.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>RequiresOverridable=</varname></term>
@ -340,6 +339,23 @@
details see above.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>BindTo=</varname></term>
<listitem><para>Configures requirement
dependencies, very similar in style to
<varname>Requires=</varname>, however
in addition to this behaviour it also
declares that this unit is stopped
when any of the units listed suddenly
disappears. Units can suddenly,
unexpectedly disappear if a service
terminates on its own choice, a device
is unplugged or a mount point
unmounted with involvement of
systemd.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Conflicts=</varname></term>
@ -440,28 +456,6 @@
state.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>StopRetroactively=</varname></term>
<listitem><para>Takes a boolean
argument. If <option>true</option> and
a unit this unit requires stops
without this being requested by the
user, this unit will be stopped as
well. (e.g. if a service exits or
crashes on its own behalf, units this
flag is set for that require it will
be stopped.) Note that normally if a
unit stops without a user request,
units depending on it will not be
terminated. Only if the user requested
shutdown of a unit, all units
depending on that unit will be shut
down as well and at the same
time. Defaults to
<option>false</option>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>StopWhenUnneeded=</varname></term>
@ -630,7 +624,7 @@
side matching. If multiple conditions
are specified the unit will be
executed iff at least one of them
apply (i.e. a logical OR is
applies (i.e. a logical OR is
applied).</para></listitem>
</varlistentry>
</variablelist>

View File

@ -70,9 +70,11 @@
" <property name=\"Requisite\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"RequisiteOverridable\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"Wants\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"BindTo\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"RequiredBy\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"RequiredByOverridable\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"WantedBy\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"BoundBy\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"Conflicts\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"ConflictedBy\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"Before\" type=\"as\" access=\"read\"/>\n" \
@ -92,7 +94,6 @@
" <property name=\"CanStop\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"CanIsolate\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"Job\" type=\"(uo)\" access=\"read\"/>\n" \
" <property name=\"StopRetroactively\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"StopWhenUnneeded\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"RefuseManualStart\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"RefuseManualStop\" type=\"b\" access=\"read\"/>\n" \
@ -112,9 +113,11 @@
{ "org.freedesktop.systemd1.Unit", "Requisite", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUISITE] }, \
{ "org.freedesktop.systemd1.Unit", "RequisiteOverridable", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUISITE_OVERRIDABLE] }, \
{ "org.freedesktop.systemd1.Unit", "Wants", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_WANTS] }, \
{ "org.freedesktop.systemd1.Unit", "BindTo", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_BIND_TO] }, \
{ "org.freedesktop.systemd1.Unit", "RequiredBy", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUIRED_BY] }, \
{ "org.freedesktop.systemd1.Unit", "RequiredByOverridable",bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_REQUIRED_BY_OVERRIDABLE] }, \
{ "org.freedesktop.systemd1.Unit", "WantedBy", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_WANTED_BY] }, \
{ "org.freedesktop.systemd1.Unit", "BoundBy", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_BOUND_BY] }, \
{ "org.freedesktop.systemd1.Unit", "Conflicts", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_CONFLICTS] }, \
{ "org.freedesktop.systemd1.Unit", "ConflictedBy", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_CONFLICTED_BY] }, \
{ "org.freedesktop.systemd1.Unit", "Before", bus_unit_append_dependencies, "as", u->meta.dependencies[UNIT_BEFORE] }, \
@ -134,7 +137,6 @@
{ "org.freedesktop.systemd1.Unit", "CanReload", bus_unit_append_can_reload, "b", u }, \
{ "org.freedesktop.systemd1.Unit", "CanIsolate", bus_unit_append_can_isolate, "b", u }, \
{ "org.freedesktop.systemd1.Unit", "Job", bus_unit_append_job, "(uo)", u }, \
{ "org.freedesktop.systemd1.Unit", "StopRetroactively", bus_property_append_bool, "b", &u->meta.stop_retroactively }, \
{ "org.freedesktop.systemd1.Unit", "StopWhenUnneeded", bus_property_append_bool, "b", &u->meta.stop_when_unneeded }, \
{ "org.freedesktop.systemd1.Unit", "RefuseManualStart", bus_property_append_bool, "b", &u->meta.refuse_manual_start }, \
{ "org.freedesktop.systemd1.Unit", "RefuseManualStop", bus_property_append_bool, "b", &u->meta.refuse_manual_stop }, \

View File

@ -1765,11 +1765,11 @@ static int load_from_path(Unit *u, const char *path) {
{ "Requisite", config_parse_deps, UINT_TO_PTR(UNIT_REQUISITE), "Unit" },
{ "RequisiteOverridable", config_parse_deps, UINT_TO_PTR(UNIT_REQUISITE_OVERRIDABLE), "Unit" },
{ "Wants", config_parse_deps, UINT_TO_PTR(UNIT_WANTS), "Unit" },
{ "BindTo", config_parse_deps, UINT_TO_PTR(UNIT_BIND_TO), "Unit" },
{ "Conflicts", config_parse_deps, UINT_TO_PTR(UNIT_CONFLICTS), "Unit" },
{ "Before", config_parse_deps, UINT_TO_PTR(UNIT_BEFORE), "Unit" },
{ "After", config_parse_deps, UINT_TO_PTR(UNIT_AFTER), "Unit" },
{ "OnFailure", config_parse_deps, UINT_TO_PTR(UNIT_ON_FAILURE), "Unit" },
{ "StopRetroactively", config_parse_bool, &u->meta.stop_retroactively, "Unit" },
{ "StopWhenUnneeded", config_parse_bool, &u->meta.stop_when_unneeded, "Unit" },
{ "RefuseManualStart", config_parse_bool, &u->meta.refuse_manual_start, "Unit" },
{ "RefuseManualStop", config_parse_bool, &u->meta.refuse_manual_stop, "Unit" },

View File

@ -1446,6 +1446,10 @@ static int transaction_add_job_and_dependencies(
if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, false, e, NULL)) < 0 && r != -EBADR)
goto fail;
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_BIND_TO], i)
if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, false, e, NULL)) < 0 && r != -EBADR)
goto fail;
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !override, override, false, e, NULL)) < 0 && r != -EBADR) {
log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r));
@ -1487,6 +1491,10 @@ static int transaction_add_job_and_dependencies(
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRED_BY], i)
if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, false, e, NULL)) < 0 && r != -EBADR)
goto fail;
SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_BOUND_BY], i)
if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, false, e, NULL)) < 0 && r != -EBADR)
goto fail;
}
/* JOB_VERIFY_STARTED, JOB_RELOAD require no dependency handling */

View File

@ -70,9 +70,6 @@ static void mount_init(Unit *u) {
* already trying to comply its last one. */
m->exec_context.same_pgrp = true;
/* Make sure we unmount when the devices we require go away */
m->meta.stop_retroactively = true;
m->timer_watch.type = WATCH_INVALID;
m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;

View File

@ -652,13 +652,11 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
if (u->meta.load_state == UNIT_LOADED) {
fprintf(f,
"%s\tStopRetroactively: %s\n"
"%s\tStopWhenUnneeded: %s\n"
"%s\tRefuseManualStart: %s\n"
"%s\tRefuseManualStop: %s\n"
"%s\tDefaultDependencies: %s\n"
"%s\tIgnoreDependencyFailure: %s\n",
prefix, yes_no(u->meta.stop_retroactively),
prefix, yes_no(u->meta.stop_when_unneeded),
prefix, yes_no(u->meta.refuse_manual_start),
prefix, yes_no(u->meta.refuse_manual_stop),
@ -768,6 +766,10 @@ static int unit_add_default_dependencies(Unit *u) {
if ((r = unit_add_default_target_dependency(u, target)) < 0)
return r;
SET_FOREACH(target, u->meta.dependencies[UNIT_BOUND_BY], i)
if ((r = unit_add_default_target_dependency(u, target)) < 0)
return r;
return 0;
}
@ -966,6 +968,10 @@ static void unit_check_unneeded(Unit *u) {
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
return;
SET_FOREACH(other, u->meta.dependencies[UNIT_BOUND_BY], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
return;
log_info("Service %s is not needed anymore. Stopping.", u->meta.id);
/* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */
@ -980,27 +986,36 @@ static void retroactively_start_dependencies(Unit *u) {
assert(UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)));
SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES], i)
if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
if (!set_get(u->meta.dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
SET_FOREACH(other, u->meta.dependencies[UNIT_BIND_TO], i)
if (!set_get(u->meta.dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
if (!set_get(u->meta.dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
manager_add_job(u->meta.manager, JOB_START, other, JOB_FAIL, false, NULL, NULL);
SET_FOREACH(other, u->meta.dependencies[UNIT_REQUISITE], i)
if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
if (!set_get(u->meta.dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
manager_add_job(u->meta.manager, JOB_START, other, JOB_REPLACE, true, NULL, NULL);
SET_FOREACH(other, u->meta.dependencies[UNIT_WANTS], i)
if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
if (!set_get(u->meta.dependencies[UNIT_AFTER], other) &&
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
manager_add_job(u->meta.manager, JOB_START, other, JOB_FAIL, false, NULL, NULL);
SET_FOREACH(other, u->meta.dependencies[UNIT_CONFLICTS], i)
if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
SET_FOREACH(other, u->meta.dependencies[UNIT_CONFLICTED_BY], i)
if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
}
@ -1011,10 +1026,9 @@ static void retroactively_stop_dependencies(Unit *u) {
assert(u);
assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)));
/* Pull down units which need us recursively if enabled */
SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY], i)
if (other->meta.stop_retroactively &&
!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
/* Pull down units which are bound to us recursively if enabled */
SET_FOREACH(other, u->meta.dependencies[UNIT_BOUND_BY], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
manager_add_job(u->meta.manager, JOB_STOP, other, JOB_REPLACE, true, NULL, NULL);
/* Garbage collect services that might not be needed anymore, if enabled */
@ -1033,6 +1047,9 @@ static void retroactively_stop_dependencies(Unit *u) {
SET_FOREACH(other, u->meta.dependencies[UNIT_REQUISITE_OVERRIDABLE], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
SET_FOREACH(other, u->meta.dependencies[UNIT_BIND_TO], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
}
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) {
@ -1397,9 +1414,11 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen
[UNIT_WANTS] = UNIT_WANTED_BY,
[UNIT_REQUISITE] = UNIT_REQUIRED_BY,
[UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
[UNIT_BIND_TO] = UNIT_BOUND_BY,
[UNIT_REQUIRED_BY] = _UNIT_DEPENDENCY_INVALID,
[UNIT_REQUIRED_BY_OVERRIDABLE] = _UNIT_DEPENDENCY_INVALID,
[UNIT_WANTED_BY] = _UNIT_DEPENDENCY_INVALID,
[UNIT_BOUND_BY] = UNIT_BIND_TO,
[UNIT_CONFLICTS] = UNIT_CONFLICTED_BY,
[UNIT_CONFLICTED_BY] = UNIT_CONFLICTS,
[UNIT_BEFORE] = UNIT_AFTER,
@ -1426,7 +1445,8 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen
(d == UNIT_REQUIRES ||
d == UNIT_REQUIRES_OVERRIDABLE ||
d == UNIT_REQUISITE ||
d == UNIT_REQUISITE_OVERRIDABLE)) {
d == UNIT_REQUISITE_OVERRIDABLE ||
d == UNIT_BIND_TO)) {
return -EINVAL;
}
@ -2129,7 +2149,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants) {
if (r < 0)
return r;
if ((r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, device, true)) < 0)
if ((r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_BIND_TO, device, true)) < 0)
return r;
if (wants)
@ -2313,9 +2333,11 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
[UNIT_REQUISITE_OVERRIDABLE] = "RequisiteOverridable",
[UNIT_REQUIRED_BY] = "RequiredBy",
[UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
[UNIT_BIND_TO] = "BindTo",
[UNIT_WANTED_BY] = "WantedBy",
[UNIT_CONFLICTS] = "Conflicts",
[UNIT_CONFLICTED_BY] = "ConflictedBy",
[UNIT_BOUND_BY] = "BoundBy",
[UNIT_BEFORE] = "Before",
[UNIT_AFTER] = "After",
[UNIT_REFERENCES] = "References",

View File

@ -102,11 +102,13 @@ enum UnitDependency {
UNIT_REQUISITE,
UNIT_REQUISITE_OVERRIDABLE,
UNIT_WANTS,
UNIT_BIND_TO,
/* Inverse of the above */
UNIT_REQUIRED_BY, /* inverse of 'requires' and 'requisite' is 'required_by' */
UNIT_REQUIRED_BY_OVERRIDABLE, /* inverse of 'requires_overridable' and 'requisite_overridable' is 'soft_required_by' */
UNIT_WANTED_BY, /* inverse of 'wants' */
UNIT_BOUND_BY, /* inverse of 'bind_to' */
/* Negative dependencies */
UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicted_by' */
@ -191,9 +193,6 @@ struct Meta {
/* Error code when we didn't manage to load the unit (negative) */
int load_error;
/* If some required dep goes down, pull down ourselves, too */
bool stop_retroactively;
/* Garbage collect us we nobody wants or requires us anymore */
bool stop_when_unneeded;

View File

@ -8,7 +8,7 @@
[Unit]
Description=File System Check on %f
DefaultDependencies=no
Requires=%i.device
BindTo=%i.device
After=systemd-readahead-collect.service systemd-readahead-replay.service %i.device
Before=local-fs.target shutdown.target

View File

@ -14,7 +14,7 @@ m4_ifdef(`TARGET_ARCH', `m4_define(`GETTY', `/sbin/agetty -8 38400')')m4_dnl
m4_dnl
[Unit]
Description=Getty on %I
Requires=dev-%i.device
BindTo=dev-%i.device
After=dev-%i.device
m4_ifdef(`TARGET_FEDORA',
After=rc-local.service

View File

@ -7,7 +7,7 @@
[Unit]
Description=Serial Getty on %I
Requires=dev-%i.device
BindTo=dev-%i.device
After=dev-%i.device
m4_ifdef(`TARGET_FEDORA',
After=rc-local.service