mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-10 01:17:44 +03:00
Merge pull request #19322 from poettering/dep-split
core: rework dependency system to be based on atoms + add three new dep types
This commit is contained in:
commit
c473437862
@ -1630,6 +1630,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as OnFailure = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as OnFailureOf = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as OnSuccess = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as OnSuccessOf = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as Triggers = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as TriggeredBy = ['...', ...];
|
||||
@ -1638,8 +1644,14 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as ReloadPropagatedFrom = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as PropagatesStopTo = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as StopPropagatedFrom = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as JoinsNamespaceOf = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as SliceOf = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as RequiresMountsFor = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as Documentation = ['...', ...];
|
||||
@ -1694,6 +1706,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly b DefaultDependencies = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s OnSuccesJobMode = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s OnFailureJobMode = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly b IgnoreOnIsolate = ...;
|
||||
@ -1771,10 +1785,22 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<!--property ConsistsOf is not documented!-->
|
||||
|
||||
<!--property OnFailureOf is not documented!-->
|
||||
|
||||
<!--property OnSuccess is not documented!-->
|
||||
|
||||
<!--property OnSuccessOf is not documented!-->
|
||||
|
||||
<!--property ReloadPropagatedFrom is not documented!-->
|
||||
|
||||
<!--property PropagatesStopTo is not documented!-->
|
||||
|
||||
<!--property StopPropagatedFrom is not documented!-->
|
||||
|
||||
<!--property JoinsNamespaceOf is not documented!-->
|
||||
|
||||
<!--property SliceOf is not documented!-->
|
||||
|
||||
<!--property FreezerState is not documented!-->
|
||||
|
||||
<!--property DropInPaths is not documented!-->
|
||||
@ -1789,6 +1815,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<!--property CanFreeze is not documented!-->
|
||||
|
||||
<!--property OnSuccesJobMode is not documented!-->
|
||||
|
||||
<!--property OnFailureJobMode is not documented!-->
|
||||
|
||||
<!--property JobRunningTimeoutUSec is not documented!-->
|
||||
@ -1901,6 +1929,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="OnFailure"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="OnFailureOf"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="OnSuccess"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="OnSuccessOf"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="Triggers"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="TriggeredBy"/>
|
||||
@ -1909,8 +1943,14 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ReloadPropagatedFrom"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="PropagatesStopTo"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="StopPropagatedFrom"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="JoinsNamespaceOf"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="SliceOf"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RequiresMountsFor"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="Documentation"/>
|
||||
@ -1979,6 +2019,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="DefaultDependencies"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="OnSuccesJobMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="OnFailureJobMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="IgnoreOnIsolate"/>
|
||||
|
@ -708,6 +708,24 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Upholds=</varname></term>
|
||||
|
||||
<listitem><para>Configures dependencies similar to <varname>Wants=</varname>, but as long a this unit
|
||||
is up, all units listed in <varname>Upholds=</varname> are started whenever found to be inactive or
|
||||
failed, and no job is queued for them. While a <varname>Wants=</varname> dependency on another unit
|
||||
has a one-time effect when this units started, a <varname>Upholds=</varname> dependency on it has a
|
||||
continuous effect, constantly restarting the unit if necessary. This is an alternative to the
|
||||
<varname>Restart=</varname> setting of service units, to ensure they are kept running whatever
|
||||
happens.</para>
|
||||
|
||||
<para>When <varname>Upholds=b.service</varname> is used on <filename>a.service</filename>, this
|
||||
dependency will show as <varname>UpheldBy=a.service</varname> in the property listing of
|
||||
<filename>b.service</filename>. The <varname>UpheldBy=</varname> dependency cannot be specified
|
||||
directly.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Conflicts=</varname></term>
|
||||
|
||||
@ -776,24 +794,36 @@
|
||||
<varlistentry>
|
||||
<term><varname>OnFailure=</varname></term>
|
||||
|
||||
<listitem><para>A space-separated list of one or more units
|
||||
that are activated when this unit enters the
|
||||
<literal>failed</literal> state. A service unit using
|
||||
<varname>Restart=</varname> enters the failed state only after
|
||||
the start limits are reached.</para></listitem>
|
||||
<listitem><para>A space-separated list of one or more units that are activated when this unit enters
|
||||
the <literal>failed</literal> state. A service unit using <varname>Restart=</varname> enters the
|
||||
failed state only after the start limits are reached.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>OnSuccess=</varname></term>
|
||||
|
||||
<listitem><para>A space-separated list of one or more units that are activated when this unit enters
|
||||
the <literal>inactive</literal> state.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>PropagatesReloadTo=</varname></term>
|
||||
<term><varname>ReloadPropagatedFrom=</varname></term>
|
||||
|
||||
<listitem><para>A space-separated list of one or more units
|
||||
where reload requests on this unit will be propagated to, or
|
||||
reload requests on the other unit will be propagated to this
|
||||
unit, respectively. Issuing a reload request on a unit will
|
||||
automatically also enqueue a reload request on all units that
|
||||
the reload request shall be propagated to via these two
|
||||
settings.</para></listitem>
|
||||
<listitem><para>A space-separated list of one or more units to which reload requests from this unit
|
||||
shall be propagated to, or units from which reload requests shall be propagated to this unit,
|
||||
respectively. Issuing a reload request on a unit will automatically also enqueue reload requests on
|
||||
all units that are linked to it using these two settings.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>PropagatesStopTo=</varname></term>
|
||||
<term><varname>StopPropagatedFrom=</varname></term>
|
||||
|
||||
<listitem><para>A space-separated list of one or more units to which stop requests from this unit
|
||||
shall be propagated to, or units from which stop requests shall be propagated to this unit,
|
||||
respectively. Issuing a stop request on a unit will automatically also enqueue stop requests on all
|
||||
units that are linked to it using these two settings.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
@ -51,6 +51,7 @@ typedef struct {
|
||||
|
||||
#define _IDX_ITERATOR_FIRST (UINT_MAX - 1)
|
||||
#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL })
|
||||
#define ITERATOR_IS_FIRST(i) ((i).idx == _IDX_ITERATOR_FIRST)
|
||||
|
||||
/* Macros for type checking */
|
||||
#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \
|
||||
|
@ -265,23 +265,32 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
|
||||
[UNIT_WANTS] = "Wants",
|
||||
[UNIT_BINDS_TO] = "BindsTo",
|
||||
[UNIT_PART_OF] = "PartOf",
|
||||
[UNIT_UPHOLDS] = "Upholds",
|
||||
[UNIT_REQUIRED_BY] = "RequiredBy",
|
||||
[UNIT_REQUISITE_OF] = "RequisiteOf",
|
||||
[UNIT_WANTED_BY] = "WantedBy",
|
||||
[UNIT_BOUND_BY] = "BoundBy",
|
||||
[UNIT_UPHELD_BY] = "UpheldBy",
|
||||
[UNIT_CONSISTS_OF] = "ConsistsOf",
|
||||
[UNIT_CONFLICTS] = "Conflicts",
|
||||
[UNIT_CONFLICTED_BY] = "ConflictedBy",
|
||||
[UNIT_BEFORE] = "Before",
|
||||
[UNIT_AFTER] = "After",
|
||||
[UNIT_ON_SUCCESS] = "OnSuccess",
|
||||
[UNIT_ON_SUCCESS_OF] = "OnSuccessOf",
|
||||
[UNIT_ON_FAILURE] = "OnFailure",
|
||||
[UNIT_ON_FAILURE_OF] = "OnFailureOf",
|
||||
[UNIT_TRIGGERS] = "Triggers",
|
||||
[UNIT_TRIGGERED_BY] = "TriggeredBy",
|
||||
[UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
|
||||
[UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
|
||||
[UNIT_PROPAGATES_STOP_TO] = "PropagatesStopTo",
|
||||
[UNIT_STOP_PROPAGATED_FROM] = "StopPropagatedFrom",
|
||||
[UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
|
||||
[UNIT_REFERENCES] = "References",
|
||||
[UNIT_REFERENCED_BY] = "ReferencedBy",
|
||||
[UNIT_IN_SLICE] = "InSlice",
|
||||
[UNIT_SLICE_OF] = "SliceOf",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);
|
||||
|
@ -211,6 +211,7 @@ typedef enum UnitDependency {
|
||||
UNIT_WANTS,
|
||||
UNIT_BINDS_TO,
|
||||
UNIT_PART_OF,
|
||||
UNIT_UPHOLDS,
|
||||
|
||||
/* Inverse of the above */
|
||||
UNIT_REQUIRED_BY, /* inverse of 'requires' is 'required_by' */
|
||||
@ -218,6 +219,7 @@ typedef enum UnitDependency {
|
||||
UNIT_WANTED_BY, /* inverse of 'wants' */
|
||||
UNIT_BOUND_BY, /* inverse of 'binds_to' */
|
||||
UNIT_CONSISTS_OF, /* inverse of 'part_of' */
|
||||
UNIT_UPHELD_BY, /* inverse of 'uphold' */
|
||||
|
||||
/* Negative dependencies */
|
||||
UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicted_by' */
|
||||
@ -227,8 +229,11 @@ typedef enum UnitDependency {
|
||||
UNIT_BEFORE, /* inverse of 'before' is 'after' and vice versa */
|
||||
UNIT_AFTER,
|
||||
|
||||
/* On Failure */
|
||||
/* OnSuccess= + OnFailure= */
|
||||
UNIT_ON_SUCCESS,
|
||||
UNIT_ON_SUCCESS_OF,
|
||||
UNIT_ON_FAILURE,
|
||||
UNIT_ON_FAILURE_OF,
|
||||
|
||||
/* Triggers (i.e. a socket triggers a service) */
|
||||
UNIT_TRIGGERS,
|
||||
@ -238,6 +243,10 @@ typedef enum UnitDependency {
|
||||
UNIT_PROPAGATES_RELOAD_TO,
|
||||
UNIT_RELOAD_PROPAGATED_FROM,
|
||||
|
||||
/* Propagate stops */
|
||||
UNIT_PROPAGATES_STOP_TO,
|
||||
UNIT_STOP_PROPAGATED_FROM,
|
||||
|
||||
/* Joins namespace of */
|
||||
UNIT_JOINS_NAMESPACE_OF,
|
||||
|
||||
@ -245,6 +254,10 @@ typedef enum UnitDependency {
|
||||
UNIT_REFERENCES, /* Inverse of 'references' is 'referenced_by' */
|
||||
UNIT_REFERENCED_BY,
|
||||
|
||||
/* Slice= */
|
||||
UNIT_IN_SLICE,
|
||||
UNIT_SLICE_OF,
|
||||
|
||||
_UNIT_DEPENDENCY_MAX,
|
||||
_UNIT_DEPENDENCY_INVALID = -EINVAL,
|
||||
} UnitDependency;
|
||||
|
@ -422,7 +422,7 @@ static int bpf_firewall_prepare_access_maps(
|
||||
assert(ret_ipv6_map_fd);
|
||||
assert(ret_has_any);
|
||||
|
||||
for (p = u; p; p = UNIT_DEREF(p->slice)) {
|
||||
for (p = u; p; p = UNIT_GET_SLICE(p)) {
|
||||
CGroupContext *cc;
|
||||
|
||||
cc = unit_get_cgroup_context(p);
|
||||
@ -463,7 +463,7 @@ static int bpf_firewall_prepare_access_maps(
|
||||
return ipv6_map_fd;
|
||||
}
|
||||
|
||||
for (p = u; p; p = UNIT_DEREF(p->slice)) {
|
||||
for (p = u; p; p = UNIT_GET_SLICE(p)) {
|
||||
CGroupContext *cc;
|
||||
|
||||
cc = unit_get_cgroup_context(p);
|
||||
|
@ -680,7 +680,7 @@ int cgroup_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const
|
||||
if (c && c->entry##_set) \
|
||||
return c->entry; \
|
||||
\
|
||||
while ((u = UNIT_DEREF(u->slice))) { \
|
||||
while ((u = UNIT_GET_SLICE(u))) { \
|
||||
c = unit_get_cgroup_context(u); \
|
||||
if (c && c->default_##entry##_set) \
|
||||
return c->default_##entry; \
|
||||
@ -1549,7 +1549,7 @@ static bool unit_get_needs_bpf_firewall(Unit *u) {
|
||||
return true;
|
||||
|
||||
/* If any parent slice has an IP access list defined, it applies too */
|
||||
for (p = UNIT_DEREF(u->slice); p; p = UNIT_DEREF(p->slice)) {
|
||||
for (p = UNIT_GET_SLICE(u); p; p = UNIT_GET_SLICE(p)) {
|
||||
c = unit_get_cgroup_context(p);
|
||||
if (!c)
|
||||
return false;
|
||||
@ -1700,12 +1700,10 @@ CGroupMask unit_get_members_mask(Unit *u) {
|
||||
u->cgroup_members_mask = 0;
|
||||
|
||||
if (u->type == UNIT_SLICE) {
|
||||
void *v;
|
||||
Unit *member;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE])
|
||||
if (UNIT_DEREF(member->slice) == u)
|
||||
u->cgroup_members_mask |= unit_get_subtree_mask(member); /* note that this calls ourselves again, for the children */
|
||||
UNIT_FOREACH_DEPENDENCY(member, u, UNIT_ATOM_SLICE_OF)
|
||||
u->cgroup_members_mask |= unit_get_subtree_mask(member); /* note that this calls ourselves again, for the children */
|
||||
}
|
||||
|
||||
u->cgroup_members_mask_valid = true;
|
||||
@ -1713,14 +1711,16 @@ CGroupMask unit_get_members_mask(Unit *u) {
|
||||
}
|
||||
|
||||
CGroupMask unit_get_siblings_mask(Unit *u) {
|
||||
Unit *slice;
|
||||
assert(u);
|
||||
|
||||
/* Returns the mask of controllers all of the unit's siblings
|
||||
* require, i.e. the members mask of the unit's parent slice
|
||||
* if there is one. */
|
||||
|
||||
if (UNIT_ISSET(u->slice))
|
||||
return unit_get_members_mask(UNIT_DEREF(u->slice));
|
||||
slice = UNIT_GET_SLICE(u);
|
||||
if (slice)
|
||||
return unit_get_members_mask(slice);
|
||||
|
||||
return unit_get_subtree_mask(u); /* we are the top-level slice */
|
||||
}
|
||||
@ -1737,6 +1737,7 @@ static CGroupMask unit_get_disable_mask(Unit *u) {
|
||||
|
||||
CGroupMask unit_get_ancestor_disable_mask(Unit *u) {
|
||||
CGroupMask mask;
|
||||
Unit *slice;
|
||||
|
||||
assert(u);
|
||||
mask = unit_get_disable_mask(u);
|
||||
@ -1744,8 +1745,9 @@ CGroupMask unit_get_ancestor_disable_mask(Unit *u) {
|
||||
/* Returns the mask of controllers which are marked as forcibly
|
||||
* disabled in any ancestor unit or the unit in question. */
|
||||
|
||||
if (UNIT_ISSET(u->slice))
|
||||
mask |= unit_get_ancestor_disable_mask(UNIT_DEREF(u->slice));
|
||||
slice = UNIT_GET_SLICE(u);
|
||||
if (slice)
|
||||
mask |= unit_get_ancestor_disable_mask(slice);
|
||||
|
||||
return mask;
|
||||
}
|
||||
@ -1787,13 +1789,16 @@ CGroupMask unit_get_enable_mask(Unit *u) {
|
||||
}
|
||||
|
||||
void unit_invalidate_cgroup_members_masks(Unit *u) {
|
||||
Unit *slice;
|
||||
|
||||
assert(u);
|
||||
|
||||
/* Recurse invalidate the member masks cache all the way up the tree */
|
||||
u->cgroup_members_mask_valid = false;
|
||||
|
||||
if (UNIT_ISSET(u->slice))
|
||||
unit_invalidate_cgroup_members_masks(UNIT_DEREF(u->slice));
|
||||
slice = UNIT_GET_SLICE(u);
|
||||
if (slice)
|
||||
unit_invalidate_cgroup_members_masks(slice);
|
||||
}
|
||||
|
||||
const char *unit_get_realized_cgroup_path(Unit *u, CGroupMask mask) {
|
||||
@ -1807,7 +1812,7 @@ const char *unit_get_realized_cgroup_path(Unit *u, CGroupMask mask) {
|
||||
FLAGS_SET(u->cgroup_realized_mask, mask))
|
||||
return u->cgroup_path;
|
||||
|
||||
u = UNIT_DEREF(u->slice);
|
||||
u = UNIT_GET_SLICE(u);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@ -1821,7 +1826,8 @@ static const char *migrate_callback(CGroupMask mask, void *userdata) {
|
||||
}
|
||||
|
||||
char *unit_default_cgroup_path(const Unit *u) {
|
||||
_cleanup_free_ char *escaped = NULL, *slice = NULL;
|
||||
_cleanup_free_ char *escaped = NULL, *slice_path = NULL;
|
||||
Unit *slice;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
@ -1829,8 +1835,9 @@ char *unit_default_cgroup_path(const Unit *u) {
|
||||
if (unit_has_name(u, SPECIAL_ROOT_SLICE))
|
||||
return strdup(u->manager->cgroup_root);
|
||||
|
||||
if (UNIT_ISSET(u->slice) && !unit_has_name(UNIT_DEREF(u->slice), SPECIAL_ROOT_SLICE)) {
|
||||
r = cg_slice_to_path(UNIT_DEREF(u->slice)->id, &slice);
|
||||
slice = UNIT_GET_SLICE(u);
|
||||
if (slice && !unit_has_name(slice, SPECIAL_ROOT_SLICE)) {
|
||||
r = cg_slice_to_path(slice->id, &slice_path);
|
||||
if (r < 0)
|
||||
return NULL;
|
||||
}
|
||||
@ -1839,7 +1846,7 @@ char *unit_default_cgroup_path(const Unit *u) {
|
||||
if (!escaped)
|
||||
return NULL;
|
||||
|
||||
return path_join(empty_to_root(u->manager->cgroup_root), slice, escaped);
|
||||
return path_join(empty_to_root(u->manager->cgroup_root), slice_path, escaped);
|
||||
}
|
||||
|
||||
int unit_set_cgroup_path(Unit *u, const char *path) {
|
||||
@ -2313,14 +2320,16 @@ static void unit_remove_from_cgroup_realize_queue(Unit *u) {
|
||||
* hierarchy downwards to the unit in question. */
|
||||
static int unit_realize_cgroup_now_enable(Unit *u, ManagerState state) {
|
||||
CGroupMask target_mask, enable_mask, new_target_mask, new_enable_mask;
|
||||
Unit *slice;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
/* First go deal with this unit's parent, or we won't be able to enable
|
||||
* any new controllers at this layer. */
|
||||
if (UNIT_ISSET(u->slice)) {
|
||||
r = unit_realize_cgroup_now_enable(UNIT_DEREF(u->slice), state);
|
||||
slice = UNIT_GET_SLICE(u);
|
||||
if (slice) {
|
||||
r = unit_realize_cgroup_now_enable(slice, state);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -2343,36 +2352,29 @@ static int unit_realize_cgroup_now_enable(Unit *u, ManagerState state) {
|
||||
* hierarchy upwards to the unit in question. */
|
||||
static int unit_realize_cgroup_now_disable(Unit *u, ManagerState state) {
|
||||
Unit *m;
|
||||
void *v;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (u->type != UNIT_SLICE)
|
||||
return 0;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, m, u->dependencies[UNIT_BEFORE]) {
|
||||
UNIT_FOREACH_DEPENDENCY(m, u, UNIT_ATOM_SLICE_OF) {
|
||||
CGroupMask target_mask, enable_mask, new_target_mask, new_enable_mask;
|
||||
int r;
|
||||
|
||||
if (UNIT_DEREF(m->slice) != u)
|
||||
continue;
|
||||
|
||||
/* The cgroup for this unit might not actually be fully
|
||||
* realised yet, in which case it isn't holding any controllers
|
||||
* open anyway. */
|
||||
/* The cgroup for this unit might not actually be fully realised yet, in which case it isn't
|
||||
* holding any controllers open anyway. */
|
||||
if (!m->cgroup_realized)
|
||||
continue;
|
||||
|
||||
/* We must disable those below us first in order to release the
|
||||
* controller. */
|
||||
/* We must disable those below us first in order to release the controller. */
|
||||
if (m->type == UNIT_SLICE)
|
||||
(void) unit_realize_cgroup_now_disable(m, state);
|
||||
|
||||
target_mask = unit_get_target_mask(m);
|
||||
enable_mask = unit_get_enable_mask(m);
|
||||
|
||||
/* We can only disable in this direction, don't try to enable
|
||||
* anything. */
|
||||
/* We can only disable in this direction, don't try to enable anything. */
|
||||
if (unit_has_mask_disables_realized(m, target_mask, enable_mask))
|
||||
continue;
|
||||
|
||||
@ -2433,6 +2435,7 @@ static int unit_realize_cgroup_now_disable(Unit *u, ManagerState state) {
|
||||
* Returns 0 on success and < 0 on failure. */
|
||||
static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
|
||||
CGroupMask target_mask, enable_mask;
|
||||
Unit *slice;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
@ -2451,8 +2454,9 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
|
||||
return r;
|
||||
|
||||
/* Enable controllers above us, if there are any */
|
||||
if (UNIT_ISSET(u->slice)) {
|
||||
r = unit_realize_cgroup_now_enable(UNIT_DEREF(u->slice), state);
|
||||
slice = UNIT_GET_SLICE(u);
|
||||
if (slice) {
|
||||
r = unit_realize_cgroup_now_enable(slice, state);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -2515,15 +2519,11 @@ void unit_add_family_to_cgroup_realize_queue(Unit *u) {
|
||||
|
||||
do {
|
||||
Unit *m;
|
||||
void *v;
|
||||
|
||||
/* Children of u likely changed when we're called */
|
||||
u->cgroup_members_mask_valid = false;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, m, u->dependencies[UNIT_BEFORE]) {
|
||||
/* Skip units that have a dependency on the slice but aren't actually in it. */
|
||||
if (UNIT_DEREF(m->slice) != u)
|
||||
continue;
|
||||
UNIT_FOREACH_DEPENDENCY(m, u, UNIT_ATOM_SLICE_OF) {
|
||||
|
||||
/* No point in doing cgroup application for units without active processes. */
|
||||
if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(m)))
|
||||
@ -2534,8 +2534,8 @@ void unit_add_family_to_cgroup_realize_queue(Unit *u) {
|
||||
if (!m->cgroup_realized)
|
||||
continue;
|
||||
|
||||
/* If the unit doesn't need any new controllers and has current ones realized, it
|
||||
* doesn't need any changes. */
|
||||
/* If the unit doesn't need any new controllers and has current ones
|
||||
* realized, it doesn't need any changes. */
|
||||
if (unit_has_mask_realized(m,
|
||||
unit_get_target_mask(m),
|
||||
unit_get_enable_mask(m)))
|
||||
@ -2546,10 +2546,14 @@ void unit_add_family_to_cgroup_realize_queue(Unit *u) {
|
||||
|
||||
/* Parent comes after children */
|
||||
unit_add_to_cgroup_realize_queue(u);
|
||||
} while ((u = UNIT_DEREF(u->slice)));
|
||||
|
||||
u = UNIT_GET_SLICE(u);
|
||||
} while (u);
|
||||
}
|
||||
|
||||
int unit_realize_cgroup(Unit *u) {
|
||||
Unit *slice;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (!UNIT_HAS_CGROUP_CONTEXT(u))
|
||||
@ -2564,8 +2568,9 @@ int unit_realize_cgroup(Unit *u) {
|
||||
* This call will defer work on the siblings and derealized ancestors to the next event loop
|
||||
* iteration and synchronously creates the parent cgroups (unit_realize_cgroup_now). */
|
||||
|
||||
if (UNIT_ISSET(u->slice))
|
||||
unit_add_family_to_cgroup_realize_queue(UNIT_DEREF(u->slice));
|
||||
slice = UNIT_GET_SLICE(u);
|
||||
if (slice)
|
||||
unit_add_family_to_cgroup_realize_queue(slice);
|
||||
|
||||
/* And realize this one now (and apply the values) */
|
||||
return unit_realize_cgroup_now(u, manager_state(u->manager));
|
||||
@ -3782,11 +3787,9 @@ void unit_invalidate_cgroup_bpf(Unit *u) {
|
||||
* list of our children includes our own. */
|
||||
if (u->type == UNIT_SLICE) {
|
||||
Unit *member;
|
||||
void *v;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE])
|
||||
if (UNIT_DEREF(member->slice) == u)
|
||||
unit_invalidate_cgroup_bpf(member);
|
||||
UNIT_FOREACH_DEPENDENCY(member, u, UNIT_ATOM_SLICE_OF)
|
||||
unit_invalidate_cgroup_bpf(member);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,21 +155,27 @@ static int property_get_dependencies(
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Hashmap **h = userdata;
|
||||
Unit *u;
|
||||
Unit *u = userdata, *other;
|
||||
UnitDependency d;
|
||||
Hashmap *deps;
|
||||
void *v;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
assert(h);
|
||||
assert(u);
|
||||
|
||||
d = unit_dependency_from_string(property);
|
||||
assert_se(d >= 0);
|
||||
|
||||
deps = unit_get_dependencies(u, d);
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "s");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, u, *h) {
|
||||
r = sd_bus_message_append(reply, "s", u->id);
|
||||
HASHMAP_FOREACH_KEY(v, other, deps) {
|
||||
r = sd_bus_message_append(reply, "s", other->id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -844,26 +850,32 @@ const sd_bus_vtable bus_unit_vtable[] = {
|
||||
SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Unit, id), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Names", "as", property_get_names, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Following", "s", property_get_following, 0, 0),
|
||||
SD_BUS_PROPERTY("Requires", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Requisite", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Wants", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTS]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("BindsTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BINDS_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PartOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PART_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RequiredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RequisiteOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("WantedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("BoundBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BOUND_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ConsistsOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Conflicts", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONFLICTS]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ConflictedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Before", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BEFORE]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("After", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_AFTER]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("OnFailure", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_ON_FAILURE]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Triggers", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_TRIGGERS]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("TriggeredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ReloadPropagatedFrom", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_JOINS_NAMESPACE_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Requires", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Requisite", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Wants", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("BindsTo", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PartOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RequiredBy", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RequisiteOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("WantedBy", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("BoundBy", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ConsistsOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Conflicts", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ConflictedBy", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Before", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("After", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("OnFailure", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("OnFailureOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("OnSuccess", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("OnSuccessOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Triggers", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("TriggeredBy", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ReloadPropagatedFrom", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PropagatesStopTo", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("StopPropagatedFrom", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("SliceOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RequiresMountsFor", "as", property_get_requires_mounts_for, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@ -893,6 +905,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
|
||||
SD_BUS_PROPERTY("RefuseManualStop", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_stop), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("AllowIsolate", "b", bus_property_get_bool, offsetof(Unit, allow_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("DefaultDependencies", "b", bus_property_get_bool, offsetof(Unit, default_dependencies), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("OnSuccesJobMode", "s", property_get_job_mode, offsetof(Unit, on_success_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("OnFailureJobMode", "s", property_get_job_mode, offsetof(Unit, on_failure_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@ -2116,6 +2129,9 @@ static int bus_unit_set_transient_property(
|
||||
if (streq(name, "DefaultDependencies"))
|
||||
return bus_set_transient_bool(u, name, &u->default_dependencies, message, flags, error);
|
||||
|
||||
if (streq(name, "OnSuccessJobMode"))
|
||||
return bus_set_transient_job_mode(u, name, &u->on_success_job_mode, message, flags, error);
|
||||
|
||||
if (streq(name, "OnFailureJobMode"))
|
||||
return bus_set_transient_job_mode(u, name, &u->on_failure_job_mode, message, flags, error);
|
||||
|
||||
@ -2231,7 +2247,7 @@ static int bus_unit_set_transient_property(
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit name '%s' is not a slice", s);
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
r = unit_set_slice(u, slice);
|
||||
r = unit_set_slice(u, slice, UNIT_DEPENDENCY_FILE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -2288,12 +2304,16 @@ static int bus_unit_set_transient_property(
|
||||
UNIT_WANTS,
|
||||
UNIT_BINDS_TO,
|
||||
UNIT_PART_OF,
|
||||
UNIT_UPHOLDS,
|
||||
UNIT_CONFLICTS,
|
||||
UNIT_BEFORE,
|
||||
UNIT_AFTER,
|
||||
UNIT_ON_SUCCESS,
|
||||
UNIT_ON_FAILURE,
|
||||
UNIT_PROPAGATES_RELOAD_TO,
|
||||
UNIT_RELOAD_PROPAGATED_FROM,
|
||||
UNIT_PROPAGATES_STOP_TO,
|
||||
UNIT_STOP_PROPAGATED_FROM,
|
||||
UNIT_JOINS_NAMESPACE_OF))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Dependency type %s may not be created transiently.", unit_dependency_to_string(d));
|
||||
|
||||
|
@ -468,7 +468,7 @@ static void device_upgrade_mount_deps(Unit *u) {
|
||||
|
||||
/* Let's upgrade Requires= to BindsTo= on us. (Used when SYSTEMD_MOUNT_DEVICE_BOUND is set) */
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUIRED_BY]) {
|
||||
HASHMAP_FOREACH_KEY(v, other, unit_get_dependencies(u, UNIT_REQUIRED_BY)) {
|
||||
if (other->type != UNIT_MOUNT)
|
||||
continue;
|
||||
|
||||
|
124
src/core/job.c
124
src/core/job.c
@ -455,7 +455,6 @@ int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u) {
|
||||
|
||||
static bool job_is_runnable(Job *j) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
assert(j);
|
||||
assert(j->installed);
|
||||
@ -477,16 +476,16 @@ static bool job_is_runnable(Job *j) {
|
||||
if (j->type == JOB_NOP)
|
||||
return true;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER])
|
||||
if (other->job && job_compare(j, other->job, UNIT_AFTER) > 0) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_AFTER)
|
||||
if (other->job && job_compare(j, other->job, UNIT_ATOM_AFTER) > 0) {
|
||||
log_unit_debug(j->unit,
|
||||
"starting held back, waiting for: %s",
|
||||
other->id);
|
||||
return false;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE])
|
||||
if (other->job && job_compare(j, other->job, UNIT_BEFORE) > 0) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_BEFORE)
|
||||
if (other->job && job_compare(j, other->job, UNIT_ATOM_BEFORE) > 0) {
|
||||
log_unit_debug(j->unit,
|
||||
"stopping held back, waiting for: %s",
|
||||
other->id);
|
||||
@ -951,13 +950,12 @@ static void job_emit_done_status_message(Unit *u, uint32_t job_id, JobType t, Jo
|
||||
job_print_done_status_message(u, t, result);
|
||||
}
|
||||
|
||||
static void job_fail_dependencies(Unit *u, UnitDependency d) {
|
||||
static void job_fail_dependencies(Unit *u, UnitDependencyAtom match_atom) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
assert(u);
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[d]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, match_atom) {
|
||||
Job *j = other->job;
|
||||
|
||||
if (!j)
|
||||
@ -970,10 +968,8 @@ static void job_fail_dependencies(Unit *u, UnitDependency d) {
|
||||
}
|
||||
|
||||
int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already) {
|
||||
Unit *u;
|
||||
Unit *other;
|
||||
Unit *u, *other;
|
||||
JobType t;
|
||||
void *v;
|
||||
|
||||
assert(j);
|
||||
assert(j->installed);
|
||||
@ -1012,43 +1008,36 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr
|
||||
|
||||
/* Fail depending jobs on failure */
|
||||
if (result != JOB_DONE && recursive) {
|
||||
if (IN_SET(t, JOB_START, JOB_VERIFY_ACTIVE)) {
|
||||
job_fail_dependencies(u, UNIT_REQUIRED_BY);
|
||||
job_fail_dependencies(u, UNIT_REQUISITE_OF);
|
||||
job_fail_dependencies(u, UNIT_BOUND_BY);
|
||||
} else if (t == JOB_STOP)
|
||||
job_fail_dependencies(u, UNIT_CONFLICTED_BY);
|
||||
if (IN_SET(t, JOB_START, JOB_VERIFY_ACTIVE))
|
||||
job_fail_dependencies(u, UNIT_ATOM_PROPAGATE_START_FAILURE);
|
||||
else if (t == JOB_STOP)
|
||||
job_fail_dependencies(u, UNIT_ATOM_PROPAGATE_STOP_FAILURE);
|
||||
}
|
||||
|
||||
/* A special check to make sure we take down anything RequisiteOf if we
|
||||
* aren't active. This is when the verify-active job merges with a
|
||||
* satisfying job type, and then loses it's invalidation effect, as the
|
||||
* result there is JOB_DONE for the start job we merged into, while we
|
||||
* should be failing the depending job if the said unit isn't in fact
|
||||
* active. Oneshots are an example of this, where going directly from
|
||||
* activating to inactive is success.
|
||||
/* A special check to make sure we take down anything RequisiteOf= if we aren't active. This is when
|
||||
* the verify-active job merges with a satisfying job type, and then loses it's invalidation effect,
|
||||
* as the result there is JOB_DONE for the start job we merged into, while we should be failing the
|
||||
* depending job if the said unit isn't in fact active. Oneshots are an example of this, where going
|
||||
* directly from activating to inactive is success.
|
||||
*
|
||||
* This happens when you use ConditionXYZ= in a unit too, since in that
|
||||
* case the job completes with the JOB_DONE result, but the unit never
|
||||
* really becomes active. Note that such a case still involves merging:
|
||||
* This happens when you use ConditionXYZ= in a unit too, since in that case the job completes with
|
||||
* the JOB_DONE result, but the unit never really becomes active. Note that such a case still
|
||||
* involves merging:
|
||||
*
|
||||
* A start job waits for something else, and a verify-active comes in
|
||||
* and merges in the installed job. Then, later, when it becomes
|
||||
* runnable, it finishes with JOB_DONE result as execution on conditions
|
||||
* not being met is skipped, breaking our dependency semantics.
|
||||
* A start job waits for something else, and a verify-active comes in and merges in the installed
|
||||
* job. Then, later, when it becomes runnable, it finishes with JOB_DONE result as execution on
|
||||
* conditions not being met is skipped, breaking our dependency semantics.
|
||||
*
|
||||
* Also, depending on if start job waits or not, the merging may or may
|
||||
* not happen (the verify-active job may trigger after it finishes), so
|
||||
* you get undeterministic results without this check.
|
||||
* Also, depending on if start job waits or not, the merging may or may not happen (the verify-active
|
||||
* job may trigger after it finishes), so you get undeterministic results without this check.
|
||||
*/
|
||||
if (result == JOB_DONE && recursive && !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) {
|
||||
if (IN_SET(t, JOB_START, JOB_RELOAD))
|
||||
job_fail_dependencies(u, UNIT_REQUISITE_OF);
|
||||
}
|
||||
/* Trigger OnFailure dependencies that are not generated by
|
||||
* the unit itself. We don't treat JOB_CANCELED as failure in
|
||||
* this context. And JOB_FAILURE is already handled by the
|
||||
* unit itself. */
|
||||
if (result == JOB_DONE && recursive &&
|
||||
IN_SET(t, JOB_START, JOB_RELOAD) &&
|
||||
!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
|
||||
job_fail_dependencies(u, UNIT_ATOM_PROPAGATE_INACTIVE_START_AS_FAILURE);
|
||||
|
||||
/* Trigger OnFailure= dependencies that are not generated by the unit itself. We don't treat
|
||||
* JOB_CANCELED as failure in this context. And JOB_FAILURE is already handled by the unit itself. */
|
||||
if (IN_SET(result, JOB_TIMEOUT, JOB_DEPENDENCY)) {
|
||||
log_unit_struct(u, LOG_NOTICE,
|
||||
"JOB_TYPE=%s", job_type_to_string(t),
|
||||
@ -1058,19 +1047,19 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr
|
||||
job_type_to_string(t),
|
||||
job_result_to_string(result)));
|
||||
|
||||
unit_start_on_failure(u);
|
||||
unit_start_on_failure(u, "OnFailure=", UNIT_ATOM_ON_FAILURE, u->on_failure_job_mode);
|
||||
}
|
||||
|
||||
unit_trigger_notify(u);
|
||||
|
||||
finish:
|
||||
/* Try to start the next jobs that can be started */
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_AFTER])
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_AFTER)
|
||||
if (other->job) {
|
||||
job_add_to_run_queue(other->job);
|
||||
job_add_to_gc_queue(other->job);
|
||||
}
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BEFORE])
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_BEFORE)
|
||||
if (other->job) {
|
||||
job_add_to_run_queue(other->job);
|
||||
job_add_to_gc_queue(other->job);
|
||||
@ -1420,7 +1409,6 @@ int job_get_timeout(Job *j, usec_t *timeout) {
|
||||
|
||||
bool job_may_gc(Job *j) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
assert(j);
|
||||
|
||||
@ -1449,12 +1437,12 @@ bool job_may_gc(Job *j) {
|
||||
return false;
|
||||
|
||||
/* The logic is inverse to job_is_runnable, we cannot GC as long as we block any job. */
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE])
|
||||
if (other->job && job_compare(j, other->job, UNIT_BEFORE) < 0)
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_BEFORE)
|
||||
if (other->job && job_compare(j, other->job, UNIT_ATOM_BEFORE) < 0)
|
||||
return false;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER])
|
||||
if (other->job && job_compare(j, other->job, UNIT_AFTER) < 0)
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_AFTER)
|
||||
if (other->job && job_compare(j, other->job, UNIT_ATOM_AFTER) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -1500,7 +1488,6 @@ int job_get_before(Job *j, Job*** ret) {
|
||||
_cleanup_free_ Job** list = NULL;
|
||||
Unit *other = NULL;
|
||||
size_t n = 0;
|
||||
void *v;
|
||||
|
||||
/* Returns a list of all pending jobs that need to finish before this job may be started. */
|
||||
|
||||
@ -1512,10 +1499,10 @@ int job_get_before(Job *j, Job*** ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_AFTER) {
|
||||
if (!other->job)
|
||||
continue;
|
||||
if (job_compare(j, other->job, UNIT_AFTER) <= 0)
|
||||
if (job_compare(j, other->job, UNIT_ATOM_AFTER) <= 0)
|
||||
continue;
|
||||
|
||||
if (!GREEDY_REALLOC(list, n+1))
|
||||
@ -1523,10 +1510,10 @@ int job_get_before(Job *j, Job*** ret) {
|
||||
list[n++] = other->job;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_BEFORE) {
|
||||
if (!other->job)
|
||||
continue;
|
||||
if (job_compare(j, other->job, UNIT_BEFORE) <= 0)
|
||||
if (job_compare(j, other->job, UNIT_ATOM_BEFORE) <= 0)
|
||||
continue;
|
||||
|
||||
if (!GREEDY_REALLOC(list, n+1))
|
||||
@ -1545,21 +1532,20 @@ int job_get_after(Job *j, Job*** ret) {
|
||||
_cleanup_free_ Job** list = NULL;
|
||||
Unit *other = NULL;
|
||||
size_t n = 0;
|
||||
void *v;
|
||||
|
||||
assert(j);
|
||||
assert(ret);
|
||||
|
||||
/* Returns a list of all pending jobs that are waiting for this job to finish. */
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_BEFORE) {
|
||||
if (!other->job)
|
||||
continue;
|
||||
|
||||
if (other->job->ignore_order)
|
||||
continue;
|
||||
|
||||
if (job_compare(j, other->job, UNIT_BEFORE) >= 0)
|
||||
if (job_compare(j, other->job, UNIT_ATOM_BEFORE) >= 0)
|
||||
continue;
|
||||
|
||||
if (!GREEDY_REALLOC(list, n+1))
|
||||
@ -1567,14 +1553,14 @@ int job_get_after(Job *j, Job*** ret) {
|
||||
list[n++] = other->job;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_AFTER) {
|
||||
if (!other->job)
|
||||
continue;
|
||||
|
||||
if (other->job->ignore_order)
|
||||
continue;
|
||||
|
||||
if (job_compare(j, other->job, UNIT_AFTER) >= 0)
|
||||
if (job_compare(j, other->job, UNIT_ATOM_AFTER) >= 0)
|
||||
continue;
|
||||
|
||||
if (!GREEDY_REALLOC(list, n+1))
|
||||
@ -1666,13 +1652,14 @@ const char* job_type_to_access_method(JobType t) {
|
||||
* stop a + start b → 1st step stop a, 2nd step start b
|
||||
* stop a + stop b → 1st step stop b, 2nd step stop a
|
||||
*
|
||||
* This has the side effect that restarts are properly
|
||||
* synchronized too.
|
||||
* This has the side effect that restarts are properly synchronized too.
|
||||
*/
|
||||
int job_compare(Job *a, Job *b, UnitDependency assume_dep) {
|
||||
int job_compare(Job *a, Job *b, UnitDependencyAtom assume_dep) {
|
||||
assert(a);
|
||||
assert(b);
|
||||
assert(a->type < _JOB_TYPE_MAX_IN_TRANSACTION);
|
||||
assert(b->type < _JOB_TYPE_MAX_IN_TRANSACTION);
|
||||
assert(IN_SET(assume_dep, UNIT_AFTER, UNIT_BEFORE));
|
||||
assert(IN_SET(assume_dep, UNIT_ATOM_AFTER, UNIT_ATOM_BEFORE));
|
||||
|
||||
/* Trivial cases first */
|
||||
if (a->type == JOB_NOP || b->type == JOB_NOP)
|
||||
@ -1681,12 +1668,11 @@ int job_compare(Job *a, Job *b, UnitDependency assume_dep) {
|
||||
if (a->ignore_order || b->ignore_order)
|
||||
return 0;
|
||||
|
||||
if (assume_dep == UNIT_AFTER)
|
||||
return -job_compare(b, a, UNIT_BEFORE);
|
||||
if (assume_dep == UNIT_ATOM_AFTER)
|
||||
return -job_compare(b, a, UNIT_ATOM_BEFORE);
|
||||
|
||||
/* Let's make it simple, JOB_STOP goes always first (in case both ua and ub stop,
|
||||
* then ub's stop goes first anyway).
|
||||
* JOB_RESTART is JOB_STOP in disguise (before it is patched to JOB_START). */
|
||||
/* Let's make it simple, JOB_STOP goes always first (in case both ua and ub stop, then ub's stop goes
|
||||
* first anyway). JOB_RESTART is JOB_STOP in disguise (before it is patched to JOB_START). */
|
||||
if (IN_SET(b->type, JOB_STOP, JOB_RESTART))
|
||||
return 1;
|
||||
else
|
||||
|
@ -6,7 +6,9 @@
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "list.h"
|
||||
#include "unit-dependency-atom.h"
|
||||
#include "unit-name.h"
|
||||
#include "unit.h"
|
||||
|
||||
typedef struct Job Job;
|
||||
typedef struct JobDependency JobDependency;
|
||||
@ -240,4 +242,4 @@ JobResult job_result_from_string(const char *s) _pure_;
|
||||
|
||||
const char* job_type_to_access_method(JobType t);
|
||||
|
||||
int job_compare(Job *a, Job *b, UnitDependency assume_dep);
|
||||
int job_compare(Job *a, Job *b, UnitDependencyAtom assume_dep);
|
||||
|
@ -261,14 +261,18 @@ Unit.Requisite, config_parse_unit_deps,
|
||||
Unit.Wants, config_parse_unit_deps, UNIT_WANTS, 0
|
||||
Unit.BindsTo, config_parse_unit_deps, UNIT_BINDS_TO, 0
|
||||
Unit.BindTo, config_parse_unit_deps, UNIT_BINDS_TO, 0
|
||||
Unit.Upholds, config_parse_unit_deps, UNIT_UPHOLDS, 0
|
||||
Unit.Conflicts, config_parse_unit_deps, UNIT_CONFLICTS, 0
|
||||
Unit.Before, config_parse_unit_deps, UNIT_BEFORE, 0
|
||||
Unit.After, config_parse_unit_deps, UNIT_AFTER, 0
|
||||
Unit.OnSuccess, config_parse_unit_deps, UNIT_ON_SUCCESS, 0
|
||||
Unit.OnFailure, config_parse_unit_deps, UNIT_ON_FAILURE, 0
|
||||
Unit.PropagatesReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0
|
||||
Unit.PropagateReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0
|
||||
Unit.ReloadPropagatedFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0
|
||||
Unit.PropagateReloadFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0
|
||||
Unit.PropagatesStopTo, config_parse_unit_deps, UNIT_PROPAGATES_STOP_TO, 0
|
||||
Unit.StopPropagatedFrom, config_parse_unit_deps, UNIT_STOP_PROPAGATED_FROM, 0
|
||||
Unit.PartOf, config_parse_unit_deps, UNIT_PART_OF, 0
|
||||
Unit.JoinsNamespaceOf, config_parse_unit_deps, UNIT_JOINS_NAMESPACE_OF, 0
|
||||
Unit.RequiresOverridable, config_parse_obsolete_unit_deps, UNIT_REQUIRES, 0
|
||||
@ -279,6 +283,7 @@ Unit.RefuseManualStart, config_parse_bool,
|
||||
Unit.RefuseManualStop, config_parse_bool, 0, offsetof(Unit, refuse_manual_stop)
|
||||
Unit.AllowIsolate, config_parse_bool, 0, offsetof(Unit, allow_isolate)
|
||||
Unit.DefaultDependencies, config_parse_bool, 0, offsetof(Unit, default_dependencies)
|
||||
Unit.OnSuccessJobMode, config_parse_job_mode, 0, offsetof(Unit, on_success_job_mode)
|
||||
Unit.OnFailureJobMode, config_parse_job_mode, 0, offsetof(Unit, on_failure_job_mode)
|
||||
{# The following is a legacy alias name for compatibility #}
|
||||
Unit.OnFailureIsolate, config_parse_job_mode_isolate, 0, offsetof(Unit, on_failure_job_mode)
|
||||
|
@ -3574,7 +3574,7 @@ int config_parse_unit_slice(
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = unit_set_slice(u, slice);
|
||||
r = unit_set_slice(u, slice, UNIT_DEPENDENCY_FILE);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to assign slice %s to unit %s, ignoring: %m", slice->id, u->id);
|
||||
return 0;
|
||||
|
@ -1141,12 +1141,11 @@ enum {
|
||||
|
||||
static void unit_gc_mark_good(Unit *u, unsigned gc_marker) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
u->gc_marker = gc_marker + GC_OFFSET_GOOD;
|
||||
|
||||
/* Recursively mark referenced units as GOOD as well */
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REFERENCES])
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_REFERENCES)
|
||||
if (other->gc_marker == gc_marker + GC_OFFSET_UNSURE)
|
||||
unit_gc_mark_good(other, gc_marker);
|
||||
}
|
||||
@ -1154,7 +1153,6 @@ static void unit_gc_mark_good(Unit *u, unsigned gc_marker) {
|
||||
static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
|
||||
Unit *other;
|
||||
bool is_bad;
|
||||
void *v;
|
||||
|
||||
assert(u);
|
||||
|
||||
@ -1172,7 +1170,7 @@ static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
|
||||
|
||||
is_bad = true;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REFERENCED_BY]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_REFERENCED_BY) {
|
||||
unit_gc_sweep(other, gc_marker);
|
||||
|
||||
if (other->gc_marker == gc_marker + GC_OFFSET_GOOD)
|
||||
@ -1297,7 +1295,7 @@ static unsigned manager_dispatch_stop_when_unneeded_queue(Manager *m) {
|
||||
/* 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 not needed anymore, but not stopping since we tried this too often recently.");
|
||||
continue;
|
||||
}
|
||||
@ -1311,6 +1309,82 @@ static unsigned manager_dispatch_stop_when_unneeded_queue(Manager *m) {
|
||||
return n;
|
||||
}
|
||||
|
||||
static unsigned manager_dispatch_start_when_upheld_queue(Manager *m) {
|
||||
unsigned n = 0;
|
||||
Unit *u;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
while ((u = m->start_when_upheld_queue)) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
Unit *culprit = NULL;
|
||||
|
||||
assert(u->in_start_when_upheld_queue);
|
||||
LIST_REMOVE(start_when_upheld_queue, m->start_when_upheld_queue, u);
|
||||
u->in_start_when_upheld_queue = false;
|
||||
|
||||
n++;
|
||||
|
||||
if (!unit_is_upheld_by_active(u, &culprit))
|
||||
continue;
|
||||
|
||||
log_unit_debug(u, "Unit is started because upheld by active unit %s.", culprit->id);
|
||||
|
||||
/* 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_start_stop_ratelimit)) {
|
||||
log_unit_warning(u, "Unit needs to be started because active unit %s upholds it, but not starting since we tried this too often recently.", culprit->id);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = manager_add_job(u->manager, JOB_START, u, JOB_FAIL, NULL, &error, NULL);
|
||||
if (r < 0)
|
||||
log_unit_warning_errno(u, r, "Failed to enqueue start job, ignoring: %s", bus_error_message(&error, r));
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static unsigned manager_dispatch_stop_when_bound_queue(Manager *m) {
|
||||
unsigned n = 0;
|
||||
Unit *u;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
while ((u = m->stop_when_bound_queue)) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
Unit *culprit = NULL;
|
||||
|
||||
assert(u->in_stop_when_bound_queue);
|
||||
LIST_REMOVE(stop_when_bound_queue, m->stop_when_bound_queue, u);
|
||||
u->in_stop_when_bound_queue = false;
|
||||
|
||||
n++;
|
||||
|
||||
if (!unit_is_bound_by_inactive(u, &culprit))
|
||||
continue;
|
||||
|
||||
log_unit_debug(u, "Unit is stopped because bound to inactive unit %s.", culprit->id);
|
||||
|
||||
/* 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_start_stop_ratelimit)) {
|
||||
log_unit_warning(u, "Unit needs to be stopped because it is bound to inactive unit %s it, but not stopping since we tried this too often recently.", culprit->id);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = manager_add_job(u->manager, JOB_STOP, u, JOB_REPLACE, NULL, &error, NULL);
|
||||
if (r < 0)
|
||||
log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r));
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void manager_clear_jobs_and_units(Manager *m) {
|
||||
Unit *u;
|
||||
|
||||
@ -1329,6 +1403,8 @@ static void manager_clear_jobs_and_units(Manager *m) {
|
||||
assert(!m->gc_unit_queue);
|
||||
assert(!m->gc_job_queue);
|
||||
assert(!m->stop_when_unneeded_queue);
|
||||
assert(!m->start_when_upheld_queue);
|
||||
assert(!m->stop_when_bound_queue);
|
||||
|
||||
assert(hashmap_isempty(m->jobs));
|
||||
assert(hashmap_isempty(m->units));
|
||||
@ -1870,30 +1946,28 @@ static int manager_dispatch_target_deps_queue(Manager *m) {
|
||||
Unit *u;
|
||||
int r = 0;
|
||||
|
||||
static const UnitDependency deps[] = {
|
||||
UNIT_REQUIRED_BY,
|
||||
UNIT_REQUISITE_OF,
|
||||
UNIT_WANTED_BY,
|
||||
UNIT_BOUND_BY
|
||||
};
|
||||
|
||||
assert(m);
|
||||
|
||||
while ((u = m->target_deps_queue)) {
|
||||
_cleanup_free_ Unit **targets = NULL;
|
||||
int n_targets;
|
||||
|
||||
assert(u->in_target_deps_queue);
|
||||
|
||||
LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u);
|
||||
u->in_target_deps_queue = false;
|
||||
|
||||
for (size_t k = 0; k < ELEMENTSOF(deps); k++) {
|
||||
Unit *target;
|
||||
void *v;
|
||||
/* Take an "atomic" snapshot of dependencies here, as the call below will likely modify the
|
||||
* dependencies, and we can't have it that hash tables we iterate through are modified while
|
||||
* we are iterating through them. */
|
||||
n_targets = unit_get_dependency_array(u, UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES, &targets);
|
||||
if (n_targets < 0)
|
||||
return n_targets;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, target, u->dependencies[deps[k]]) {
|
||||
r = unit_add_default_target_dependency(u, target);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
for (int i = 0; i < n_targets; i++) {
|
||||
r = unit_add_default_target_dependency(u, targets[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2958,6 +3032,12 @@ int manager_loop(Manager *m) {
|
||||
if (manager_dispatch_cgroup_realize_queue(m) > 0)
|
||||
continue;
|
||||
|
||||
if (manager_dispatch_start_when_upheld_queue(m) > 0)
|
||||
continue;
|
||||
|
||||
if (manager_dispatch_stop_when_bound_queue(m) > 0)
|
||||
continue;
|
||||
|
||||
if (manager_dispatch_stop_when_unneeded_queue(m) > 0)
|
||||
continue;
|
||||
|
||||
|
@ -187,6 +187,12 @@ struct Manager {
|
||||
/* Units that might be subject to StopWhenUnneeded= clean-up */
|
||||
LIST_HEAD(Unit, stop_when_unneeded_queue);
|
||||
|
||||
/* Units which are upheld by another other which we might need to act on */
|
||||
LIST_HEAD(Unit, start_when_upheld_queue);
|
||||
|
||||
/* Units that have BindsTo= another unit, and might need to be shutdown because the bound unit is not active. */
|
||||
LIST_HEAD(Unit, stop_when_bound_queue);
|
||||
|
||||
sd_event *event;
|
||||
|
||||
/* This maps PIDs we care about to units that are interested in. We allow multiple units to he interested in
|
||||
|
@ -117,6 +117,8 @@ libcore_sources = '''
|
||||
timer.h
|
||||
transaction.c
|
||||
transaction.h
|
||||
unit-dependency-atom.c
|
||||
unit-dependency-atom.h
|
||||
unit-printf.c
|
||||
unit-printf.h
|
||||
unit-serialize.c
|
||||
|
@ -66,6 +66,24 @@ static bool MOUNT_STATE_WITH_PROCESS(MountState state) {
|
||||
MOUNT_CLEANING);
|
||||
}
|
||||
|
||||
static MountParameters* get_mount_parameters_fragment(Mount *m) {
|
||||
assert(m);
|
||||
|
||||
if (m->from_fragment)
|
||||
return &m->parameters_fragment;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static MountParameters* get_mount_parameters(Mount *m) {
|
||||
assert(m);
|
||||
|
||||
if (m->from_proc_self_mountinfo)
|
||||
return &m->parameters_proc_self_mountinfo;
|
||||
|
||||
return get_mount_parameters_fragment(m);
|
||||
}
|
||||
|
||||
static bool mount_is_automount(const MountParameters *p) {
|
||||
assert(p);
|
||||
|
||||
@ -116,16 +134,33 @@ static bool mount_is_bind(const MountParameters *p) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool mount_is_bound_to_device(const Mount *m) {
|
||||
static bool mount_is_bound_to_device(Mount *m) {
|
||||
const MountParameters *p;
|
||||
|
||||
if (m->from_fragment)
|
||||
return true;
|
||||
assert(m);
|
||||
|
||||
/* Determines whether to place a Requires= or BindsTo= dependency on the backing device unit. We do
|
||||
* this by checking for the x-systemd.device-bound mount option. Iff it is set we use BindsTo=,
|
||||
* otherwise Requires=. But note that we might combine the latter with StopPropagatedFrom=, see
|
||||
* below. */
|
||||
|
||||
p = get_mount_parameters(m);
|
||||
if (!p)
|
||||
return false;
|
||||
|
||||
p = &m->parameters_proc_self_mountinfo;
|
||||
return fstab_test_option(p->options, "x-systemd.device-bound\0");
|
||||
}
|
||||
|
||||
static bool mount_propagate_stop(Mount *m) {
|
||||
assert(m);
|
||||
|
||||
if (mount_is_bound_to_device(m)) /* If we are using BindsTo= the stop propagation is implicit, no need to bother */
|
||||
return false;
|
||||
|
||||
return m->from_fragment; /* let's propagate stop whenever this is an explicitly configured unit,
|
||||
* otherwise let's not bother. */
|
||||
}
|
||||
|
||||
static bool mount_needs_quota(const MountParameters *p) {
|
||||
assert(p);
|
||||
|
||||
@ -234,24 +269,6 @@ static void mount_done(Unit *u) {
|
||||
m->timer_event_source = sd_event_source_unref(m->timer_event_source);
|
||||
}
|
||||
|
||||
static MountParameters* get_mount_parameters_fragment(Mount *m) {
|
||||
assert(m);
|
||||
|
||||
if (m->from_fragment)
|
||||
return &m->parameters_fragment;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static MountParameters* get_mount_parameters(Mount *m) {
|
||||
assert(m);
|
||||
|
||||
if (m->from_proc_self_mountinfo)
|
||||
return &m->parameters_proc_self_mountinfo;
|
||||
|
||||
return get_mount_parameters_fragment(m);
|
||||
}
|
||||
|
||||
static int update_parameters_proc_self_mountinfo(
|
||||
Mount *m,
|
||||
const char *what,
|
||||
@ -367,8 +384,9 @@ static int mount_add_device_dependencies(Mount *m) {
|
||||
return 0;
|
||||
|
||||
/* Mount units from /proc/self/mountinfo are not bound to devices by default since they're subject to
|
||||
* races when devices are unplugged. But the user can still force this dep with an appropriate option
|
||||
* (or udev property) so the mount units are automatically stopped when the device disappears
|
||||
* races when mounts are established by other tools with different backing devices than what we
|
||||
* maintain. The user can still force this to be a BindsTo= dependency with an appropriate option (or
|
||||
* udev property) so the mount units are automatically stopped when the device disappears
|
||||
* suddenly. */
|
||||
dep = mount_is_bound_to_device(m) ? UNIT_BINDS_TO : UNIT_REQUIRES;
|
||||
|
||||
@ -378,6 +396,11 @@ static int mount_add_device_dependencies(Mount *m) {
|
||||
r = unit_add_node_dependency(UNIT(m), p->what, dep, mask);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (mount_propagate_stop(m)) {
|
||||
r = unit_add_node_dependency(UNIT(m), p->what, UNIT_STOP_PROPAGATED_FROM, mask);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return unit_add_blockdev_dependency(UNIT(m), p->what, mask);
|
||||
}
|
||||
|
@ -1245,12 +1245,11 @@ static int service_collect_fds(
|
||||
|
||||
rn_socket_fds = 1;
|
||||
} else {
|
||||
void *v;
|
||||
Unit *u;
|
||||
|
||||
/* Pass all our configured sockets for singleton services */
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY]) {
|
||||
UNIT_FOREACH_DEPENDENCY(u, UNIT(s), UNIT_ATOM_TRIGGERED_BY) {
|
||||
_cleanup_free_ int *cfds = NULL;
|
||||
Socket *sock;
|
||||
int cn_fds;
|
||||
|
@ -47,25 +47,20 @@ static void slice_set_state(Slice *t, SliceState state) {
|
||||
}
|
||||
|
||||
static int slice_add_parent_slice(Slice *s) {
|
||||
Unit *u = UNIT(s), *parent;
|
||||
Unit *u = UNIT(s);
|
||||
_cleanup_free_ char *a = NULL;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (UNIT_ISSET(u->slice))
|
||||
if (UNIT_GET_SLICE(u))
|
||||
return 0;
|
||||
|
||||
r = slice_build_parent_slice(u->id, &a);
|
||||
if (r <= 0) /* 0 means root slice */
|
||||
return r;
|
||||
|
||||
r = manager_load_unit(u->manager, a, NULL, NULL, &parent);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
unit_ref_set(&u->slice, u, parent);
|
||||
return 0;
|
||||
return unit_add_dependency_by_name(u, UNIT_IN_SLICE, a, true, UNIT_DEPENDENCY_IMPLICIT);
|
||||
}
|
||||
|
||||
static int slice_add_default_dependencies(Slice *s) {
|
||||
@ -101,7 +96,7 @@ static int slice_verify(Slice *s) {
|
||||
if (r < 0)
|
||||
return log_unit_error_errno(UNIT(s), r, "Failed to determine parent slice: %m");
|
||||
|
||||
if (parent ? !unit_has_name(UNIT_DEREF(UNIT(s)->slice), parent) : UNIT_ISSET(UNIT(s)->slice))
|
||||
if (parent ? !unit_has_name(UNIT_GET_SLICE(UNIT(s)), parent) : !!UNIT_GET_SLICE(UNIT(s)))
|
||||
return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Located outside of parent slice. Refusing.");
|
||||
|
||||
return 0;
|
||||
@ -346,15 +341,11 @@ static void slice_enumerate_perpetual(Manager *m) {
|
||||
|
||||
static bool slice_freezer_action_supported_by_children(Unit *s) {
|
||||
Unit *member;
|
||||
void *v;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE]) {
|
||||
int r;
|
||||
|
||||
if (UNIT_DEREF(member->slice) != s)
|
||||
continue;
|
||||
UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_SLICE_OF) {
|
||||
|
||||
if (member->type == UNIT_SLICE) {
|
||||
r = slice_freezer_action_supported_by_children(member);
|
||||
@ -371,7 +362,6 @@ static bool slice_freezer_action_supported_by_children(Unit *s) {
|
||||
|
||||
static int slice_freezer_action(Unit *s, FreezerAction action) {
|
||||
Unit *member;
|
||||
void *v;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
@ -382,15 +372,11 @@ static int slice_freezer_action(Unit *s, FreezerAction action) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE]) {
|
||||
if (UNIT_DEREF(member->slice) != s)
|
||||
continue;
|
||||
|
||||
UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_SLICE_OF) {
|
||||
if (action == FREEZER_FREEZE)
|
||||
r = UNIT_VTABLE(member)->freeze(member);
|
||||
else
|
||||
r = UNIT_VTABLE(member)->thaw(member);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -2340,11 +2340,9 @@ static void socket_enter_running(Socket *s, int cfd_in) {
|
||||
if (cfd < 0) {
|
||||
bool pending = false;
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
/* If there's already a start pending don't bother to
|
||||
* do anything */
|
||||
HASHMAP_FOREACH_KEY(v, other, UNIT(s)->dependencies[UNIT_TRIGGERS])
|
||||
/* If there's already a start pending don't bother to do anything */
|
||||
UNIT_FOREACH_DEPENDENCY(other, UNIT(s), UNIT_ATOM_TRIGGERS)
|
||||
if (unit_active_or_pending(other)) {
|
||||
pending = true;
|
||||
break;
|
||||
|
@ -35,34 +35,31 @@ static void target_set_state(Target *t, TargetState state) {
|
||||
}
|
||||
|
||||
static int target_add_default_dependencies(Target *t) {
|
||||
|
||||
static const UnitDependency deps[] = {
|
||||
UNIT_REQUIRES,
|
||||
UNIT_REQUISITE,
|
||||
UNIT_WANTS,
|
||||
UNIT_BINDS_TO,
|
||||
UNIT_PART_OF
|
||||
};
|
||||
|
||||
int r;
|
||||
_cleanup_free_ Unit **others = NULL;
|
||||
int r, n_others;
|
||||
|
||||
assert(t);
|
||||
|
||||
if (!UNIT(t)->default_dependencies)
|
||||
return 0;
|
||||
|
||||
/* Imply ordering for requirement dependencies on target units. Note that when the user created a contradicting
|
||||
* ordering manually we won't add anything in here to make sure we don't create a loop. */
|
||||
/* Imply ordering for requirement dependencies on target units. Note that when the user created a
|
||||
* contradicting ordering manually we won't add anything in here to make sure we don't create a
|
||||
* loop.
|
||||
*
|
||||
* Note that quite likely iterating through these dependencies will add new dependencies, which
|
||||
* conflicts with the hashmap-based iteration logic. Hence, instead of iterating through the
|
||||
* dependencies and acting on them as we go, first take an "atomic snapshot" of sorts and iterate
|
||||
* through that. */
|
||||
|
||||
for (size_t k = 0; k < ELEMENTSOF(deps); k++) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
n_others = unit_get_dependency_array(UNIT(t), UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE, &others);
|
||||
if (n_others < 0)
|
||||
return n_others;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, UNIT(t)->dependencies[deps[k]]) {
|
||||
r = unit_add_default_target_dependency(other, UNIT(t));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
for (int i = 0; i < n_others; i++) {
|
||||
r = unit_add_default_target_dependency(others[i], UNIT(t));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (unit_has_name(UNIT(t), SPECIAL_SHUTDOWN_TARGET))
|
||||
|
@ -347,21 +347,20 @@ static char* merge_unit_ids(const char* unit_log_field, char **pairs) {
|
||||
}
|
||||
|
||||
static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, sd_bus_error *e) {
|
||||
Unit *u;
|
||||
void *v;
|
||||
int r;
|
||||
static const UnitDependency directions[] = {
|
||||
UNIT_BEFORE,
|
||||
UNIT_AFTER,
|
||||
|
||||
static const UnitDependencyAtom directions[] = {
|
||||
UNIT_ATOM_BEFORE,
|
||||
UNIT_ATOM_AFTER,
|
||||
};
|
||||
size_t d;
|
||||
|
||||
int r;
|
||||
|
||||
assert(tr);
|
||||
assert(j);
|
||||
assert(!j->transaction_prev);
|
||||
|
||||
/* Does a recursive sweep through the ordering graph, looking
|
||||
* for a cycle. If we find a cycle we try to break it. */
|
||||
/* Does a recursive sweep through the ordering graph, looking for a cycle. If we find a cycle we try
|
||||
* to break it. */
|
||||
|
||||
/* Have we seen this before? */
|
||||
if (j->generation == generation) {
|
||||
@ -369,18 +368,14 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
|
||||
_cleanup_free_ char **array = NULL, *unit_ids = NULL;
|
||||
char **unit_id, **job_type;
|
||||
|
||||
/* If the marker is NULL we have been here already and
|
||||
* decided the job was loop-free from here. Hence
|
||||
* shortcut things and return right-away. */
|
||||
/* If the marker is NULL we have been here already and decided the job was loop-free from
|
||||
* here. Hence shortcut things and return right-away. */
|
||||
if (!j->marker)
|
||||
return 0;
|
||||
|
||||
/* So, the marker is not NULL and we already have been here. We have
|
||||
* a cycle. Let's try to break it. We go backwards in our path and
|
||||
* try to find a suitable job to remove. We use the marker to find
|
||||
* our way back, since smart how we are we stored our way back in
|
||||
* there. */
|
||||
|
||||
/* So, the marker is not NULL and we already have been here. We have a cycle. Let's try to
|
||||
* break it. We go backwards in our path and try to find a suitable job to remove. We use the
|
||||
* marker to find our way back, since smart how we are we stored our way back in there. */
|
||||
for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
|
||||
|
||||
/* For logging below */
|
||||
@ -449,16 +444,17 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
|
||||
* the graph over 'before' edges in the actual job execution order. We traverse over both unit
|
||||
* ordering dependencies and we test with job_compare() whether it is the 'before' edge in the job
|
||||
* execution ordering. */
|
||||
for (d = 0; d < ELEMENTSOF(directions); d++) {
|
||||
HASHMAP_FOREACH_KEY(v, u, j->unit->dependencies[directions[d]]) {
|
||||
for (size_t d = 0; d < ELEMENTSOF(directions); d++) {
|
||||
Unit *u;
|
||||
|
||||
UNIT_FOREACH_DEPENDENCY(u, j->unit, directions[d]) {
|
||||
Job *o;
|
||||
|
||||
/* Is there a job for this unit? */
|
||||
o = hashmap_get(tr->jobs, u);
|
||||
if (!o) {
|
||||
/* Ok, there is no job for this in the
|
||||
* transaction, but maybe there is already one
|
||||
* running? */
|
||||
/* Ok, there is no job for this in the transaction, but maybe there is
|
||||
* already one running? */
|
||||
o = u->job;
|
||||
if (!o)
|
||||
continue;
|
||||
@ -879,13 +875,12 @@ static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependen
|
||||
void transaction_add_propagate_reload_jobs(Transaction *tr, Unit *unit, Job *by, bool ignore_order, sd_bus_error *e) {
|
||||
JobType nt;
|
||||
Unit *dep;
|
||||
void *v;
|
||||
int r;
|
||||
|
||||
assert(tr);
|
||||
assert(unit);
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, dep, unit->dependencies[UNIT_PROPAGATES_RELOAD_TO]) {
|
||||
UNIT_FOREACH_DEPENDENCY(dep, unit, UNIT_ATOM_PROPAGATES_RELOAD_TO) {
|
||||
nt = job_type_collapse(JOB_TRY_RELOAD, dep);
|
||||
if (nt == JOB_NOP)
|
||||
continue;
|
||||
@ -914,7 +909,6 @@ int transaction_add_job_and_dependencies(
|
||||
bool is_new;
|
||||
Unit *dep;
|
||||
Job *ret;
|
||||
void *v;
|
||||
int r;
|
||||
|
||||
assert(tr);
|
||||
@ -1006,7 +1000,7 @@ int transaction_add_job_and_dependencies(
|
||||
|
||||
/* Finally, recursively add in all dependencies. */
|
||||
if (IN_SET(type, JOB_START, JOB_RESTART)) {
|
||||
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_REQUIRES]) {
|
||||
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_START) {
|
||||
r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
if (r != -EBADR) /* job type not applicable */
|
||||
@ -1016,17 +1010,7 @@ int transaction_add_job_and_dependencies(
|
||||
}
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_BINDS_TO]) {
|
||||
r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
if (r != -EBADR) /* job type not applicable */
|
||||
goto fail;
|
||||
|
||||
sd_bus_error_free(e);
|
||||
}
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_WANTS]) {
|
||||
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_START_IGNORED) {
|
||||
r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
/* unit masked, job type not applicable and unit not found are not considered as errors. */
|
||||
@ -1038,7 +1022,7 @@ int transaction_add_job_and_dependencies(
|
||||
}
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_REQUISITE]) {
|
||||
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_VERIFY) {
|
||||
r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, false, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
if (r != -EBADR) /* job type not applicable */
|
||||
@ -1048,7 +1032,7 @@ int transaction_add_job_and_dependencies(
|
||||
}
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_CONFLICTS]) {
|
||||
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_STOP) {
|
||||
r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, true, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
if (r != -EBADR) /* job type not applicable */
|
||||
@ -1058,7 +1042,7 @@ int transaction_add_job_and_dependencies(
|
||||
}
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_CONFLICTED_BY]) {
|
||||
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_STOP_IGNORED) {
|
||||
r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, false, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
log_unit_warning(dep,
|
||||
@ -1067,41 +1051,37 @@ int transaction_add_job_and_dependencies(
|
||||
sd_bus_error_free(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (IN_SET(type, JOB_STOP, JOB_RESTART)) {
|
||||
static const UnitDependency propagate_deps[] = {
|
||||
UNIT_REQUIRED_BY,
|
||||
UNIT_REQUISITE_OF,
|
||||
UNIT_BOUND_BY,
|
||||
UNIT_CONSISTS_OF,
|
||||
};
|
||||
|
||||
UnitDependencyAtom atom;
|
||||
JobType ptype;
|
||||
unsigned j;
|
||||
|
||||
/* We propagate STOP as STOP, but RESTART only
|
||||
* as TRY_RESTART, in order not to start
|
||||
/* We propagate STOP as STOP, but RESTART only as TRY_RESTART, in order not to start
|
||||
* dependencies that are not around. */
|
||||
ptype = type == JOB_RESTART ? JOB_TRY_RESTART : type;
|
||||
if (type == JOB_RESTART) {
|
||||
atom = UNIT_ATOM_PROPAGATE_RESTART;
|
||||
ptype = JOB_TRY_RESTART;
|
||||
} else {
|
||||
ptype = JOB_STOP;
|
||||
atom = UNIT_ATOM_PROPAGATE_STOP;
|
||||
}
|
||||
|
||||
for (j = 0; j < ELEMENTSOF(propagate_deps); j++)
|
||||
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[propagate_deps[j]]) {
|
||||
JobType nt;
|
||||
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, atom) {
|
||||
JobType nt;
|
||||
|
||||
nt = job_type_collapse(ptype, dep);
|
||||
if (nt == JOB_NOP)
|
||||
continue;
|
||||
nt = job_type_collapse(ptype, dep);
|
||||
if (nt == JOB_NOP)
|
||||
continue;
|
||||
|
||||
r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
if (r != -EBADR) /* job type not applicable */
|
||||
goto fail;
|
||||
r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
if (r != -EBADR) /* job type not applicable */
|
||||
goto fail;
|
||||
|
||||
sd_bus_error_free(e);
|
||||
}
|
||||
sd_bus_error_free(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type == JOB_RELOAD)
|
||||
@ -1150,14 +1130,14 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
|
||||
}
|
||||
|
||||
int transaction_add_triggering_jobs(Transaction *tr, Unit *u) {
|
||||
void *v;
|
||||
Unit *trigger;
|
||||
int r;
|
||||
|
||||
assert(tr);
|
||||
assert(u);
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, trigger, u->dependencies[UNIT_TRIGGERED_BY]) {
|
||||
UNIT_FOREACH_DEPENDENCY(trigger, u, UNIT_ATOM_TRIGGERED_BY) {
|
||||
|
||||
/* No need to stop inactive jobs */
|
||||
if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(trigger)) && !trigger->job)
|
||||
continue;
|
||||
|
240
src/core/unit-dependency-atom.c
Normal file
240
src/core/unit-dependency-atom.c
Normal file
@ -0,0 +1,240 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "unit-dependency-atom.h"
|
||||
|
||||
static const UnitDependencyAtom atom_map[_UNIT_DEPENDENCY_MAX] = {
|
||||
/* A table that maps high-level dependency types to low-level dependency "atoms". The latter actually
|
||||
* describe specific facets of dependency behaviour. The former combine them into one user-facing
|
||||
* concept. Atoms are a bit mask, though a bunch of dependency types have only a single bit set.
|
||||
*
|
||||
* Typically when the user configures a dependency they go via dependency type, but when we act on
|
||||
* them we go by atom.
|
||||
*
|
||||
* NB: when you add a new dependency type here, make sure to also add one to the (best-effort)
|
||||
* reverse table in unit_dependency_from_unique_atom() further down. */
|
||||
|
||||
[UNIT_REQUIRES] = UNIT_ATOM_PULL_IN_START |
|
||||
UNIT_ATOM_RETROACTIVE_START_REPLACE |
|
||||
UNIT_ATOM_ADD_STOP_WHEN_UNNEEDED_QUEUE |
|
||||
UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE,
|
||||
|
||||
[UNIT_REQUISITE] = UNIT_ATOM_PULL_IN_VERIFY |
|
||||
UNIT_ATOM_ADD_STOP_WHEN_UNNEEDED_QUEUE |
|
||||
UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE,
|
||||
|
||||
[UNIT_WANTS] = UNIT_ATOM_PULL_IN_START_IGNORED |
|
||||
UNIT_ATOM_RETROACTIVE_START_FAIL |
|
||||
UNIT_ATOM_ADD_STOP_WHEN_UNNEEDED_QUEUE |
|
||||
UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE,
|
||||
|
||||
[UNIT_BINDS_TO] = UNIT_ATOM_PULL_IN_START |
|
||||
UNIT_ATOM_RETROACTIVE_START_REPLACE |
|
||||
UNIT_ATOM_CANNOT_BE_ACTIVE_WITHOUT |
|
||||
UNIT_ATOM_ADD_STOP_WHEN_UNNEEDED_QUEUE |
|
||||
UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE,
|
||||
|
||||
[UNIT_PART_OF] = UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE,
|
||||
|
||||
[UNIT_UPHOLDS] = UNIT_ATOM_PULL_IN_START_IGNORED |
|
||||
UNIT_ATOM_RETROACTIVE_START_REPLACE |
|
||||
UNIT_ATOM_ADD_START_WHEN_UPHELD_QUEUE |
|
||||
UNIT_ATOM_ADD_STOP_WHEN_UNNEEDED_QUEUE |
|
||||
UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE,
|
||||
|
||||
[UNIT_REQUIRED_BY] = UNIT_ATOM_PROPAGATE_STOP |
|
||||
UNIT_ATOM_PROPAGATE_RESTART |
|
||||
UNIT_ATOM_PROPAGATE_START_FAILURE |
|
||||
UNIT_ATOM_PINS_STOP_WHEN_UNNEEDED |
|
||||
UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES,
|
||||
|
||||
[UNIT_REQUISITE_OF] = UNIT_ATOM_PROPAGATE_STOP |
|
||||
UNIT_ATOM_PROPAGATE_RESTART |
|
||||
UNIT_ATOM_PROPAGATE_START_FAILURE |
|
||||
UNIT_ATOM_PROPAGATE_INACTIVE_START_AS_FAILURE |
|
||||
UNIT_ATOM_PINS_STOP_WHEN_UNNEEDED |
|
||||
UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES,
|
||||
|
||||
[UNIT_WANTED_BY] = UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES |
|
||||
UNIT_ATOM_PINS_STOP_WHEN_UNNEEDED,
|
||||
|
||||
[UNIT_BOUND_BY] = UNIT_ATOM_RETROACTIVE_STOP_ON_STOP |
|
||||
UNIT_ATOM_PROPAGATE_STOP |
|
||||
UNIT_ATOM_PROPAGATE_RESTART |
|
||||
UNIT_ATOM_PROPAGATE_START_FAILURE |
|
||||
UNIT_ATOM_PINS_STOP_WHEN_UNNEEDED |
|
||||
UNIT_ATOM_ADD_CANNOT_BE_ACTIVE_WITHOUT_QUEUE |
|
||||
UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES,
|
||||
|
||||
[UNIT_UPHELD_BY] = UNIT_ATOM_START_STEADILY |
|
||||
UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES |
|
||||
UNIT_ATOM_PINS_STOP_WHEN_UNNEEDED,
|
||||
|
||||
[UNIT_CONSISTS_OF] = UNIT_ATOM_PROPAGATE_STOP |
|
||||
UNIT_ATOM_PROPAGATE_RESTART,
|
||||
|
||||
[UNIT_CONFLICTS] = UNIT_ATOM_PULL_IN_STOP |
|
||||
UNIT_ATOM_RETROACTIVE_STOP_ON_START,
|
||||
|
||||
[UNIT_CONFLICTED_BY] = UNIT_ATOM_PULL_IN_STOP_IGNORED |
|
||||
UNIT_ATOM_RETROACTIVE_STOP_ON_START |
|
||||
UNIT_ATOM_PROPAGATE_STOP_FAILURE,
|
||||
|
||||
[UNIT_PROPAGATES_STOP_TO] = UNIT_ATOM_RETROACTIVE_STOP_ON_STOP |
|
||||
UNIT_ATOM_PROPAGATE_STOP,
|
||||
|
||||
/* These are simple dependency types: they consist of a single atom only */
|
||||
[UNIT_BEFORE] = UNIT_ATOM_BEFORE,
|
||||
[UNIT_AFTER] = UNIT_ATOM_AFTER,
|
||||
[UNIT_ON_SUCCESS] = UNIT_ATOM_ON_SUCCESS,
|
||||
[UNIT_ON_FAILURE] = UNIT_ATOM_ON_FAILURE,
|
||||
[UNIT_TRIGGERS] = UNIT_ATOM_TRIGGERS,
|
||||
[UNIT_TRIGGERED_BY] = UNIT_ATOM_TRIGGERED_BY,
|
||||
[UNIT_PROPAGATES_RELOAD_TO] = UNIT_ATOM_PROPAGATES_RELOAD_TO,
|
||||
[UNIT_JOINS_NAMESPACE_OF] = UNIT_ATOM_JOINS_NAMESPACE_OF,
|
||||
[UNIT_REFERENCES] = UNIT_ATOM_REFERENCES,
|
||||
[UNIT_REFERENCED_BY] = UNIT_ATOM_REFERENCED_BY,
|
||||
[UNIT_IN_SLICE] = UNIT_ATOM_IN_SLICE,
|
||||
[UNIT_SLICE_OF] = UNIT_ATOM_SLICE_OF,
|
||||
|
||||
/* These are dependency types without effect on our state engine. We maintain them only to make
|
||||
* things discoverable/debuggable as they are the inverse dependencies to some of the above. As they
|
||||
* have no effect of their own, they all map to no atoms at all, i.e. the value 0. */
|
||||
[UNIT_RELOAD_PROPAGATED_FROM] = 0,
|
||||
[UNIT_ON_SUCCESS_OF] = 0,
|
||||
[UNIT_ON_FAILURE_OF] = 0,
|
||||
[UNIT_STOP_PROPAGATED_FROM] = 0,
|
||||
};
|
||||
|
||||
UnitDependencyAtom unit_dependency_to_atom(UnitDependency d) {
|
||||
if (d < 0)
|
||||
return _UNIT_DEPENDENCY_ATOM_INVALID;
|
||||
|
||||
assert(d < _UNIT_DEPENDENCY_MAX);
|
||||
|
||||
return atom_map[d];
|
||||
}
|
||||
|
||||
UnitDependency unit_dependency_from_unique_atom(UnitDependencyAtom atom) {
|
||||
|
||||
/* This is a "best-effort" function that maps the specified 'atom' mask to a dependency type that is
|
||||
* is equal to or has a superset of bits set if that's uniquely possible. The idea is that this
|
||||
* function is used when iterating through deps that have a specific atom: if there's exactly one
|
||||
* dependency type of the specific atom we don't need iterate through all deps a unit has, but can
|
||||
* pinpoint things directly.
|
||||
*
|
||||
* This function will return _UNIT_DEPENDENCY_INVALID in case the specified value is not known or not
|
||||
* uniquely defined, i.e. there are multiple dependencies with the atom or the combination set. */
|
||||
|
||||
switch ((int64_t) atom) {
|
||||
|
||||
/* Note that we can't list UNIT_REQUIRES here since it's a true subset of UNIT_BINDS_TO, and
|
||||
* hence its atom bits not uniquely mappable. */
|
||||
|
||||
case UNIT_ATOM_PULL_IN_VERIFY |
|
||||
UNIT_ATOM_ADD_STOP_WHEN_UNNEEDED_QUEUE |
|
||||
UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE:
|
||||
case UNIT_ATOM_PULL_IN_VERIFY: /* a single dep type uses this atom */
|
||||
return UNIT_REQUISITE;
|
||||
|
||||
case UNIT_ATOM_PULL_IN_START_IGNORED |
|
||||
UNIT_ATOM_RETROACTIVE_START_FAIL |
|
||||
UNIT_ATOM_ADD_STOP_WHEN_UNNEEDED_QUEUE |
|
||||
UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE:
|
||||
case UNIT_ATOM_RETROACTIVE_START_FAIL:
|
||||
return UNIT_WANTS;
|
||||
|
||||
case UNIT_ATOM_PULL_IN_START |
|
||||
UNIT_ATOM_RETROACTIVE_START_REPLACE |
|
||||
UNIT_ATOM_CANNOT_BE_ACTIVE_WITHOUT |
|
||||
UNIT_ATOM_ADD_STOP_WHEN_UNNEEDED_QUEUE |
|
||||
UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE:
|
||||
case UNIT_ATOM_CANNOT_BE_ACTIVE_WITHOUT:
|
||||
return UNIT_BINDS_TO;
|
||||
|
||||
case UNIT_ATOM_PULL_IN_START_IGNORED |
|
||||
UNIT_ATOM_RETROACTIVE_START_REPLACE |
|
||||
UNIT_ATOM_ADD_START_WHEN_UPHELD_QUEUE |
|
||||
UNIT_ATOM_ADD_STOP_WHEN_UNNEEDED_QUEUE |
|
||||
UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE:
|
||||
case UNIT_ATOM_ADD_START_WHEN_UPHELD_QUEUE:
|
||||
return UNIT_UPHOLDS;
|
||||
|
||||
case UNIT_ATOM_PROPAGATE_STOP |
|
||||
UNIT_ATOM_PROPAGATE_RESTART |
|
||||
UNIT_ATOM_PROPAGATE_START_FAILURE |
|
||||
UNIT_ATOM_PROPAGATE_INACTIVE_START_AS_FAILURE |
|
||||
UNIT_ATOM_PINS_STOP_WHEN_UNNEEDED |
|
||||
UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES:
|
||||
case UNIT_ATOM_PROPAGATE_INACTIVE_START_AS_FAILURE:
|
||||
return UNIT_REQUISITE_OF;
|
||||
|
||||
case UNIT_ATOM_RETROACTIVE_STOP_ON_STOP |
|
||||
UNIT_ATOM_PROPAGATE_STOP |
|
||||
UNIT_ATOM_PROPAGATE_RESTART |
|
||||
UNIT_ATOM_PROPAGATE_START_FAILURE |
|
||||
UNIT_ATOM_PINS_STOP_WHEN_UNNEEDED |
|
||||
UNIT_ATOM_ADD_CANNOT_BE_ACTIVE_WITHOUT_QUEUE |
|
||||
UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES:
|
||||
case UNIT_ATOM_ADD_CANNOT_BE_ACTIVE_WITHOUT_QUEUE:
|
||||
return UNIT_BOUND_BY;
|
||||
|
||||
case UNIT_ATOM_START_STEADILY |
|
||||
UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES |
|
||||
UNIT_ATOM_PINS_STOP_WHEN_UNNEEDED:
|
||||
case UNIT_ATOM_START_STEADILY:
|
||||
return UNIT_UPHELD_BY;
|
||||
|
||||
case UNIT_ATOM_PULL_IN_STOP |
|
||||
UNIT_ATOM_RETROACTIVE_STOP_ON_START:
|
||||
case UNIT_ATOM_PULL_IN_STOP:
|
||||
return UNIT_CONFLICTS;
|
||||
|
||||
case UNIT_ATOM_PULL_IN_STOP_IGNORED |
|
||||
UNIT_ATOM_RETROACTIVE_STOP_ON_START |
|
||||
UNIT_ATOM_PROPAGATE_STOP_FAILURE:
|
||||
case UNIT_ATOM_PULL_IN_STOP_IGNORED:
|
||||
case UNIT_ATOM_PROPAGATE_STOP_FAILURE:
|
||||
return UNIT_CONFLICTED_BY;
|
||||
|
||||
/* And now, the simple ones */
|
||||
|
||||
case UNIT_ATOM_BEFORE:
|
||||
return UNIT_BEFORE;
|
||||
|
||||
case UNIT_ATOM_AFTER:
|
||||
return UNIT_AFTER;
|
||||
|
||||
case UNIT_ATOM_ON_SUCCESS:
|
||||
return UNIT_ON_SUCCESS;
|
||||
|
||||
case UNIT_ATOM_ON_FAILURE:
|
||||
return UNIT_ON_FAILURE;
|
||||
|
||||
case UNIT_ATOM_TRIGGERS:
|
||||
return UNIT_TRIGGERS;
|
||||
|
||||
case UNIT_ATOM_TRIGGERED_BY:
|
||||
return UNIT_TRIGGERED_BY;
|
||||
|
||||
case UNIT_ATOM_PROPAGATES_RELOAD_TO:
|
||||
return UNIT_PROPAGATES_RELOAD_TO;
|
||||
|
||||
case UNIT_ATOM_JOINS_NAMESPACE_OF:
|
||||
return UNIT_JOINS_NAMESPACE_OF;
|
||||
|
||||
case UNIT_ATOM_REFERENCES:
|
||||
return UNIT_REFERENCES;
|
||||
|
||||
case UNIT_ATOM_REFERENCED_BY:
|
||||
return UNIT_REFERENCED_BY;
|
||||
|
||||
case UNIT_ATOM_IN_SLICE:
|
||||
return UNIT_IN_SLICE;
|
||||
|
||||
case UNIT_ATOM_SLICE_OF:
|
||||
return UNIT_SLICE_OF;
|
||||
|
||||
default:
|
||||
return _UNIT_DEPENDENCY_INVALID;
|
||||
}
|
||||
}
|
87
src/core/unit-dependency-atom.h
Normal file
87
src/core/unit-dependency-atom.h
Normal file
@ -0,0 +1,87 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "unit-def.h"
|
||||
|
||||
/* Flags that identify the various "atomic" behaviours a specific dependency type implies. Each dependency is
|
||||
* a combination of one or more of these flags that define what they actually entail. */
|
||||
typedef enum UnitDependencyAtom {
|
||||
|
||||
/* This unit pulls in the other unit as JOB_START job into the transaction, and if that doesn't work
|
||||
* the transaction fails. */
|
||||
UNIT_ATOM_PULL_IN_START = UINT64_C(1) << 0,
|
||||
/* Similar, but if it doesn't work, ignore. */
|
||||
UNIT_ATOM_PULL_IN_START_IGNORED = UINT64_C(1) << 1,
|
||||
/* Pull in a JOB_VERIFY job into the transaction, i.e. pull in JOB_VERIFY rather than
|
||||
* JOB_START. i.e. check the unit is started but don't pull it in. */
|
||||
UNIT_ATOM_PULL_IN_VERIFY = UINT64_C(1) << 2,
|
||||
|
||||
/* Pull in a JOB_STOP job for the other job into transactions, and fail if that doesn't work. */
|
||||
UNIT_ATOM_PULL_IN_STOP = UINT64_C(1) << 3,
|
||||
/* Same, but don't fail, ignore it. */
|
||||
UNIT_ATOM_PULL_IN_STOP_IGNORED = UINT64_C(1) << 4,
|
||||
|
||||
/* If our enters inactive state, add the other unit to the StopWhenUneeded= queue */
|
||||
UNIT_ATOM_ADD_STOP_WHEN_UNNEEDED_QUEUE = UINT64_C(1) << 5,
|
||||
/* Pin the other unit i.e. ensure StopWhenUneeded= won't trigger for the other unit as long as we are
|
||||
* not in inactive state */
|
||||
UNIT_ATOM_PINS_STOP_WHEN_UNNEEDED = UINT64_C(1) << 6,
|
||||
|
||||
/* Stop our unit if the other unit happens to inactive */
|
||||
UNIT_ATOM_CANNOT_BE_ACTIVE_WITHOUT = UINT64_C(1) << 7,
|
||||
/* If our unit enters inactive state, add the other unit to the BoundBy= queue */
|
||||
UNIT_ATOM_ADD_CANNOT_BE_ACTIVE_WITHOUT_QUEUE = UINT64_C(1) << 8,
|
||||
|
||||
/* Start this unit whenever we find it inactive and the other unit active */
|
||||
UNIT_ATOM_START_STEADILY = UINT64_C(1) << 9,
|
||||
/* Whenever our unit becomes active, add other unit to start_when_upheld_queue */
|
||||
UNIT_ATOM_ADD_START_WHEN_UPHELD_QUEUE = UINT64_C(1) << 10,
|
||||
|
||||
/* If our unit unexpectedly becomes active, retroactively start the other unit too, in "replace" job
|
||||
* mode */
|
||||
UNIT_ATOM_RETROACTIVE_START_REPLACE = UINT64_C(1) << 11,
|
||||
/* Similar, but in "fail" job mode */
|
||||
UNIT_ATOM_RETROACTIVE_START_FAIL = UINT64_C(1) << 12,
|
||||
/* If our unit unexpectedly becomes active, retroactively stop the other unit too */
|
||||
UNIT_ATOM_RETROACTIVE_STOP_ON_START = UINT64_C(1) << 13,
|
||||
/* If our unit unexpectedly becomes inactive, retroactively stop the other unit too */
|
||||
UNIT_ATOM_RETROACTIVE_STOP_ON_STOP = UINT64_C(1) << 14,
|
||||
|
||||
/* If a start job for this unit fails, propagate the failure to start job of other unit too */
|
||||
UNIT_ATOM_PROPAGATE_START_FAILURE = UINT64_C(1) << 15,
|
||||
/* If a stop job for this unit fails, propagate the failure to any stop job of the other unit too */
|
||||
UNIT_ATOM_PROPAGATE_STOP_FAILURE = UINT64_C(1) << 16,
|
||||
/* If our start job succeeded but the unit is inactive then (think: oneshot units), propagate this as
|
||||
* failure to the other unit. */
|
||||
UNIT_ATOM_PROPAGATE_INACTIVE_START_AS_FAILURE = UINT64_C(1) << 17,
|
||||
/* When putting together a transaction, propagate JOB_STOP from our unit to the other. */
|
||||
UNIT_ATOM_PROPAGATE_STOP = UINT64_C(1) << 18,
|
||||
/* When putting together a transaction, propagate JOB_RESTART from our unit to the other. */
|
||||
UNIT_ATOM_PROPAGATE_RESTART = UINT64_C(1) << 19,
|
||||
|
||||
/* Add the other unit to the default target dependency queue */
|
||||
UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE = UINT64_C(1) << 20,
|
||||
/* Recheck default target deps on other units (which are target units) */
|
||||
UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES = UINT64_C(1) << 21,
|
||||
|
||||
/* The remaining atoms map 1:1 to the equally named high-level deps */
|
||||
UNIT_ATOM_BEFORE = UINT64_C(1) << 22,
|
||||
UNIT_ATOM_AFTER = UINT64_C(1) << 23,
|
||||
UNIT_ATOM_ON_SUCCESS = UINT64_C(1) << 24,
|
||||
UNIT_ATOM_ON_FAILURE = UINT64_C(1) << 25,
|
||||
UNIT_ATOM_TRIGGERS = UINT64_C(1) << 26,
|
||||
UNIT_ATOM_TRIGGERED_BY = UINT64_C(1) << 27,
|
||||
UNIT_ATOM_PROPAGATES_RELOAD_TO = UINT64_C(1) << 28,
|
||||
UNIT_ATOM_JOINS_NAMESPACE_OF = UINT64_C(1) << 29,
|
||||
UNIT_ATOM_REFERENCES = UINT64_C(1) << 30,
|
||||
UNIT_ATOM_REFERENCED_BY = UINT64_C(1) << 31,
|
||||
UNIT_ATOM_IN_SLICE = UINT64_C(1) << 32,
|
||||
UNIT_ATOM_SLICE_OF = UINT64_C(1) << 33,
|
||||
_UNIT_DEPENDENCY_ATOM_MAX = (UINT64_C(1) << 34) - 1,
|
||||
_UNIT_DEPENDENCY_ATOM_INVALID = -EINVAL,
|
||||
} UnitDependencyAtom;
|
||||
|
||||
UnitDependencyAtom unit_dependency_to_atom(UnitDependency d);
|
||||
UnitDependency unit_dependency_from_unique_atom(UnitDependencyAtom atom);
|
@ -132,18 +132,15 @@ static int specifier_cgroup_root(char specifier, const void *data, const void *u
|
||||
}
|
||||
|
||||
static int specifier_cgroup_slice(char specifier, const void *data, const void *userdata, char **ret) {
|
||||
const Unit *u = userdata;
|
||||
const Unit *u = userdata, *slice;
|
||||
char *n;
|
||||
|
||||
assert(u);
|
||||
|
||||
bad_specifier(u, specifier);
|
||||
|
||||
if (UNIT_ISSET(u->slice)) {
|
||||
const Unit *slice;
|
||||
|
||||
slice = UNIT_DEREF(u->slice);
|
||||
|
||||
slice = UNIT_GET_SLICE(u);
|
||||
if (slice) {
|
||||
if (slice->cgroup_path)
|
||||
n = strdup(slice->cgroup_path);
|
||||
else
|
||||
|
@ -604,10 +604,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
"%s\tNeed Daemon Reload: %s\n"
|
||||
"%s\tTransient: %s\n"
|
||||
"%s\tPerpetual: %s\n"
|
||||
"%s\tGarbage Collection Mode: %s\n"
|
||||
"%s\tSlice: %s\n"
|
||||
"%s\tCGroup: %s\n"
|
||||
"%s\tCGroup realized: %s\n",
|
||||
"%s\tGarbage Collection Mode: %s\n",
|
||||
prefix, unit_description(u),
|
||||
prefix, strna(u->instance),
|
||||
prefix, unit_load_state_to_string(u->load_state),
|
||||
@ -621,10 +618,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
prefix, yes_no(unit_need_daemon_reload(u)),
|
||||
prefix, yes_no(u->transient),
|
||||
prefix, yes_no(u->perpetual),
|
||||
prefix, collect_mode_to_string(u->collect_mode),
|
||||
prefix, strna(unit_slice_name(u)),
|
||||
prefix, strna(u->cgroup_path),
|
||||
prefix, yes_no(u->cgroup_realized));
|
||||
prefix, collect_mode_to_string(u->collect_mode));
|
||||
|
||||
if (u->markers != 0) {
|
||||
fprintf(f, "%s\tMarkers:", prefix);
|
||||
@ -635,37 +629,47 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
fputs("\n", f);
|
||||
}
|
||||
|
||||
if (u->cgroup_realized_mask != 0) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
(void) cg_mask_to_string(u->cgroup_realized_mask, &s);
|
||||
fprintf(f, "%s\tCGroup realized mask: %s\n", prefix, strnull(s));
|
||||
}
|
||||
if (UNIT_HAS_CGROUP_CONTEXT(u)) {
|
||||
fprintf(f,
|
||||
"%s\tSlice: %s\n"
|
||||
"%s\tCGroup: %s\n"
|
||||
"%s\tCGroup realized: %s\n",
|
||||
prefix, strna(unit_slice_name(u)),
|
||||
prefix, strna(u->cgroup_path),
|
||||
prefix, yes_no(u->cgroup_realized));
|
||||
|
||||
if (u->cgroup_enabled_mask != 0) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
(void) cg_mask_to_string(u->cgroup_enabled_mask, &s);
|
||||
fprintf(f, "%s\tCGroup enabled mask: %s\n", prefix, strnull(s));
|
||||
}
|
||||
if (u->cgroup_realized_mask != 0) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
(void) cg_mask_to_string(u->cgroup_realized_mask, &s);
|
||||
fprintf(f, "%s\tCGroup realized mask: %s\n", prefix, strnull(s));
|
||||
}
|
||||
|
||||
m = unit_get_own_mask(u);
|
||||
if (m != 0) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
(void) cg_mask_to_string(m, &s);
|
||||
fprintf(f, "%s\tCGroup own mask: %s\n", prefix, strnull(s));
|
||||
}
|
||||
if (u->cgroup_enabled_mask != 0) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
(void) cg_mask_to_string(u->cgroup_enabled_mask, &s);
|
||||
fprintf(f, "%s\tCGroup enabled mask: %s\n", prefix, strnull(s));
|
||||
}
|
||||
|
||||
m = unit_get_members_mask(u);
|
||||
if (m != 0) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
(void) cg_mask_to_string(m, &s);
|
||||
fprintf(f, "%s\tCGroup members mask: %s\n", prefix, strnull(s));
|
||||
}
|
||||
m = unit_get_own_mask(u);
|
||||
if (m != 0) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
(void) cg_mask_to_string(m, &s);
|
||||
fprintf(f, "%s\tCGroup own mask: %s\n", prefix, strnull(s));
|
||||
}
|
||||
|
||||
m = unit_get_delegate_mask(u);
|
||||
if (m != 0) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
(void) cg_mask_to_string(m, &s);
|
||||
fprintf(f, "%s\tCGroup delegate mask: %s\n", prefix, strnull(s));
|
||||
m = unit_get_members_mask(u);
|
||||
if (m != 0) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
(void) cg_mask_to_string(m, &s);
|
||||
fprintf(f, "%s\tCGroup members mask: %s\n", prefix, strnull(s));
|
||||
}
|
||||
|
||||
m = unit_get_delegate_mask(u);
|
||||
if (m != 0) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
(void) cg_mask_to_string(m, &s);
|
||||
fprintf(f, "%s\tCGroup delegate mask: %s\n", prefix, strnull(s));
|
||||
}
|
||||
}
|
||||
|
||||
if (!sd_id128_is_null(u->invocation_id))
|
||||
@ -735,7 +739,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
UnitDependencyInfo di;
|
||||
Unit *other;
|
||||
|
||||
HASHMAP_FOREACH_KEY(di.data, other, u->dependencies[d]) {
|
||||
HASHMAP_FOREACH_KEY(di.data, other, unit_get_dependencies(u, d)) {
|
||||
bool space = false;
|
||||
|
||||
fprintf(f, "%s\t%s: %s (", prefix, unit_dependency_to_string(d), other->id);
|
||||
@ -770,12 +774,14 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
"%s\tRefuseManualStart: %s\n"
|
||||
"%s\tRefuseManualStop: %s\n"
|
||||
"%s\tDefaultDependencies: %s\n"
|
||||
"%s\tOnSuccessJobMode: %s\n"
|
||||
"%s\tOnFailureJobMode: %s\n"
|
||||
"%s\tIgnoreOnIsolate: %s\n",
|
||||
prefix, yes_no(u->stop_when_unneeded),
|
||||
prefix, yes_no(u->refuse_manual_start),
|
||||
prefix, yes_no(u->refuse_manual_stop),
|
||||
prefix, yes_no(u->default_dependencies),
|
||||
prefix, job_mode_to_string(u->on_success_job_mode),
|
||||
prefix, job_mode_to_string(u->on_failure_job_mode),
|
||||
prefix, yes_no(u->ignore_on_isolate));
|
||||
|
||||
|
998
src/core/unit.c
998
src/core/unit.c
File diff suppressed because it is too large
Load Diff
111
src/core/unit.h
111
src/core/unit.h
@ -102,6 +102,16 @@ typedef union UnitDependencyInfo {
|
||||
} _packed_;
|
||||
} UnitDependencyInfo;
|
||||
|
||||
/* Newer LLVM versions don't like implicit casts from large pointer types to smaller enums, hence let's add
|
||||
* explicit type-safe helpers for that. */
|
||||
static inline UnitDependency UNIT_DEPENDENCY_FROM_PTR(const void *p) {
|
||||
return PTR_TO_INT(p);
|
||||
}
|
||||
|
||||
static inline void* UNIT_DEPENDENCY_TO_PTR(UnitDependency d) {
|
||||
return INT_TO_PTR(d);
|
||||
}
|
||||
|
||||
#include "job.h"
|
||||
|
||||
struct UnitRef {
|
||||
@ -125,11 +135,13 @@ typedef struct Unit {
|
||||
|
||||
Set *aliases; /* All the other names. */
|
||||
|
||||
/* For each dependency type we maintain a Hashmap whose key is the Unit* object, and the value encodes why the
|
||||
* dependency exists, using the UnitDependencyInfo type */
|
||||
Hashmap *dependencies[_UNIT_DEPENDENCY_MAX];
|
||||
/* For each dependency type we can look up another Hashmap with this, whose key is a Unit* object,
|
||||
* and whose value encodes why the dependency exists, using the UnitDependencyInfo type. i.e. a
|
||||
* Hashmap(UnitDependency → Hashmap(Unit* → UnitDependencyInfo)) */
|
||||
Hashmap *dependencies;
|
||||
|
||||
/* Similar, for RequiresMountsFor= path dependencies. The key is the path, the value the UnitDependencyInfo type */
|
||||
/* Similar, for RequiresMountsFor= path dependencies. The key is the path, the value the
|
||||
* UnitDependencyInfo type */
|
||||
Hashmap *requires_mounts_for;
|
||||
|
||||
char *description;
|
||||
@ -190,8 +202,6 @@ typedef struct Unit {
|
||||
dual_timestamp active_exit_timestamp;
|
||||
dual_timestamp inactive_enter_timestamp;
|
||||
|
||||
UnitRef slice;
|
||||
|
||||
/* Per type list */
|
||||
LIST_FIELDS(Unit, units_by_type);
|
||||
|
||||
@ -219,9 +229,15 @@ typedef struct Unit {
|
||||
/* Target dependencies queue */
|
||||
LIST_FIELDS(Unit, target_deps_queue);
|
||||
|
||||
/* Queue of units with StopWhenUnneeded set that shell be checked for clean-up. */
|
||||
/* Queue of units with StopWhenUnneeded= set that shall be checked for clean-up. */
|
||||
LIST_FIELDS(Unit, stop_when_unneeded_queue);
|
||||
|
||||
/* Queue of units that have an Uphold= dependency from some other unit, and should be checked for starting */
|
||||
LIST_FIELDS(Unit, start_when_upheld_queue);
|
||||
|
||||
/* Queue of units that have a BindTo= dependency on some other unit, and should possibly be shut down */
|
||||
LIST_FIELDS(Unit, stop_when_bound_queue);
|
||||
|
||||
/* PIDs we keep an eye on. Note that a unit might have many
|
||||
* more, but these are the ones we care enough about to
|
||||
* process SIGCHLD for */
|
||||
@ -250,8 +266,8 @@ typedef struct Unit {
|
||||
int success_action_exit_status, failure_action_exit_status;
|
||||
char *reboot_arg;
|
||||
|
||||
/* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */
|
||||
RateLimit auto_stop_ratelimit;
|
||||
/* Make sure we never enter endless loops with the StopWhenUnneeded=, BindsTo=, Uphold= logic */
|
||||
RateLimit auto_start_stop_ratelimit;
|
||||
|
||||
/* Reference to a specific UID/GID */
|
||||
uid_t ref_uid;
|
||||
@ -324,7 +340,8 @@ typedef struct Unit {
|
||||
* ones which might have appeared. */
|
||||
sd_event_source *rewatch_pids_event_source;
|
||||
|
||||
/* How to start OnFailure units */
|
||||
/* How to start OnSuccess=/OnFailure= units */
|
||||
JobMode on_success_job_mode;
|
||||
JobMode on_failure_job_mode;
|
||||
|
||||
/* Tweaking the GC logic */
|
||||
@ -372,6 +389,8 @@ typedef struct Unit {
|
||||
bool in_cgroup_oom_queue:1;
|
||||
bool in_target_deps_queue:1;
|
||||
bool in_stop_when_unneeded_queue:1;
|
||||
bool in_start_when_upheld_queue:1;
|
||||
bool in_stop_when_bound_queue:1;
|
||||
|
||||
bool sent_dbus_new_signal:1;
|
||||
|
||||
@ -683,8 +702,19 @@ static inline const UnitVTable* UNIT_VTABLE(const Unit *u) {
|
||||
#define UNIT_HAS_CGROUP_CONTEXT(u) (UNIT_VTABLE(u)->cgroup_context_offset > 0)
|
||||
#define UNIT_HAS_KILL_CONTEXT(u) (UNIT_VTABLE(u)->kill_context_offset > 0)
|
||||
|
||||
Unit* unit_has_dependency(const Unit *u, UnitDependencyAtom atom, Unit *other);
|
||||
int unit_get_dependency_array(const Unit *u, UnitDependencyAtom atom, Unit ***ret_array);
|
||||
|
||||
static inline Hashmap* unit_get_dependencies(Unit *u, UnitDependency d) {
|
||||
return hashmap_get(u->dependencies, UNIT_DEPENDENCY_TO_PTR(d));
|
||||
}
|
||||
|
||||
static inline Unit* UNIT_TRIGGER(Unit *u) {
|
||||
return hashmap_first_key(u->dependencies[UNIT_TRIGGERS]);
|
||||
return unit_has_dependency(u, UNIT_ATOM_TRIGGERS, NULL);
|
||||
}
|
||||
|
||||
static inline Unit* UNIT_GET_SLICE(const Unit *u) {
|
||||
return unit_has_dependency(u, UNIT_ATOM_IN_SLICE, NULL);
|
||||
}
|
||||
|
||||
Unit* unit_new(Manager *m, size_t size);
|
||||
@ -718,6 +748,8 @@ void unit_add_to_cleanup_queue(Unit *u);
|
||||
void unit_add_to_gc_queue(Unit *u);
|
||||
void unit_add_to_target_deps_queue(Unit *u);
|
||||
void unit_submit_to_stop_when_unneeded_queue(Unit *u);
|
||||
void unit_submit_to_start_when_upheld_queue(Unit *u);
|
||||
void unit_submit_to_stop_when_bound_queue(Unit *u);
|
||||
|
||||
int unit_merge(Unit *u, Unit *other);
|
||||
int unit_merge_by_name(Unit *u, const char *other);
|
||||
@ -727,7 +759,7 @@ Unit *unit_follow_merge(Unit *u) _pure_;
|
||||
int unit_load_fragment_and_dropin(Unit *u, bool fragment_required);
|
||||
int unit_load(Unit *unit);
|
||||
|
||||
int unit_set_slice(Unit *u, Unit *slice);
|
||||
int unit_set_slice(Unit *u, Unit *slice, UnitDependencyMask mask);
|
||||
int unit_set_default_slice(Unit *u);
|
||||
|
||||
const char *unit_description(Unit *u) _pure_;
|
||||
@ -807,7 +839,7 @@ bool unit_will_restart(Unit *u);
|
||||
|
||||
int unit_add_default_target_dependency(Unit *u, Unit *target);
|
||||
|
||||
void unit_start_on_failure(Unit *u);
|
||||
void unit_start_on_failure(Unit *u, const char *dependency_name, UnitDependencyAtom atom, JobMode job_mode);
|
||||
void unit_trigger_notify(Unit *u);
|
||||
|
||||
UnitFileState unit_get_unit_file_state(Unit *u);
|
||||
@ -847,6 +879,8 @@ bool unit_type_supported(UnitType t);
|
||||
bool unit_is_pristine(Unit *u);
|
||||
|
||||
bool unit_is_unneeded(Unit *u);
|
||||
bool unit_is_upheld_by_active(Unit *u, Unit **ret_culprit);
|
||||
bool unit_is_bound_by_inactive(Unit *u, Unit **ret_culprit);
|
||||
|
||||
pid_t unit_control_pid(Unit *u);
|
||||
pid_t unit_main_pid(Unit *u);
|
||||
@ -990,3 +1024,54 @@ int unit_thaw_vtable_common(Unit *u);
|
||||
|
||||
const char* collect_mode_to_string(CollectMode m) _const_;
|
||||
CollectMode collect_mode_from_string(const char *s) _pure_;
|
||||
|
||||
typedef struct UnitForEachDependencyData {
|
||||
/* Stores state for the FOREACH macro below for iterating through all deps that have any of the
|
||||
* specified dependency atom bits set */
|
||||
UnitDependencyAtom match_atom;
|
||||
Hashmap *by_type, *by_unit;
|
||||
void *current_type;
|
||||
Iterator by_type_iterator, by_unit_iterator;
|
||||
Unit **current_unit;
|
||||
} UnitForEachDependencyData;
|
||||
|
||||
/* Iterates through all dependencies that have a specific atom in the dependency type set. This tries to be
|
||||
* smart: if the atom is unique, we'll directly go to right entry. Otherwise we'll iterate through the
|
||||
* per-dependency type hashmap and match all dep that have the right atom set. */
|
||||
#define _UNIT_FOREACH_DEPENDENCY(other, u, ma, data) \
|
||||
for (UnitForEachDependencyData data = { \
|
||||
.match_atom = (ma), \
|
||||
.by_type = (u)->dependencies, \
|
||||
.by_type_iterator = ITERATOR_FIRST, \
|
||||
.current_unit = &(other), \
|
||||
}; \
|
||||
({ \
|
||||
UnitDependency _dt = _UNIT_DEPENDENCY_INVALID; \
|
||||
bool _found; \
|
||||
\
|
||||
if (data.by_type && ITERATOR_IS_FIRST(data.by_type_iterator)) { \
|
||||
_dt = unit_dependency_from_unique_atom(data.match_atom); \
|
||||
if (_dt >= 0) { \
|
||||
data.by_unit = hashmap_get(data.by_type, UNIT_DEPENDENCY_TO_PTR(_dt)); \
|
||||
data.current_type = UNIT_DEPENDENCY_TO_PTR(_dt); \
|
||||
data.by_type = NULL; \
|
||||
_found = !!data.by_unit; \
|
||||
} \
|
||||
} \
|
||||
if (_dt < 0) \
|
||||
_found = hashmap_iterate(data.by_type, \
|
||||
&data.by_type_iterator, \
|
||||
(void**)&(data.by_unit), \
|
||||
(const void**) &(data.current_type)); \
|
||||
_found; \
|
||||
}); ) \
|
||||
if ((unit_dependency_to_atom(UNIT_DEPENDENCY_FROM_PTR(data.current_type)) & data.match_atom) != 0) \
|
||||
for (data.by_unit_iterator = ITERATOR_FIRST; \
|
||||
hashmap_iterate(data.by_unit, \
|
||||
&data.by_unit_iterator, \
|
||||
NULL, \
|
||||
(const void**) data.current_unit); )
|
||||
|
||||
/* Note: this matches deps that have *any* of the atoms specified in match_atom set */
|
||||
#define UNIT_FOREACH_DEPENDENCY(other, u, match_atom) \
|
||||
_UNIT_FOREACH_DEPENDENCY(other, u, match_atom, UNIQ_T(data, UNIQ))
|
||||
|
@ -70,13 +70,13 @@ static int test_cgroup_mask(void) {
|
||||
assert_se(manager_load_startable_unit_or_warn(m, "parent-deep.slice", NULL, &parent_deep) >= 0);
|
||||
assert_se(manager_load_startable_unit_or_warn(m, "nomem.slice", NULL, &nomem_parent) >= 0);
|
||||
assert_se(manager_load_startable_unit_or_warn(m, "nomemleaf.service", NULL, &nomem_leaf) >= 0);
|
||||
assert_se(UNIT_DEREF(son->slice) == parent);
|
||||
assert_se(UNIT_DEREF(daughter->slice) == parent);
|
||||
assert_se(UNIT_DEREF(parent_deep->slice) == parent);
|
||||
assert_se(UNIT_DEREF(grandchild->slice) == parent_deep);
|
||||
assert_se(UNIT_DEREF(nomem_leaf->slice) == nomem_parent);
|
||||
root = UNIT_DEREF(parent->slice);
|
||||
assert_se(UNIT_DEREF(nomem_parent->slice) == root);
|
||||
assert_se(UNIT_GET_SLICE(son) == parent);
|
||||
assert_se(UNIT_GET_SLICE(daughter) == parent);
|
||||
assert_se(UNIT_GET_SLICE(parent_deep) == parent);
|
||||
assert_se(UNIT_GET_SLICE(grandchild) == parent_deep);
|
||||
assert_se(UNIT_GET_SLICE(nomem_leaf) == nomem_parent);
|
||||
root = UNIT_GET_SLICE(parent);
|
||||
assert_se(UNIT_GET_SLICE(nomem_parent) == root);
|
||||
|
||||
/* Verify per-unit cgroups settings. */
|
||||
ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(son), CGROUP_MASK_CPU);
|
||||
|
@ -91,28 +91,28 @@ static int test_default_memory_low(void) {
|
||||
assert_se(manager_load_startable_unit_or_warn(m, "dml.slice", NULL, &dml) >= 0);
|
||||
|
||||
assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough.slice", NULL, &dml_passthrough) >= 0);
|
||||
assert_se(UNIT_DEREF(dml_passthrough->slice) == dml);
|
||||
assert_se(UNIT_GET_SLICE(dml_passthrough) == dml);
|
||||
assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-empty.service", NULL, &dml_passthrough_empty) >= 0);
|
||||
assert_se(UNIT_DEREF(dml_passthrough_empty->slice) == dml_passthrough);
|
||||
assert_se(UNIT_GET_SLICE(dml_passthrough_empty) == dml_passthrough);
|
||||
assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-dml.service", NULL, &dml_passthrough_set_dml) >= 0);
|
||||
assert_se(UNIT_DEREF(dml_passthrough_set_dml->slice) == dml_passthrough);
|
||||
assert_se(UNIT_GET_SLICE(dml_passthrough_set_dml) == dml_passthrough);
|
||||
assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-ml.service", NULL, &dml_passthrough_set_ml) >= 0);
|
||||
assert_se(UNIT_DEREF(dml_passthrough_set_ml->slice) == dml_passthrough);
|
||||
assert_se(UNIT_GET_SLICE(dml_passthrough_set_ml) == dml_passthrough);
|
||||
|
||||
assert_se(manager_load_startable_unit_or_warn(m, "dml-override.slice", NULL, &dml_override) >= 0);
|
||||
assert_se(UNIT_DEREF(dml_override->slice) == dml);
|
||||
assert_se(UNIT_GET_SLICE(dml_override) == dml);
|
||||
assert_se(manager_load_startable_unit_or_warn(m, "dml-override-empty.service", NULL, &dml_override_empty) >= 0);
|
||||
assert_se(UNIT_DEREF(dml_override_empty->slice) == dml_override);
|
||||
assert_se(UNIT_GET_SLICE(dml_override_empty) == dml_override);
|
||||
|
||||
assert_se(manager_load_startable_unit_or_warn(m, "dml-discard.slice", NULL, &dml_discard) >= 0);
|
||||
assert_se(UNIT_DEREF(dml_discard->slice) == dml);
|
||||
assert_se(UNIT_GET_SLICE(dml_discard) == dml);
|
||||
assert_se(manager_load_startable_unit_or_warn(m, "dml-discard-empty.service", NULL, &dml_discard_empty) >= 0);
|
||||
assert_se(UNIT_DEREF(dml_discard_empty->slice) == dml_discard);
|
||||
assert_se(UNIT_GET_SLICE(dml_discard_empty) == dml_discard);
|
||||
assert_se(manager_load_startable_unit_or_warn(m, "dml-discard-set-ml.service", NULL, &dml_discard_set_ml) >= 0);
|
||||
assert_se(UNIT_DEREF(dml_discard_set_ml->slice) == dml_discard);
|
||||
assert_se(UNIT_GET_SLICE(dml_discard_set_ml) == dml_discard);
|
||||
|
||||
root = UNIT_DEREF(dml->slice);
|
||||
assert_se(!UNIT_ISSET(root->slice));
|
||||
assert_se(root = UNIT_GET_SLICE(dml));
|
||||
assert_se(!UNIT_GET_SLICE(root));
|
||||
|
||||
assert_se(unit_get_ancestor_memory_low(root) == CGROUP_LIMIT_MIN);
|
||||
|
||||
|
@ -6,16 +6,75 @@
|
||||
#include "bus-util.h"
|
||||
#include "manager.h"
|
||||
#include "rm-rf.h"
|
||||
#include "service.h"
|
||||
#include "special.h"
|
||||
#include "strv.h"
|
||||
#include "tests.h"
|
||||
#include "service.h"
|
||||
#include "unit-serialize.h"
|
||||
|
||||
static void verify_dependency_atoms(void) {
|
||||
UnitDependencyAtom combined = 0, multi_use_atoms = 0;
|
||||
|
||||
/* Let's guarantee that our dependency type/atom translation tables are fully correct */
|
||||
|
||||
for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
|
||||
UnitDependencyAtom a;
|
||||
UnitDependency reverse;
|
||||
bool has_superset = false;
|
||||
|
||||
assert_se((a = unit_dependency_to_atom(d)) >= 0);
|
||||
|
||||
for (UnitDependency t = 0; t < _UNIT_DEPENDENCY_MAX; t++) {
|
||||
UnitDependencyAtom b;
|
||||
|
||||
if (t == d)
|
||||
continue;
|
||||
|
||||
assert_se((b = unit_dependency_to_atom(t)) >= 0);
|
||||
|
||||
if ((a & b) == a) {
|
||||
has_superset = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reverse = unit_dependency_from_unique_atom(a);
|
||||
assert_se(reverse == _UNIT_DEPENDENCY_INVALID || reverse >= 0);
|
||||
|
||||
assert_se((reverse < 0) == has_superset); /* If one dependency type is a superset of another,
|
||||
* then the reverse mapping is not unique, verify
|
||||
* that. */
|
||||
|
||||
log_info("Verified dependency type: %s", unit_dependency_to_string(d));
|
||||
|
||||
multi_use_atoms |= combined & a;
|
||||
combined |= a;
|
||||
}
|
||||
|
||||
/* Make sure all atoms are used, i.e. there's at least one dependency type that references it. */
|
||||
assert_se(combined == _UNIT_DEPENDENCY_ATOM_MAX);
|
||||
|
||||
for (UnitDependencyAtom a = 1; a <= _UNIT_DEPENDENCY_ATOM_MAX; a <<= 1) {
|
||||
|
||||
if (multi_use_atoms & a) {
|
||||
/* If an atom is used by multiple dep types, then mapping the atom to a dependency is
|
||||
* not unique and *must* fail */
|
||||
assert_se(unit_dependency_from_unique_atom(a) == _UNIT_DEPENDENCY_INVALID);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If only a single dep type uses specific atom, let's guarantee our mapping table is
|
||||
complete, and thus the atom can be mapped to the single dep type that is used. */
|
||||
assert_se(unit_dependency_from_unique_atom(a) >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(manager_freep) Manager *m = NULL;
|
||||
Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL,
|
||||
*h = NULL, *i = NULL, *a_conj = NULL, *unit_with_multiple_dashes = NULL;
|
||||
*h = NULL, *i = NULL, *a_conj = NULL, *unit_with_multiple_dashes = NULL, *stub = NULL;
|
||||
Job *j;
|
||||
int r;
|
||||
|
||||
@ -122,37 +181,83 @@ int main(int argc, char *argv[]) {
|
||||
assert_se(manager_add_job(m, JOB_START, a_conj, JOB_REPLACE, NULL, NULL, &j) == -EDEADLK);
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
|
||||
assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b));
|
||||
assert_se(!hashmap_get(b->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], c));
|
||||
assert_se(!hashmap_get(c->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
|
||||
assert_se(unit_add_dependency(a, UNIT_PROPAGATES_RELOAD_TO, b, true, UNIT_DEPENDENCY_UDEV) == 0);
|
||||
assert_se(unit_add_dependency(a, UNIT_PROPAGATES_RELOAD_TO, c, true, UNIT_DEPENDENCY_PROC_SWAP) == 0);
|
||||
|
||||
assert_se(hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b));
|
||||
assert_se(hashmap_get(b->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], c));
|
||||
assert_se(hashmap_get(c->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b));
|
||||
assert_se(hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
assert_se(hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c));
|
||||
assert_se(hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
|
||||
unit_remove_dependencies(a, UNIT_DEPENDENCY_UDEV);
|
||||
|
||||
assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b));
|
||||
assert_se(!hashmap_get(b->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], c));
|
||||
assert_se(hashmap_get(c->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
assert_se(hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c));
|
||||
assert_se(hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
|
||||
unit_remove_dependencies(a, UNIT_DEPENDENCY_PROC_SWAP);
|
||||
|
||||
assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b));
|
||||
assert_se(!hashmap_get(b->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], c));
|
||||
assert_se(!hashmap_get(c->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
|
||||
assert_se(manager_load_unit(m, "unit-with-multiple-dashes.service", NULL, NULL, &unit_with_multiple_dashes) >= 0);
|
||||
|
||||
assert_se(strv_equal(unit_with_multiple_dashes->documentation, STRV_MAKE("man:test", "man:override2", "man:override3")));
|
||||
assert_se(streq_ptr(unit_with_multiple_dashes->description, "override4"));
|
||||
|
||||
/* Now merge a synthetic unit into the existing one */
|
||||
assert_se(unit_new_for_name(m, sizeof(Service), "merged.service", &stub) >= 0);
|
||||
assert_se(unit_add_dependency_by_name(stub, UNIT_AFTER, SPECIAL_BASIC_TARGET, true, UNIT_DEPENDENCY_FILE) >= 0);
|
||||
assert_se(unit_add_dependency_by_name(stub, UNIT_AFTER, "quux.target", true, UNIT_DEPENDENCY_FILE) >= 0);
|
||||
assert_se(unit_add_dependency_by_name(stub, UNIT_AFTER, SPECIAL_ROOT_SLICE, true, UNIT_DEPENDENCY_FILE) >= 0);
|
||||
assert_se(unit_add_dependency_by_name(stub, UNIT_REQUIRES, "non-existing.mount", true, UNIT_DEPENDENCY_FILE) >= 0);
|
||||
assert_se(unit_add_dependency_by_name(stub, UNIT_ON_FAILURE, "non-existing-on-failure.target", true, UNIT_DEPENDENCY_FILE) >= 0);
|
||||
|
||||
log_info("/* Merging a+stub, dumps before */");
|
||||
unit_dump(a, stderr, NULL);
|
||||
unit_dump(stub, stderr, NULL);
|
||||
assert_se(unit_merge(a, stub) >= 0);
|
||||
log_info("/* Dump of merged a+stub */");
|
||||
unit_dump(a, stderr, NULL);
|
||||
|
||||
assert_se(unit_has_dependency(a, UNIT_ATOM_AFTER, manager_get_unit(m, SPECIAL_BASIC_TARGET)));
|
||||
assert_se(unit_has_dependency(a, UNIT_ATOM_AFTER, manager_get_unit(m, "quux.target")));
|
||||
assert_se(unit_has_dependency(a, UNIT_ATOM_AFTER, manager_get_unit(m, SPECIAL_ROOT_SLICE)));
|
||||
assert_se(unit_has_dependency(a, UNIT_ATOM_PULL_IN_START, manager_get_unit(m, "non-existing.mount")));
|
||||
assert_se(unit_has_dependency(a, UNIT_ATOM_RETROACTIVE_START_REPLACE, manager_get_unit(m, "non-existing.mount")));
|
||||
assert_se(unit_has_dependency(a, UNIT_ATOM_ON_FAILURE, manager_get_unit(m, "non-existing-on-failure.target")));
|
||||
assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_FAILURE, manager_get_unit(m, "basic.target")));
|
||||
assert_se(!unit_has_dependency(a, UNIT_ATOM_PROPAGATES_RELOAD_TO, manager_get_unit(m, "non-existing-on-failure.target")));
|
||||
|
||||
assert_se(unit_has_name(a, "a.service"));
|
||||
assert_se(unit_has_name(a, "merged.service"));
|
||||
|
||||
unsigned mm = 1;
|
||||
Unit *other;
|
||||
|
||||
UNIT_FOREACH_DEPENDENCY(other, a, UNIT_ATOM_AFTER) {
|
||||
mm *= unit_has_name(other, SPECIAL_BASIC_TARGET) ? 3 : 1;
|
||||
mm *= unit_has_name(other, "quux.target") ? 5 : 1;
|
||||
mm *= unit_has_name(other, SPECIAL_ROOT_SLICE) ? 7 : 1;
|
||||
}
|
||||
|
||||
UNIT_FOREACH_DEPENDENCY(other, a, UNIT_ATOM_ON_FAILURE)
|
||||
mm *= unit_has_name(other, "non-existing-on-failure.target") ? 11 : 1;
|
||||
|
||||
UNIT_FOREACH_DEPENDENCY(other, a, UNIT_ATOM_PULL_IN_START)
|
||||
mm *= unit_has_name(other, "non-existing.mount") ? 13 : 1;
|
||||
|
||||
assert_se(mm == 3U*5U*7U*11U*13U);
|
||||
|
||||
verify_dependency_atoms();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
1
test/TEST-57-ONSUCCESS-UPHOLD/Makefile
Symbolic link
1
test/TEST-57-ONSUCCESS-UPHOLD/Makefile
Symbolic link
@ -0,0 +1 @@
|
||||
../TEST-01-BASIC/Makefile
|
7
test/TEST-57-ONSUCCESS-UPHOLD/test.sh
Executable file
7
test/TEST-57-ONSUCCESS-UPHOLD/test.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
TEST_DESCRIPTION="test OnSuccess= + Uphold= + PropagatesStopTo= + BindsTo="
|
||||
. $TEST_BASE_DIR/test-functions
|
||||
|
||||
do_test "$@" 57
|
@ -76,10 +76,13 @@ JoinsNamespaceOf=
|
||||
OnFailure=
|
||||
OnFailureIsolate=
|
||||
OnFailureJobMode=
|
||||
OnSuccess=
|
||||
OnSuccessJobMode=
|
||||
PartOf=
|
||||
PropagateReloadFrom=
|
||||
PropagateReloadTo=
|
||||
PropagatesReloadTo=
|
||||
PropagatesStopTo=
|
||||
RebootArgument=
|
||||
RefuseManualStart=
|
||||
RefuseManualStop=
|
||||
@ -97,8 +100,10 @@ StartLimitBurst=
|
||||
StartLimitInterval=
|
||||
StartLimitIntervalSec=
|
||||
StopWhenUnneeded=
|
||||
StopPropagatedFrom=
|
||||
SuccessAction=
|
||||
SuccessActionExitStatus=
|
||||
Upholds=
|
||||
Wants=
|
||||
[Install]
|
||||
Alias=
|
||||
|
8
test/units/testsuite-57-binds-to.service
Normal file
8
test/units/testsuite-57-binds-to.service
Normal file
@ -0,0 +1,8 @@
|
||||
[Unit]
|
||||
Description=Unit with BindsTo=
|
||||
BindsTo=testsuite-57-bound-by.service
|
||||
After=testsuite-57-bound-by.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/sleep infinity
|
||||
ExecStopPost=systemctl kill --kill-who=main -sRTMIN+1 testsuite-57.service
|
5
test/units/testsuite-57-bound-by.service
Normal file
5
test/units/testsuite-57-bound-by.service
Normal file
@ -0,0 +1,5 @@
|
||||
[Unit]
|
||||
Description=Unit with BoundBy=
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/sleep 0.7
|
6
test/units/testsuite-57-fail.service
Normal file
6
test/units/testsuite-57-fail.service
Normal file
@ -0,0 +1,6 @@
|
||||
[Unit]
|
||||
Description=Failing unit
|
||||
OnFailure=testsuite-57-uphold.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/false
|
9
test/units/testsuite-57-prop-stop-one.service
Normal file
9
test/units/testsuite-57-prop-stop-one.service
Normal file
@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Stop Propagation Receiver
|
||||
Wants=testsuite-57-prop-stop-two.service
|
||||
After=testsuite-57-prop-stop-two.service
|
||||
StopPropagatedFrom=testsuite-57-prop-stop-two.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/sleep infinity
|
||||
ExecStopPost=systemctl kill --kill-who=main -sUSR2 testsuite-57.service
|
5
test/units/testsuite-57-prop-stop-two.service
Normal file
5
test/units/testsuite-57-prop-stop-two.service
Normal file
@ -0,0 +1,5 @@
|
||||
[Unit]
|
||||
Description=Stop Propagation Sender
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/sleep 1.5
|
10
test/units/testsuite-57-short-lived.service
Normal file
10
test/units/testsuite-57-short-lived.service
Normal file
@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=Shortlived Unit
|
||||
StopWhenUnneeded=yes
|
||||
|
||||
# Bump up the start limit logic, so that we can be restarted frequently enough
|
||||
StartLimitBurst=15
|
||||
StartLimitIntervalSec=1h
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/lib/systemd/tests/testdata/units/testsuite-57-short-lived.sh
|
18
test/units/testsuite-57-short-lived.sh
Executable file
18
test/units/testsuite-57-short-lived.sh
Executable file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
if [ -f /tmp/testsuite-57.counter ] ; then
|
||||
read -r counter < /tmp/testsuite-57.counter
|
||||
counter=$(("$counter" + 1))
|
||||
else
|
||||
counter=0
|
||||
fi
|
||||
|
||||
echo "$counter" > /tmp/testsuite-57.counter
|
||||
|
||||
if [ "$counter" -eq 5 ] ; then
|
||||
systemctl kill --kill-who=main -sUSR1 testsuite-57.service
|
||||
fi
|
||||
|
||||
exec sleep 1.5
|
6
test/units/testsuite-57-success.service
Normal file
6
test/units/testsuite-57-success.service
Normal file
@ -0,0 +1,6 @@
|
||||
[Unit]
|
||||
Description=Succeeding unit
|
||||
OnSuccess=testsuite-57-fail.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/true
|
6
test/units/testsuite-57-uphold.service
Normal file
6
test/units/testsuite-57-uphold.service
Normal file
@ -0,0 +1,6 @@
|
||||
[Unit]
|
||||
Description=Upholding Unit
|
||||
Upholds=testsuite-57-short-lived.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/sleep infinity
|
7
test/units/testsuite-57.service
Normal file
7
test/units/testsuite-57.service
Normal file
@ -0,0 +1,7 @@
|
||||
[Unit]
|
||||
Description=TEST-57-ONSUCCESS-UPHOLD
|
||||
|
||||
[Service]
|
||||
ExecStartPre=rm -f /failed /testok
|
||||
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
|
||||
Type=oneshot
|
68
test/units/testsuite-57.sh
Executable file
68
test/units/testsuite-57.sh
Executable file
@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
systemd-analyze log-level debug
|
||||
systemd-analyze log-target journal
|
||||
|
||||
# Idea is this:
|
||||
# 1. we start testsuite-57-success.service
|
||||
# 2. which through OnSuccess= starts testsuite-57-fail.service,
|
||||
# 3. which through OnFailure= starts testsuite-57-uphold.service,
|
||||
# 4. which through Uphold= starts/keeps testsuite-57-short-lived.service running,
|
||||
# 5. which will sleep 1s when invoked, and on the 5th invocation send us a SIGUSR1
|
||||
# 6. once we got that we finish cleanly
|
||||
|
||||
sigusr1=0
|
||||
trap sigusr1=1 SIGUSR1
|
||||
|
||||
systemctl start testsuite-57-success.service
|
||||
|
||||
while [ "$sigusr1" -eq 0 ] ; do
|
||||
sleep .5
|
||||
done
|
||||
|
||||
systemctl stop testsuite-57-uphold.service
|
||||
|
||||
# Idea is this:
|
||||
# 1. we start testsuite-57-prop-stop-one.service
|
||||
# 2. which through Wants=/After= pulls in testsuite-57-prop-stop-two.service as well
|
||||
# 3. testsuite-57-prop-stop-one.service then sleeps indefinitely
|
||||
# 4. testsuite-57-prop-stop-two.service sleeps a short time and exits
|
||||
# 5. the StopPropagatedFrom= dependency between the two should ensure *both* will exit as result
|
||||
# 6. an ExecStopPost= line on testsuite-57-prop-stop-one.service will send us a SIGUSR2
|
||||
# 7. once we got that we finish cleanly
|
||||
|
||||
sigusr2=0
|
||||
trap sigusr2=1 SIGUSR2
|
||||
|
||||
systemctl start testsuite-57-prop-stop-one.service
|
||||
|
||||
while [ "$sigusr2" -eq 0 ] ; do
|
||||
sleep .5
|
||||
done
|
||||
|
||||
|
||||
# Idea is this:
|
||||
# 1. we start testsuite-57-binds-to.service
|
||||
# 2. which through BindsTo=/After= pulls in testsuite-57-bound-by.service as well
|
||||
# 3. testsuite-57-bound-by.service suddenly dies
|
||||
# 4. testsuite-57-binds-to.service should then also be pulled down (it otherwise just hangs)
|
||||
# 6. an ExecStopPost= line on testsuite-57-binds-to.service will send us a SIGRTMIN1+1
|
||||
# 7. once we got that we finish cleanly
|
||||
|
||||
sigrtmin1=0
|
||||
trap sigrtmin1=1 SIGRTMIN+1
|
||||
|
||||
systemctl start testsuite-57-binds-to.service
|
||||
|
||||
while [ "$sigrtmin1" -eq 0 ] ; do
|
||||
sleep .5
|
||||
done
|
||||
|
||||
systemd-analyze log-level info
|
||||
|
||||
echo OK >/testok
|
||||
|
||||
exit 0
|
Loading…
Reference in New Issue
Block a user