1
0
mirror of https://github.com/systemd/systemd synced 2026-03-17 02:24:48 +01:00

Compare commits

..

No commits in common. "89c629fc4b67631d133ee2f432dcb85e7a182c56" and "9caf9859b4608b88a38619d65d890ab6f2fcac43" have entirely different histories.

56 changed files with 843 additions and 2044 deletions

View File

@ -659,9 +659,9 @@
<varlistentry> <varlistentry>
<term><option>tpm2-pcrs=</option></term> <term><option>tpm2-pcrs=</option></term>
<listitem><para>Takes a <literal>+</literal> separated list of numeric TPM2 PCR (i.e. "Platform <listitem><para>Takes a comma separated list of numeric TPM2 PCR (i.e. "Platform Configuration
Configuration Register") indexes to bind the TPM2 volume unlocking to. This option is only useful Register") indexes to bind the TPM2 volume unlocking to. This option is only useful when TPM2
when TPM2 enrollment metadata is not available in the LUKS2 JSON token header already, the way enrollment metadata is not available in the LUKS2 JSON token header already, the way
<command>systemd-cryptenroll</command> writes it there. If not used (and no metadata in the LUKS2 <command>systemd-cryptenroll</command> writes it there. If not used (and no metadata in the LUKS2
JSON token header defines it), defaults to a list of a single entry: PCR 7. Assign an empty string to JSON token header defines it), defaults to a list of a single entry: PCR 7. Assign an empty string to
encode a policy that binds the key to no PCRs, making the key accessible to local programs regardless encode a policy that binds the key to no PCRs, making the key accessible to local programs regardless

View File

@ -113,18 +113,18 @@
<varlistentry> <varlistentry>
<term><command>terminate-session</command> <replaceable>ID</replaceable></term> <term><command>terminate-session</command> <replaceable>ID</replaceable></term>
<listitem><para>Terminates a session. This kills all processes of the session and deallocates all <listitem><para>Terminates a session. This kills all processes
resources attached to the session. If the argument is specified as empty string the session invoking of the session and deallocates all resources attached to the
the command is terminated.</para></listitem> session. </para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><command>kill-session</command> <replaceable>ID</replaceable></term> <term><command>kill-session</command> <replaceable>ID</replaceable></term>
<listitem><para>Send a signal to one or more processes of the session. Use <listitem><para>Send a signal to one or more processes of the
<option>--kill-who=</option> to select which process to kill. Use <option>--signal=</option> to session. Use <option>--kill-who=</option> to select which
select the signal to send. If the argument is specified as empty string the signal is sent to the process to kill. Use <option>--signal=</option> to select the
session invoking the command.</para></listitem> signal to send.</para></listitem>
</varlistentry> </varlistentry>
</variablelist></refsect2> </variablelist></refsect2>
@ -184,17 +184,17 @@
<varlistentry> <varlistentry>
<term><command>terminate-user</command> <replaceable>USER</replaceable></term> <term><command>terminate-user</command> <replaceable>USER</replaceable></term>
<listitem><para>Terminates all sessions of a user. This kills all processes of all sessions of the <listitem><para>Terminates all sessions of a user. This kills
user and deallocates all runtime resources attached to the user. If the argument is specified as all processes of all sessions of the user and deallocates all
empty string the sessions of the user invoking the command are terminated.</para></listitem> runtime resources attached to the user.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><command>kill-user</command> <replaceable>USER</replaceable></term> <term><command>kill-user</command> <replaceable>USER</replaceable></term>
<listitem><para>Send a signal to all processes of a user. Use <option>--signal=</option> to select <listitem><para>Send a signal to all processes of a user. Use
the signal to send. If the argument is specified as empty string the signal is sent to the sessions <option>--signal=</option> to select the signal to send.
of the user invoking the command.</para></listitem> </para></listitem>
</varlistentry> </varlistentry>
</variablelist></refsect2> </variablelist></refsect2>

View File

@ -1630,12 +1630,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as OnFailure = ['...', ...]; readonly as OnFailure = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @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 = ['...', ...]; readonly as Triggers = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as TriggeredBy = ['...', ...]; readonly as TriggeredBy = ['...', ...];
@ -1644,14 +1638,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as ReloadPropagatedFrom = ['...', ...]; readonly as ReloadPropagatedFrom = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @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 = ['...', ...]; readonly as JoinsNamespaceOf = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as SliceOf = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as RequiresMountsFor = ['...', ...]; readonly as RequiresMountsFor = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Documentation = ['...', ...]; readonly as Documentation = ['...', ...];
@ -1706,8 +1694,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b DefaultDependencies = ...; readonly b DefaultDependencies = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s OnSuccesJobMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s OnFailureJobMode = '...'; readonly s OnFailureJobMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b IgnoreOnIsolate = ...; readonly b IgnoreOnIsolate = ...;
@ -1785,22 +1771,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<!--property ConsistsOf is not documented!--> <!--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 ReloadPropagatedFrom is not documented!-->
<!--property PropagatesStopTo is not documented!-->
<!--property StopPropagatedFrom is not documented!-->
<!--property JoinsNamespaceOf is not documented!--> <!--property JoinsNamespaceOf is not documented!-->
<!--property SliceOf is not documented!-->
<!--property FreezerState is not documented!--> <!--property FreezerState is not documented!-->
<!--property DropInPaths is not documented!--> <!--property DropInPaths is not documented!-->
@ -1815,8 +1789,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<!--property CanFreeze is not documented!--> <!--property CanFreeze is not documented!-->
<!--property OnSuccesJobMode is not documented!-->
<!--property OnFailureJobMode is not documented!--> <!--property OnFailureJobMode is not documented!-->
<!--property JobRunningTimeoutUSec is not documented!--> <!--property JobRunningTimeoutUSec is not documented!-->
@ -1929,12 +1901,6 @@ 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="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="Triggers"/>
<variablelist class="dbus-property" generated="True" extra-ref="TriggeredBy"/> <variablelist class="dbus-property" generated="True" extra-ref="TriggeredBy"/>
@ -1943,14 +1909,8 @@ 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="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="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="RequiresMountsFor"/>
<variablelist class="dbus-property" generated="True" extra-ref="Documentation"/> <variablelist class="dbus-property" generated="True" extra-ref="Documentation"/>
@ -2019,8 +1979,6 @@ 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="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="OnFailureJobMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="IgnoreOnIsolate"/> <variablelist class="dbus-property" generated="True" extra-ref="IgnoreOnIsolate"/>

View File

@ -176,11 +176,11 @@
<term><option>--tpm2-pcrs=</option><arg rep="repeat">PCR</arg></term> <term><option>--tpm2-pcrs=</option><arg rep="repeat">PCR</arg></term>
<listitem><para>Configures the TPM2 PCRs (Platform Configuration Registers) to bind the enrollment <listitem><para>Configures the TPM2 PCRs (Platform Configuration Registers) to bind the enrollment
requested via <option>--tpm2-device=</option> to. Takes a <literal>+</literal> separated list of requested via <option>--tpm2-device=</option> to. Takes a comma separated list of numeric PCR indexes
numeric PCR indexes in the range 0…23. If not used, defaults to PCR 7 only. If an empty string is in the range 0…23. If not used, defaults to PCR 7 only. If an empty string is specified, binds the
specified, binds the enrollment to no PCRs at all. PCRs allow binding the enrollment to specific enrollment to no PCRs at all. PCRs allow binding the enrollment to specific software versions and
software versions and system state, so that the enrolled unlocking key is only accessible (may be system state, so that the enrolled unlocking key is only accessible (may be "unsealed") if specific
"unsealed") if specific trusted software and/or configuration is used.</para></listitem> trusted software and/or configuration is used.</para></listitem>
<table> <table>
<title>Well-known PCR Definitions</title> <title>Well-known PCR Definitions</title>

View File

@ -708,24 +708,6 @@
</listitem> </listitem>
</varlistentry> </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> <varlistentry>
<term><varname>Conflicts=</varname></term> <term><varname>Conflicts=</varname></term>
@ -794,36 +776,24 @@
<varlistentry> <varlistentry>
<term><varname>OnFailure=</varname></term> <term><varname>OnFailure=</varname></term>
<listitem><para>A space-separated list of one or more units that are activated when this unit enters <listitem><para>A space-separated list of one or more units
the <literal>failed</literal> state. A service unit using <varname>Restart=</varname> enters the that are activated when this unit enters the
failed state only after the start limits are reached.</para></listitem> <literal>failed</literal> state. A service unit using
</varlistentry> <varname>Restart=</varname> enters the failed state only after
the start limits are reached.</para></listitem>
<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>
<varlistentry> <varlistentry>
<term><varname>PropagatesReloadTo=</varname></term> <term><varname>PropagatesReloadTo=</varname></term>
<term><varname>ReloadPropagatedFrom=</varname></term> <term><varname>ReloadPropagatedFrom=</varname></term>
<listitem><para>A space-separated list of one or more units to which reload requests from this unit <listitem><para>A space-separated list of one or more units
shall be propagated to, or units from which reload requests shall be propagated to this unit, where reload requests on this unit will be propagated to, or
respectively. Issuing a reload request on a unit will automatically also enqueue reload requests on reload requests on the other unit will be propagated to this
all units that are linked to it using these two settings.</para></listitem> unit, respectively. Issuing a reload request on a unit will
</varlistentry> automatically also enqueue a reload request on all units that
the reload request shall be propagated to via these two
<varlistentry> settings.</para></listitem>
<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>
<varlistentry> <varlistentry>

View File

@ -51,7 +51,6 @@ typedef struct {
#define _IDX_ITERATOR_FIRST (UINT_MAX - 1) #define _IDX_ITERATOR_FIRST (UINT_MAX - 1)
#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL }) #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 */ /* Macros for type checking */
#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \ #define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \

View File

@ -265,32 +265,23 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
[UNIT_WANTS] = "Wants", [UNIT_WANTS] = "Wants",
[UNIT_BINDS_TO] = "BindsTo", [UNIT_BINDS_TO] = "BindsTo",
[UNIT_PART_OF] = "PartOf", [UNIT_PART_OF] = "PartOf",
[UNIT_UPHOLDS] = "Upholds",
[UNIT_REQUIRED_BY] = "RequiredBy", [UNIT_REQUIRED_BY] = "RequiredBy",
[UNIT_REQUISITE_OF] = "RequisiteOf", [UNIT_REQUISITE_OF] = "RequisiteOf",
[UNIT_WANTED_BY] = "WantedBy", [UNIT_WANTED_BY] = "WantedBy",
[UNIT_BOUND_BY] = "BoundBy", [UNIT_BOUND_BY] = "BoundBy",
[UNIT_UPHELD_BY] = "UpheldBy",
[UNIT_CONSISTS_OF] = "ConsistsOf", [UNIT_CONSISTS_OF] = "ConsistsOf",
[UNIT_CONFLICTS] = "Conflicts", [UNIT_CONFLICTS] = "Conflicts",
[UNIT_CONFLICTED_BY] = "ConflictedBy", [UNIT_CONFLICTED_BY] = "ConflictedBy",
[UNIT_BEFORE] = "Before", [UNIT_BEFORE] = "Before",
[UNIT_AFTER] = "After", [UNIT_AFTER] = "After",
[UNIT_ON_SUCCESS] = "OnSuccess",
[UNIT_ON_SUCCESS_OF] = "OnSuccessOf",
[UNIT_ON_FAILURE] = "OnFailure", [UNIT_ON_FAILURE] = "OnFailure",
[UNIT_ON_FAILURE_OF] = "OnFailureOf",
[UNIT_TRIGGERS] = "Triggers", [UNIT_TRIGGERS] = "Triggers",
[UNIT_TRIGGERED_BY] = "TriggeredBy", [UNIT_TRIGGERED_BY] = "TriggeredBy",
[UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo", [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo",
[UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom", [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
[UNIT_PROPAGATES_STOP_TO] = "PropagatesStopTo",
[UNIT_STOP_PROPAGATED_FROM] = "StopPropagatedFrom",
[UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf", [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
[UNIT_REFERENCES] = "References", [UNIT_REFERENCES] = "References",
[UNIT_REFERENCED_BY] = "ReferencedBy", [UNIT_REFERENCED_BY] = "ReferencedBy",
[UNIT_IN_SLICE] = "InSlice",
[UNIT_SLICE_OF] = "SliceOf",
}; };
DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency); DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);

View File

@ -211,7 +211,6 @@ typedef enum UnitDependency {
UNIT_WANTS, UNIT_WANTS,
UNIT_BINDS_TO, UNIT_BINDS_TO,
UNIT_PART_OF, UNIT_PART_OF,
UNIT_UPHOLDS,
/* Inverse of the above */ /* Inverse of the above */
UNIT_REQUIRED_BY, /* inverse of 'requires' is 'required_by' */ UNIT_REQUIRED_BY, /* inverse of 'requires' is 'required_by' */
@ -219,7 +218,6 @@ typedef enum UnitDependency {
UNIT_WANTED_BY, /* inverse of 'wants' */ UNIT_WANTED_BY, /* inverse of 'wants' */
UNIT_BOUND_BY, /* inverse of 'binds_to' */ UNIT_BOUND_BY, /* inverse of 'binds_to' */
UNIT_CONSISTS_OF, /* inverse of 'part_of' */ UNIT_CONSISTS_OF, /* inverse of 'part_of' */
UNIT_UPHELD_BY, /* inverse of 'uphold' */
/* Negative dependencies */ /* Negative dependencies */
UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicted_by' */ UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicted_by' */
@ -229,11 +227,8 @@ typedef enum UnitDependency {
UNIT_BEFORE, /* inverse of 'before' is 'after' and vice versa */ UNIT_BEFORE, /* inverse of 'before' is 'after' and vice versa */
UNIT_AFTER, UNIT_AFTER,
/* OnSuccess= + OnFailure= */ /* On Failure */
UNIT_ON_SUCCESS,
UNIT_ON_SUCCESS_OF,
UNIT_ON_FAILURE, UNIT_ON_FAILURE,
UNIT_ON_FAILURE_OF,
/* Triggers (i.e. a socket triggers a service) */ /* Triggers (i.e. a socket triggers a service) */
UNIT_TRIGGERS, UNIT_TRIGGERS,
@ -243,10 +238,6 @@ typedef enum UnitDependency {
UNIT_PROPAGATES_RELOAD_TO, UNIT_PROPAGATES_RELOAD_TO,
UNIT_RELOAD_PROPAGATED_FROM, UNIT_RELOAD_PROPAGATED_FROM,
/* Propagate stops */
UNIT_PROPAGATES_STOP_TO,
UNIT_STOP_PROPAGATED_FROM,
/* Joins namespace of */ /* Joins namespace of */
UNIT_JOINS_NAMESPACE_OF, UNIT_JOINS_NAMESPACE_OF,
@ -254,10 +245,6 @@ typedef enum UnitDependency {
UNIT_REFERENCES, /* Inverse of 'references' is 'referenced_by' */ UNIT_REFERENCES, /* Inverse of 'references' is 'referenced_by' */
UNIT_REFERENCED_BY, UNIT_REFERENCED_BY,
/* Slice= */
UNIT_IN_SLICE,
UNIT_SLICE_OF,
_UNIT_DEPENDENCY_MAX, _UNIT_DEPENDENCY_MAX,
_UNIT_DEPENDENCY_INVALID = -EINVAL, _UNIT_DEPENDENCY_INVALID = -EINVAL,
} UnitDependency; } UnitDependency;

View File

@ -422,7 +422,7 @@ static int bpf_firewall_prepare_access_maps(
assert(ret_ipv6_map_fd); assert(ret_ipv6_map_fd);
assert(ret_has_any); assert(ret_has_any);
for (p = u; p; p = UNIT_GET_SLICE(p)) { for (p = u; p; p = UNIT_DEREF(p->slice)) {
CGroupContext *cc; CGroupContext *cc;
cc = unit_get_cgroup_context(p); cc = unit_get_cgroup_context(p);
@ -463,7 +463,7 @@ static int bpf_firewall_prepare_access_maps(
return ipv6_map_fd; return ipv6_map_fd;
} }
for (p = u; p; p = UNIT_GET_SLICE(p)) { for (p = u; p; p = UNIT_DEREF(p->slice)) {
CGroupContext *cc; CGroupContext *cc;
cc = unit_get_cgroup_context(p); cc = unit_get_cgroup_context(p);

View File

@ -680,7 +680,7 @@ int cgroup_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const
if (c && c->entry##_set) \ if (c && c->entry##_set) \
return c->entry; \ return c->entry; \
\ \
while ((u = UNIT_GET_SLICE(u))) { \ while ((u = UNIT_DEREF(u->slice))) { \
c = unit_get_cgroup_context(u); \ c = unit_get_cgroup_context(u); \
if (c && c->default_##entry##_set) \ if (c && c->default_##entry##_set) \
return c->default_##entry; \ return c->default_##entry; \
@ -1549,7 +1549,7 @@ static bool unit_get_needs_bpf_firewall(Unit *u) {
return true; return true;
/* If any parent slice has an IP access list defined, it applies too */ /* If any parent slice has an IP access list defined, it applies too */
for (p = UNIT_GET_SLICE(u); p; p = UNIT_GET_SLICE(p)) { for (p = UNIT_DEREF(u->slice); p; p = UNIT_DEREF(p->slice)) {
c = unit_get_cgroup_context(p); c = unit_get_cgroup_context(p);
if (!c) if (!c)
return false; return false;
@ -1700,10 +1700,12 @@ CGroupMask unit_get_members_mask(Unit *u) {
u->cgroup_members_mask = 0; u->cgroup_members_mask = 0;
if (u->type == UNIT_SLICE) { if (u->type == UNIT_SLICE) {
void *v;
Unit *member; Unit *member;
UNIT_FOREACH_DEPENDENCY(member, u, UNIT_ATOM_SLICE_OF) HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE])
u->cgroup_members_mask |= unit_get_subtree_mask(member); /* note that this calls ourselves again, for the children */ if (UNIT_DEREF(member->slice) == u)
u->cgroup_members_mask |= unit_get_subtree_mask(member); /* note that this calls ourselves again, for the children */
} }
u->cgroup_members_mask_valid = true; u->cgroup_members_mask_valid = true;
@ -1711,16 +1713,14 @@ CGroupMask unit_get_members_mask(Unit *u) {
} }
CGroupMask unit_get_siblings_mask(Unit *u) { CGroupMask unit_get_siblings_mask(Unit *u) {
Unit *slice;
assert(u); assert(u);
/* Returns the mask of controllers all of the unit's siblings /* Returns the mask of controllers all of the unit's siblings
* require, i.e. the members mask of the unit's parent slice * require, i.e. the members mask of the unit's parent slice
* if there is one. */ * if there is one. */
slice = UNIT_GET_SLICE(u); if (UNIT_ISSET(u->slice))
if (slice) return unit_get_members_mask(UNIT_DEREF(u->slice));
return unit_get_members_mask(slice);
return unit_get_subtree_mask(u); /* we are the top-level slice */ return unit_get_subtree_mask(u); /* we are the top-level slice */
} }
@ -1737,7 +1737,6 @@ static CGroupMask unit_get_disable_mask(Unit *u) {
CGroupMask unit_get_ancestor_disable_mask(Unit *u) { CGroupMask unit_get_ancestor_disable_mask(Unit *u) {
CGroupMask mask; CGroupMask mask;
Unit *slice;
assert(u); assert(u);
mask = unit_get_disable_mask(u); mask = unit_get_disable_mask(u);
@ -1745,9 +1744,8 @@ CGroupMask unit_get_ancestor_disable_mask(Unit *u) {
/* Returns the mask of controllers which are marked as forcibly /* Returns the mask of controllers which are marked as forcibly
* disabled in any ancestor unit or the unit in question. */ * disabled in any ancestor unit or the unit in question. */
slice = UNIT_GET_SLICE(u); if (UNIT_ISSET(u->slice))
if (slice) mask |= unit_get_ancestor_disable_mask(UNIT_DEREF(u->slice));
mask |= unit_get_ancestor_disable_mask(slice);
return mask; return mask;
} }
@ -1789,16 +1787,13 @@ CGroupMask unit_get_enable_mask(Unit *u) {
} }
void unit_invalidate_cgroup_members_masks(Unit *u) { void unit_invalidate_cgroup_members_masks(Unit *u) {
Unit *slice;
assert(u); assert(u);
/* Recurse invalidate the member masks cache all the way up the tree */ /* Recurse invalidate the member masks cache all the way up the tree */
u->cgroup_members_mask_valid = false; u->cgroup_members_mask_valid = false;
slice = UNIT_GET_SLICE(u); if (UNIT_ISSET(u->slice))
if (slice) unit_invalidate_cgroup_members_masks(UNIT_DEREF(u->slice));
unit_invalidate_cgroup_members_masks(slice);
} }
const char *unit_get_realized_cgroup_path(Unit *u, CGroupMask mask) { const char *unit_get_realized_cgroup_path(Unit *u, CGroupMask mask) {
@ -1812,7 +1807,7 @@ const char *unit_get_realized_cgroup_path(Unit *u, CGroupMask mask) {
FLAGS_SET(u->cgroup_realized_mask, mask)) FLAGS_SET(u->cgroup_realized_mask, mask))
return u->cgroup_path; return u->cgroup_path;
u = UNIT_GET_SLICE(u); u = UNIT_DEREF(u->slice);
} }
return NULL; return NULL;
@ -1826,8 +1821,7 @@ static const char *migrate_callback(CGroupMask mask, void *userdata) {
} }
char *unit_default_cgroup_path(const Unit *u) { char *unit_default_cgroup_path(const Unit *u) {
_cleanup_free_ char *escaped = NULL, *slice_path = NULL; _cleanup_free_ char *escaped = NULL, *slice = NULL;
Unit *slice;
int r; int r;
assert(u); assert(u);
@ -1835,9 +1829,8 @@ char *unit_default_cgroup_path(const Unit *u) {
if (unit_has_name(u, SPECIAL_ROOT_SLICE)) if (unit_has_name(u, SPECIAL_ROOT_SLICE))
return strdup(u->manager->cgroup_root); return strdup(u->manager->cgroup_root);
slice = UNIT_GET_SLICE(u); if (UNIT_ISSET(u->slice) && !unit_has_name(UNIT_DEREF(u->slice), SPECIAL_ROOT_SLICE)) {
if (slice && !unit_has_name(slice, SPECIAL_ROOT_SLICE)) { r = cg_slice_to_path(UNIT_DEREF(u->slice)->id, &slice);
r = cg_slice_to_path(slice->id, &slice_path);
if (r < 0) if (r < 0)
return NULL; return NULL;
} }
@ -1846,7 +1839,7 @@ char *unit_default_cgroup_path(const Unit *u) {
if (!escaped) if (!escaped)
return NULL; return NULL;
return path_join(empty_to_root(u->manager->cgroup_root), slice_path, escaped); return path_join(empty_to_root(u->manager->cgroup_root), slice, escaped);
} }
int unit_set_cgroup_path(Unit *u, const char *path) { int unit_set_cgroup_path(Unit *u, const char *path) {
@ -2320,16 +2313,14 @@ static void unit_remove_from_cgroup_realize_queue(Unit *u) {
* hierarchy downwards to the unit in question. */ * hierarchy downwards to the unit in question. */
static int unit_realize_cgroup_now_enable(Unit *u, ManagerState state) { static int unit_realize_cgroup_now_enable(Unit *u, ManagerState state) {
CGroupMask target_mask, enable_mask, new_target_mask, new_enable_mask; CGroupMask target_mask, enable_mask, new_target_mask, new_enable_mask;
Unit *slice;
int r; int r;
assert(u); assert(u);
/* First go deal with this unit's parent, or we won't be able to enable /* First go deal with this unit's parent, or we won't be able to enable
* any new controllers at this layer. */ * any new controllers at this layer. */
slice = UNIT_GET_SLICE(u); if (UNIT_ISSET(u->slice)) {
if (slice) { r = unit_realize_cgroup_now_enable(UNIT_DEREF(u->slice), state);
r = unit_realize_cgroup_now_enable(slice, state);
if (r < 0) if (r < 0)
return r; return r;
} }
@ -2352,29 +2343,36 @@ static int unit_realize_cgroup_now_enable(Unit *u, ManagerState state) {
* hierarchy upwards to the unit in question. */ * hierarchy upwards to the unit in question. */
static int unit_realize_cgroup_now_disable(Unit *u, ManagerState state) { static int unit_realize_cgroup_now_disable(Unit *u, ManagerState state) {
Unit *m; Unit *m;
void *v;
assert(u); assert(u);
if (u->type != UNIT_SLICE) if (u->type != UNIT_SLICE)
return 0; return 0;
UNIT_FOREACH_DEPENDENCY(m, u, UNIT_ATOM_SLICE_OF) { HASHMAP_FOREACH_KEY(v, m, u->dependencies[UNIT_BEFORE]) {
CGroupMask target_mask, enable_mask, new_target_mask, new_enable_mask; CGroupMask target_mask, enable_mask, new_target_mask, new_enable_mask;
int r; int r;
/* The cgroup for this unit might not actually be fully realised yet, in which case it isn't if (UNIT_DEREF(m->slice) != u)
* holding any controllers open anyway. */ continue;
/* 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) if (!m->cgroup_realized)
continue; 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) if (m->type == UNIT_SLICE)
(void) unit_realize_cgroup_now_disable(m, state); (void) unit_realize_cgroup_now_disable(m, state);
target_mask = unit_get_target_mask(m); target_mask = unit_get_target_mask(m);
enable_mask = unit_get_enable_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)) if (unit_has_mask_disables_realized(m, target_mask, enable_mask))
continue; continue;
@ -2435,7 +2433,6 @@ static int unit_realize_cgroup_now_disable(Unit *u, ManagerState state) {
* Returns 0 on success and < 0 on failure. */ * Returns 0 on success and < 0 on failure. */
static int unit_realize_cgroup_now(Unit *u, ManagerState state) { static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
CGroupMask target_mask, enable_mask; CGroupMask target_mask, enable_mask;
Unit *slice;
int r; int r;
assert(u); assert(u);
@ -2454,9 +2451,8 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
return r; return r;
/* Enable controllers above us, if there are any */ /* Enable controllers above us, if there are any */
slice = UNIT_GET_SLICE(u); if (UNIT_ISSET(u->slice)) {
if (slice) { r = unit_realize_cgroup_now_enable(UNIT_DEREF(u->slice), state);
r = unit_realize_cgroup_now_enable(slice, state);
if (r < 0) if (r < 0)
return r; return r;
} }
@ -2519,11 +2515,15 @@ void unit_add_family_to_cgroup_realize_queue(Unit *u) {
do { do {
Unit *m; Unit *m;
void *v;
/* Children of u likely changed when we're called */ /* Children of u likely changed when we're called */
u->cgroup_members_mask_valid = false; u->cgroup_members_mask_valid = false;
UNIT_FOREACH_DEPENDENCY(m, u, UNIT_ATOM_SLICE_OF) { 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;
/* No point in doing cgroup application for units without active processes. */ /* No point in doing cgroup application for units without active processes. */
if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(m))) 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) if (!m->cgroup_realized)
continue; continue;
/* If the unit doesn't need any new controllers and has current ones /* If the unit doesn't need any new controllers and has current ones realized, it
* realized, it doesn't need any changes. */ * doesn't need any changes. */
if (unit_has_mask_realized(m, if (unit_has_mask_realized(m,
unit_get_target_mask(m), unit_get_target_mask(m),
unit_get_enable_mask(m))) unit_get_enable_mask(m)))
@ -2546,14 +2546,10 @@ void unit_add_family_to_cgroup_realize_queue(Unit *u) {
/* Parent comes after children */ /* Parent comes after children */
unit_add_to_cgroup_realize_queue(u); 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) { int unit_realize_cgroup(Unit *u) {
Unit *slice;
assert(u); assert(u);
if (!UNIT_HAS_CGROUP_CONTEXT(u)) if (!UNIT_HAS_CGROUP_CONTEXT(u))
@ -2568,9 +2564,8 @@ int unit_realize_cgroup(Unit *u) {
* This call will defer work on the siblings and derealized ancestors to the next event loop * 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). */ * iteration and synchronously creates the parent cgroups (unit_realize_cgroup_now). */
slice = UNIT_GET_SLICE(u); if (UNIT_ISSET(u->slice))
if (slice) unit_add_family_to_cgroup_realize_queue(UNIT_DEREF(u->slice));
unit_add_family_to_cgroup_realize_queue(slice);
/* And realize this one now (and apply the values) */ /* And realize this one now (and apply the values) */
return unit_realize_cgroup_now(u, manager_state(u->manager)); return unit_realize_cgroup_now(u, manager_state(u->manager));
@ -3787,9 +3782,11 @@ void unit_invalidate_cgroup_bpf(Unit *u) {
* list of our children includes our own. */ * list of our children includes our own. */
if (u->type == UNIT_SLICE) { if (u->type == UNIT_SLICE) {
Unit *member; Unit *member;
void *v;
UNIT_FOREACH_DEPENDENCY(member, u, UNIT_ATOM_SLICE_OF) HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE])
unit_invalidate_cgroup_bpf(member); if (UNIT_DEREF(member->slice) == u)
unit_invalidate_cgroup_bpf(member);
} }
} }

View File

@ -155,27 +155,21 @@ static int property_get_dependencies(
void *userdata, void *userdata,
sd_bus_error *error) { sd_bus_error *error) {
Unit *u = userdata, *other; Hashmap **h = userdata;
UnitDependency d; Unit *u;
Hashmap *deps;
void *v; void *v;
int r; int r;
assert(bus); assert(bus);
assert(reply); assert(reply);
assert(u); assert(h);
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"); r = sd_bus_message_open_container(reply, 'a', "s");
if (r < 0) if (r < 0)
return r; return r;
HASHMAP_FOREACH_KEY(v, other, deps) { HASHMAP_FOREACH_KEY(v, u, *h) {
r = sd_bus_message_append(reply, "s", other->id); r = sd_bus_message_append(reply, "s", u->id);
if (r < 0) if (r < 0)
return r; return r;
} }
@ -850,32 +844,26 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Unit, id), SD_BUS_VTABLE_PROPERTY_CONST), 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("Names", "as", property_get_names, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Following", "s", property_get_following, 0, 0), SD_BUS_PROPERTY("Following", "s", property_get_following, 0, 0),
SD_BUS_PROPERTY("Requires", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST), 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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, 0, 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("OnFailureOf", "as", property_get_dependencies, 0, 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("OnSuccess", "as", property_get_dependencies, 0, 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("OnSuccessOf", "as", property_get_dependencies, 0, 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("Triggers", "as", property_get_dependencies, 0, 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("TriggeredBy", "as", property_get_dependencies, 0, 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("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("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("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), SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@ -905,7 +893,6 @@ 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("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("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("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("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("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), SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@ -2129,9 +2116,6 @@ static int bus_unit_set_transient_property(
if (streq(name, "DefaultDependencies")) if (streq(name, "DefaultDependencies"))
return bus_set_transient_bool(u, name, &u->default_dependencies, message, flags, error); 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")) if (streq(name, "OnFailureJobMode"))
return bus_set_transient_job_mode(u, name, &u->on_failure_job_mode, message, flags, error); return bus_set_transient_job_mode(u, name, &u->on_failure_job_mode, message, flags, error);
@ -2247,7 +2231,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); 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)) { if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
r = unit_set_slice(u, slice, UNIT_DEPENDENCY_FILE); r = unit_set_slice(u, slice);
if (r < 0) if (r < 0)
return r; return r;
@ -2304,16 +2288,12 @@ static int bus_unit_set_transient_property(
UNIT_WANTS, UNIT_WANTS,
UNIT_BINDS_TO, UNIT_BINDS_TO,
UNIT_PART_OF, UNIT_PART_OF,
UNIT_UPHOLDS,
UNIT_CONFLICTS, UNIT_CONFLICTS,
UNIT_BEFORE, UNIT_BEFORE,
UNIT_AFTER, UNIT_AFTER,
UNIT_ON_SUCCESS,
UNIT_ON_FAILURE, UNIT_ON_FAILURE,
UNIT_PROPAGATES_RELOAD_TO, UNIT_PROPAGATES_RELOAD_TO,
UNIT_RELOAD_PROPAGATED_FROM, UNIT_RELOAD_PROPAGATED_FROM,
UNIT_PROPAGATES_STOP_TO,
UNIT_STOP_PROPAGATED_FROM,
UNIT_JOINS_NAMESPACE_OF)) 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)); return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Dependency type %s may not be created transiently.", unit_dependency_to_string(d));

View File

@ -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) */ /* Let's upgrade Requires= to BindsTo= on us. (Used when SYSTEMD_MOUNT_DEVICE_BOUND is set) */
HASHMAP_FOREACH_KEY(v, other, unit_get_dependencies(u, UNIT_REQUIRED_BY)) { HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUIRED_BY]) {
if (other->type != UNIT_MOUNT) if (other->type != UNIT_MOUNT)
continue; continue;

View File

@ -455,6 +455,7 @@ int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u) {
static bool job_is_runnable(Job *j) { static bool job_is_runnable(Job *j) {
Unit *other; Unit *other;
void *v;
assert(j); assert(j);
assert(j->installed); assert(j->installed);
@ -476,16 +477,16 @@ static bool job_is_runnable(Job *j) {
if (j->type == JOB_NOP) if (j->type == JOB_NOP)
return true; return true;
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_AFTER) HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER])
if (other->job && job_compare(j, other->job, UNIT_ATOM_AFTER) > 0) { if (other->job && job_compare(j, other->job, UNIT_AFTER) > 0) {
log_unit_debug(j->unit, log_unit_debug(j->unit,
"starting held back, waiting for: %s", "starting held back, waiting for: %s",
other->id); other->id);
return false; return false;
} }
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_BEFORE) HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE])
if (other->job && job_compare(j, other->job, UNIT_ATOM_BEFORE) > 0) { if (other->job && job_compare(j, other->job, UNIT_BEFORE) > 0) {
log_unit_debug(j->unit, log_unit_debug(j->unit,
"stopping held back, waiting for: %s", "stopping held back, waiting for: %s",
other->id); other->id);
@ -950,12 +951,13 @@ static void job_emit_done_status_message(Unit *u, uint32_t job_id, JobType t, Jo
job_print_done_status_message(u, t, result); job_print_done_status_message(u, t, result);
} }
static void job_fail_dependencies(Unit *u, UnitDependencyAtom match_atom) { static void job_fail_dependencies(Unit *u, UnitDependency d) {
Unit *other; Unit *other;
void *v;
assert(u); assert(u);
UNIT_FOREACH_DEPENDENCY(other, u, match_atom) { HASHMAP_FOREACH_KEY(v, other, u->dependencies[d]) {
Job *j = other->job; Job *j = other->job;
if (!j) if (!j)
@ -968,8 +970,10 @@ static void job_fail_dependencies(Unit *u, UnitDependencyAtom match_atom) {
} }
int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already) { int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already) {
Unit *u, *other; Unit *u;
Unit *other;
JobType t; JobType t;
void *v;
assert(j); assert(j);
assert(j->installed); assert(j->installed);
@ -1008,36 +1012,43 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr
/* Fail depending jobs on failure */ /* Fail depending jobs on failure */
if (result != JOB_DONE && recursive) { if (result != JOB_DONE && recursive) {
if (IN_SET(t, JOB_START, JOB_VERIFY_ACTIVE)) if (IN_SET(t, JOB_START, JOB_VERIFY_ACTIVE)) {
job_fail_dependencies(u, UNIT_ATOM_PROPAGATE_START_FAILURE); job_fail_dependencies(u, UNIT_REQUIRED_BY);
else if (t == JOB_STOP) job_fail_dependencies(u, UNIT_REQUISITE_OF);
job_fail_dependencies(u, UNIT_ATOM_PROPAGATE_STOP_FAILURE); job_fail_dependencies(u, UNIT_BOUND_BY);
} else if (t == JOB_STOP)
job_fail_dependencies(u, UNIT_CONFLICTED_BY);
} }
/* A special check to make sure we take down anything RequisiteOf= if we aren't active. This is when /* A special check to make sure we take down anything RequisiteOf if we
* the verify-active job merges with a satisfying job type, and then loses it's invalidation effect, * aren't active. This is when the verify-active job merges with a
* as the result there is JOB_DONE for the start job we merged into, while we should be failing the * satisfying job type, and then loses it's invalidation effect, as the
* depending job if the said unit isn't in fact active. Oneshots are an example of this, where going * result there is JOB_DONE for the start job we merged into, while we
* directly from activating to inactive is success. * 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 * This happens when you use ConditionXYZ= in a unit too, since in that
* the JOB_DONE result, but the unit never really becomes active. Note that such a case still * case the job completes with the JOB_DONE result, but the unit never
* involves merging: * 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 * A start job waits for something else, and a verify-active comes in
* job. Then, later, when it becomes runnable, it finishes with JOB_DONE result as execution on * and merges in the installed job. Then, later, when it becomes
* conditions not being met is skipped, breaking our dependency semantics. * 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 * Also, depending on if start job waits or not, the merging may or may
* job may trigger after it finishes), so you get undeterministic results without this check. * not happen (the verify-active job may trigger after it finishes), so
* you get undeterministic results without this check.
*/ */
if (result == JOB_DONE && recursive && if (result == JOB_DONE && recursive && !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) {
IN_SET(t, JOB_START, JOB_RELOAD) && if (IN_SET(t, JOB_START, JOB_RELOAD))
!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) job_fail_dependencies(u, UNIT_REQUISITE_OF);
job_fail_dependencies(u, UNIT_ATOM_PROPAGATE_INACTIVE_START_AS_FAILURE); }
/* Trigger OnFailure dependencies that are not generated by
/* Trigger OnFailure= dependencies that are not generated by the unit itself. We don't treat * the unit itself. We don't treat JOB_CANCELED as failure in
* JOB_CANCELED as failure in this context. And JOB_FAILURE is already handled by the unit itself. */ * this context. And JOB_FAILURE is already handled by the
* unit itself. */
if (IN_SET(result, JOB_TIMEOUT, JOB_DEPENDENCY)) { if (IN_SET(result, JOB_TIMEOUT, JOB_DEPENDENCY)) {
log_unit_struct(u, LOG_NOTICE, log_unit_struct(u, LOG_NOTICE,
"JOB_TYPE=%s", job_type_to_string(t), "JOB_TYPE=%s", job_type_to_string(t),
@ -1047,19 +1058,19 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr
job_type_to_string(t), job_type_to_string(t),
job_result_to_string(result))); job_result_to_string(result)));
unit_start_on_failure(u, "OnFailure=", UNIT_ATOM_ON_FAILURE, u->on_failure_job_mode); unit_start_on_failure(u);
} }
unit_trigger_notify(u); unit_trigger_notify(u);
finish: finish:
/* Try to start the next jobs that can be started */ /* Try to start the next jobs that can be started */
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_AFTER) HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_AFTER])
if (other->job) { if (other->job) {
job_add_to_run_queue(other->job); job_add_to_run_queue(other->job);
job_add_to_gc_queue(other->job); job_add_to_gc_queue(other->job);
} }
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_BEFORE) HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BEFORE])
if (other->job) { if (other->job) {
job_add_to_run_queue(other->job); job_add_to_run_queue(other->job);
job_add_to_gc_queue(other->job); job_add_to_gc_queue(other->job);
@ -1409,6 +1420,7 @@ int job_get_timeout(Job *j, usec_t *timeout) {
bool job_may_gc(Job *j) { bool job_may_gc(Job *j) {
Unit *other; Unit *other;
void *v;
assert(j); assert(j);
@ -1437,12 +1449,12 @@ bool job_may_gc(Job *j) {
return false; return false;
/* The logic is inverse to job_is_runnable, we cannot GC as long as we block any job. */ /* The logic is inverse to job_is_runnable, we cannot GC as long as we block any job. */
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_BEFORE) HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE])
if (other->job && job_compare(j, other->job, UNIT_ATOM_BEFORE) < 0) if (other->job && job_compare(j, other->job, UNIT_BEFORE) < 0)
return false; return false;
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_AFTER) HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER])
if (other->job && job_compare(j, other->job, UNIT_ATOM_AFTER) < 0) if (other->job && job_compare(j, other->job, UNIT_AFTER) < 0)
return false; return false;
return true; return true;
@ -1488,6 +1500,7 @@ int job_get_before(Job *j, Job*** ret) {
_cleanup_free_ Job** list = NULL; _cleanup_free_ Job** list = NULL;
Unit *other = NULL; Unit *other = NULL;
size_t n = 0; size_t n = 0;
void *v;
/* Returns a list of all pending jobs that need to finish before this job may be started. */ /* Returns a list of all pending jobs that need to finish before this job may be started. */
@ -1499,10 +1512,10 @@ int job_get_before(Job *j, Job*** ret) {
return 0; return 0;
} }
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_AFTER) { HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER]) {
if (!other->job) if (!other->job)
continue; continue;
if (job_compare(j, other->job, UNIT_ATOM_AFTER) <= 0) if (job_compare(j, other->job, UNIT_AFTER) <= 0)
continue; continue;
if (!GREEDY_REALLOC(list, n+1)) if (!GREEDY_REALLOC(list, n+1))
@ -1510,10 +1523,10 @@ int job_get_before(Job *j, Job*** ret) {
list[n++] = other->job; list[n++] = other->job;
} }
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_BEFORE) { HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE]) {
if (!other->job) if (!other->job)
continue; continue;
if (job_compare(j, other->job, UNIT_ATOM_BEFORE) <= 0) if (job_compare(j, other->job, UNIT_BEFORE) <= 0)
continue; continue;
if (!GREEDY_REALLOC(list, n+1)) if (!GREEDY_REALLOC(list, n+1))
@ -1532,20 +1545,21 @@ int job_get_after(Job *j, Job*** ret) {
_cleanup_free_ Job** list = NULL; _cleanup_free_ Job** list = NULL;
Unit *other = NULL; Unit *other = NULL;
size_t n = 0; size_t n = 0;
void *v;
assert(j); assert(j);
assert(ret); assert(ret);
/* Returns a list of all pending jobs that are waiting for this job to finish. */ /* Returns a list of all pending jobs that are waiting for this job to finish. */
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_BEFORE) { HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE]) {
if (!other->job) if (!other->job)
continue; continue;
if (other->job->ignore_order) if (other->job->ignore_order)
continue; continue;
if (job_compare(j, other->job, UNIT_ATOM_BEFORE) >= 0) if (job_compare(j, other->job, UNIT_BEFORE) >= 0)
continue; continue;
if (!GREEDY_REALLOC(list, n+1)) if (!GREEDY_REALLOC(list, n+1))
@ -1553,14 +1567,14 @@ int job_get_after(Job *j, Job*** ret) {
list[n++] = other->job; list[n++] = other->job;
} }
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_AFTER) { HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER]) {
if (!other->job) if (!other->job)
continue; continue;
if (other->job->ignore_order) if (other->job->ignore_order)
continue; continue;
if (job_compare(j, other->job, UNIT_ATOM_AFTER) >= 0) if (job_compare(j, other->job, UNIT_AFTER) >= 0)
continue; continue;
if (!GREEDY_REALLOC(list, n+1)) if (!GREEDY_REALLOC(list, n+1))
@ -1652,14 +1666,13 @@ const char* job_type_to_access_method(JobType t) {
* stop a + start b 1st step stop a, 2nd step start b * stop a + start b 1st step stop a, 2nd step start b
* stop a + stop b 1st step stop b, 2nd step stop a * 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, UnitDependencyAtom assume_dep) { int job_compare(Job *a, Job *b, UnitDependency assume_dep) {
assert(a);
assert(b);
assert(a->type < _JOB_TYPE_MAX_IN_TRANSACTION); assert(a->type < _JOB_TYPE_MAX_IN_TRANSACTION);
assert(b->type < _JOB_TYPE_MAX_IN_TRANSACTION); assert(b->type < _JOB_TYPE_MAX_IN_TRANSACTION);
assert(IN_SET(assume_dep, UNIT_ATOM_AFTER, UNIT_ATOM_BEFORE)); assert(IN_SET(assume_dep, UNIT_AFTER, UNIT_BEFORE));
/* Trivial cases first */ /* Trivial cases first */
if (a->type == JOB_NOP || b->type == JOB_NOP) if (a->type == JOB_NOP || b->type == JOB_NOP)
@ -1668,11 +1681,12 @@ int job_compare(Job *a, Job *b, UnitDependencyAtom assume_dep) {
if (a->ignore_order || b->ignore_order) if (a->ignore_order || b->ignore_order)
return 0; return 0;
if (assume_dep == UNIT_ATOM_AFTER) if (assume_dep == UNIT_AFTER)
return -job_compare(b, a, UNIT_ATOM_BEFORE); return -job_compare(b, a, UNIT_BEFORE);
/* Let's make it simple, JOB_STOP goes always first (in case both ua and ub stop, then ub's stop goes /* Let's make it simple, JOB_STOP goes always first (in case both ua and ub stop,
* first anyway). JOB_RESTART is JOB_STOP in disguise (before it is patched to JOB_START). */ * 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)) if (IN_SET(b->type, JOB_STOP, JOB_RESTART))
return 1; return 1;
else else

View File

@ -6,9 +6,7 @@
#include "sd-event.h" #include "sd-event.h"
#include "list.h" #include "list.h"
#include "unit-dependency-atom.h"
#include "unit-name.h" #include "unit-name.h"
#include "unit.h"
typedef struct Job Job; typedef struct Job Job;
typedef struct JobDependency JobDependency; typedef struct JobDependency JobDependency;
@ -242,4 +240,4 @@ JobResult job_result_from_string(const char *s) _pure_;
const char* job_type_to_access_method(JobType t); const char* job_type_to_access_method(JobType t);
int job_compare(Job *a, Job *b, UnitDependencyAtom assume_dep); int job_compare(Job *a, Job *b, UnitDependency assume_dep);

View File

@ -261,18 +261,14 @@ Unit.Requisite, config_parse_unit_deps,
Unit.Wants, config_parse_unit_deps, UNIT_WANTS, 0 Unit.Wants, config_parse_unit_deps, UNIT_WANTS, 0
Unit.BindsTo, config_parse_unit_deps, UNIT_BINDS_TO, 0 Unit.BindsTo, config_parse_unit_deps, UNIT_BINDS_TO, 0
Unit.BindTo, 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.Conflicts, config_parse_unit_deps, UNIT_CONFLICTS, 0
Unit.Before, config_parse_unit_deps, UNIT_BEFORE, 0 Unit.Before, config_parse_unit_deps, UNIT_BEFORE, 0
Unit.After, config_parse_unit_deps, UNIT_AFTER, 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.OnFailure, config_parse_unit_deps, UNIT_ON_FAILURE, 0
Unit.PropagatesReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0 Unit.PropagatesReloadTo, config_parse_unit_deps, UNIT_PROPAGATES_RELOAD_TO, 0
Unit.PropagateReloadTo, 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.ReloadPropagatedFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0
Unit.PropagateReloadFrom, 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.PartOf, config_parse_unit_deps, UNIT_PART_OF, 0
Unit.JoinsNamespaceOf, config_parse_unit_deps, UNIT_JOINS_NAMESPACE_OF, 0 Unit.JoinsNamespaceOf, config_parse_unit_deps, UNIT_JOINS_NAMESPACE_OF, 0
Unit.RequiresOverridable, config_parse_obsolete_unit_deps, UNIT_REQUIRES, 0 Unit.RequiresOverridable, config_parse_obsolete_unit_deps, UNIT_REQUIRES, 0
@ -283,7 +279,6 @@ Unit.RefuseManualStart, config_parse_bool,
Unit.RefuseManualStop, config_parse_bool, 0, offsetof(Unit, refuse_manual_stop) Unit.RefuseManualStop, config_parse_bool, 0, offsetof(Unit, refuse_manual_stop)
Unit.AllowIsolate, config_parse_bool, 0, offsetof(Unit, allow_isolate) Unit.AllowIsolate, config_parse_bool, 0, offsetof(Unit, allow_isolate)
Unit.DefaultDependencies, config_parse_bool, 0, offsetof(Unit, default_dependencies) 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) Unit.OnFailureJobMode, config_parse_job_mode, 0, offsetof(Unit, on_failure_job_mode)
{# The following is a legacy alias name for compatibility #} {# The following is a legacy alias name for compatibility #}
Unit.OnFailureIsolate, config_parse_job_mode_isolate, 0, offsetof(Unit, on_failure_job_mode) Unit.OnFailureIsolate, config_parse_job_mode_isolate, 0, offsetof(Unit, on_failure_job_mode)

View File

@ -789,7 +789,7 @@ int config_parse_exec(
return ignore ? 0 : -ENOEXEC; return ignore ? 0 : -ENOEXEC;
} }
if (!(path_is_absolute(path) ? path_is_valid(path) : filename_is_valid(path))) { if (!path_is_absolute(path) && !filename_is_valid(path)) {
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0, log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
"Neither a valid executable name nor an absolute path%s: %s", "Neither a valid executable name nor an absolute path%s: %s",
ignore ? ", ignoring" : "", path); ignore ? ", ignoring" : "", path);
@ -3574,7 +3574,7 @@ int config_parse_unit_slice(
return 0; return 0;
} }
r = unit_set_slice(u, slice, UNIT_DEPENDENCY_FILE); r = unit_set_slice(u, slice);
if (r < 0) { 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); log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to assign slice %s to unit %s, ignoring: %m", slice->id, u->id);
return 0; return 0;

View File

@ -1141,11 +1141,12 @@ enum {
static void unit_gc_mark_good(Unit *u, unsigned gc_marker) { static void unit_gc_mark_good(Unit *u, unsigned gc_marker) {
Unit *other; Unit *other;
void *v;
u->gc_marker = gc_marker + GC_OFFSET_GOOD; u->gc_marker = gc_marker + GC_OFFSET_GOOD;
/* Recursively mark referenced units as GOOD as well */ /* Recursively mark referenced units as GOOD as well */
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_REFERENCES) HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REFERENCES])
if (other->gc_marker == gc_marker + GC_OFFSET_UNSURE) if (other->gc_marker == gc_marker + GC_OFFSET_UNSURE)
unit_gc_mark_good(other, gc_marker); unit_gc_mark_good(other, gc_marker);
} }
@ -1153,6 +1154,7 @@ static void unit_gc_mark_good(Unit *u, unsigned gc_marker) {
static void unit_gc_sweep(Unit *u, unsigned gc_marker) { static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
Unit *other; Unit *other;
bool is_bad; bool is_bad;
void *v;
assert(u); assert(u);
@ -1170,7 +1172,7 @@ static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
is_bad = true; is_bad = true;
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_REFERENCED_BY) { HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REFERENCED_BY]) {
unit_gc_sweep(other, gc_marker); unit_gc_sweep(other, gc_marker);
if (other->gc_marker == gc_marker + GC_OFFSET_GOOD) if (other->gc_marker == gc_marker + GC_OFFSET_GOOD)
@ -1295,7 +1297,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 /* If stopping a unit fails continuously we might enter a stop loop here, hence stop acting on the
* service being unnecessary after a while. */ * service being unnecessary after a while. */
if (!ratelimit_below(&u->auto_start_stop_ratelimit)) { if (!ratelimit_below(&u->auto_stop_ratelimit)) {
log_unit_warning(u, "Unit not needed anymore, but not stopping since we tried this too often recently."); log_unit_warning(u, "Unit not needed anymore, but not stopping since we tried this too often recently.");
continue; continue;
} }
@ -1309,82 +1311,6 @@ static unsigned manager_dispatch_stop_when_unneeded_queue(Manager *m) {
return n; 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) { static void manager_clear_jobs_and_units(Manager *m) {
Unit *u; Unit *u;
@ -1403,8 +1329,6 @@ static void manager_clear_jobs_and_units(Manager *m) {
assert(!m->gc_unit_queue); assert(!m->gc_unit_queue);
assert(!m->gc_job_queue); assert(!m->gc_job_queue);
assert(!m->stop_when_unneeded_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->jobs));
assert(hashmap_isempty(m->units)); assert(hashmap_isempty(m->units));
@ -1946,28 +1870,30 @@ static int manager_dispatch_target_deps_queue(Manager *m) {
Unit *u; Unit *u;
int r = 0; int r = 0;
static const UnitDependency deps[] = {
UNIT_REQUIRED_BY,
UNIT_REQUISITE_OF,
UNIT_WANTED_BY,
UNIT_BOUND_BY
};
assert(m); assert(m);
while ((u = m->target_deps_queue)) { while ((u = m->target_deps_queue)) {
_cleanup_free_ Unit **targets = NULL;
int n_targets;
assert(u->in_target_deps_queue); assert(u->in_target_deps_queue);
LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u); LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u);
u->in_target_deps_queue = false; u->in_target_deps_queue = false;
/* Take an "atomic" snapshot of dependencies here, as the call below will likely modify the for (size_t k = 0; k < ELEMENTSOF(deps); k++) {
* dependencies, and we can't have it that hash tables we iterate through are modified while Unit *target;
* we are iterating through them. */ void *v;
n_targets = unit_get_dependency_array(u, UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES, &targets);
if (n_targets < 0)
return n_targets;
for (int i = 0; i < n_targets; i++) { HASHMAP_FOREACH_KEY(v, target, u->dependencies[deps[k]]) {
r = unit_add_default_target_dependency(u, targets[i]); r = unit_add_default_target_dependency(u, target);
if (r < 0) if (r < 0)
return r; return r;
}
} }
} }
@ -3032,12 +2958,6 @@ int manager_loop(Manager *m) {
if (manager_dispatch_cgroup_realize_queue(m) > 0) if (manager_dispatch_cgroup_realize_queue(m) > 0)
continue; 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) if (manager_dispatch_stop_when_unneeded_queue(m) > 0)
continue; continue;

View File

@ -187,12 +187,6 @@ struct Manager {
/* Units that might be subject to StopWhenUnneeded= clean-up */ /* Units that might be subject to StopWhenUnneeded= clean-up */
LIST_HEAD(Unit, stop_when_unneeded_queue); 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; sd_event *event;
/* This maps PIDs we care about to units that are interested in. We allow multiple units to he interested in /* This maps PIDs we care about to units that are interested in. We allow multiple units to he interested in

View File

@ -117,8 +117,6 @@ libcore_sources = '''
timer.h timer.h
transaction.c transaction.c
transaction.h transaction.h
unit-dependency-atom.c
unit-dependency-atom.h
unit-printf.c unit-printf.c
unit-printf.h unit-printf.h
unit-serialize.c unit-serialize.c

View File

@ -66,24 +66,6 @@ static bool MOUNT_STATE_WITH_PROCESS(MountState state) {
MOUNT_CLEANING); 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) { static bool mount_is_automount(const MountParameters *p) {
assert(p); assert(p);
@ -134,33 +116,16 @@ static bool mount_is_bind(const MountParameters *p) {
return false; return false;
} }
static bool mount_is_bound_to_device(Mount *m) { static bool mount_is_bound_to_device(const Mount *m) {
const MountParameters *p; const MountParameters *p;
assert(m); if (m->from_fragment)
return true;
/* 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"); 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) { static bool mount_needs_quota(const MountParameters *p) {
assert(p); assert(p);
@ -269,6 +234,24 @@ static void mount_done(Unit *u) {
m->timer_event_source = sd_event_source_unref(m->timer_event_source); 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( static int update_parameters_proc_self_mountinfo(
Mount *m, Mount *m,
const char *what, const char *what,
@ -384,9 +367,8 @@ static int mount_add_device_dependencies(Mount *m) {
return 0; return 0;
/* Mount units from /proc/self/mountinfo are not bound to devices by default since they're subject to /* Mount units from /proc/self/mountinfo are not bound to devices by default since they're subject to
* races when mounts are established by other tools with different backing devices than what we * races when devices are unplugged. But the user can still force this dep with an appropriate option
* maintain. The user can still force this to be a BindsTo= dependency with an appropriate option (or * (or udev property) so the mount units are automatically stopped when the device disappears
* udev property) so the mount units are automatically stopped when the device disappears
* suddenly. */ * suddenly. */
dep = mount_is_bound_to_device(m) ? UNIT_BINDS_TO : UNIT_REQUIRES; dep = mount_is_bound_to_device(m) ? UNIT_BINDS_TO : UNIT_REQUIRES;
@ -396,11 +378,6 @@ static int mount_add_device_dependencies(Mount *m) {
r = unit_add_node_dependency(UNIT(m), p->what, dep, mask); r = unit_add_node_dependency(UNIT(m), p->what, dep, mask);
if (r < 0) if (r < 0)
return r; 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); return unit_add_blockdev_dependency(UNIT(m), p->what, mask);
} }

View File

@ -36,10 +36,10 @@ static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v
int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) { int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
static const int flags_table[_PATH_TYPE_MAX] = { static const int flags_table[_PATH_TYPE_MAX] = {
[PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
[PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
[PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO, [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO,
[PATH_MODIFIED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO|IN_MODIFY, [PATH_MODIFIED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO|IN_MODIFY,
[PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO, [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO,
}; };
@ -55,15 +55,13 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
if (s->inotify_fd < 0) { if (s->inotify_fd < 0) {
r = log_error_errno(errno, "Failed to allocate inotify fd: %m"); r = -errno;
goto fail; goto fail;
} }
r = sd_event_add_io(s->unit->manager->event, &s->event_source, s->inotify_fd, EPOLLIN, handler, s); r = sd_event_add_io(s->unit->manager->event, &s->event_source, s->inotify_fd, EPOLLIN, handler, s);
if (r < 0) { if (r < 0)
log_error_errno(r, "Failed to add inotify fd to event loop: %m");
goto fail; goto fail;
}
(void) sd_event_source_set_description(s->event_source, "path"); (void) sd_event_source_set_description(s->event_source, "path");
@ -71,9 +69,9 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
assert(!strstr(s->path, "//")); assert(!strstr(s->path, "//"));
for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) { for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) {
bool incomplete = false; char *cut = NULL;
int flags, wd = -1; int flags;
char tmp, *cut; char tmp;
if (slash) { if (slash) {
cut = slash + (slash == s->path); cut = slash + (slash == s->path);
@ -81,50 +79,28 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
*cut = '\0'; *cut = '\0';
flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO; flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
} else { } else
cut = NULL;
flags = flags_table[s->type]; flags = flags_table[s->type];
}
/* If this is a symlink watch both the symlink inode and where it points to. If the inode is r = inotify_add_watch(s->inotify_fd, s->path, flags);
* not a symlink both calls will install the same watch, which is redundant and doesn't if (r < 0) {
* hurt. */ if (IN_SET(errno, EACCES, ENOENT)) {
for (int follow_symlink = 0; follow_symlink < 2; follow_symlink ++) { if (cut)
uint32_t f = flags; *cut = tmp;
break;
SET_FLAG(f, IN_DONT_FOLLOW, !follow_symlink);
wd = inotify_add_watch(s->inotify_fd, s->path, f);
if (wd < 0) {
if (IN_SET(errno, EACCES, ENOENT)) {
incomplete = true; /* This is an expected error, let's accept this
* quietly: we have an incomplete watch for
* now. */
break;
}
/* This second call to inotify_add_watch() should fail like the previous one
* and is done for logging the error in a comprehensive way. */
wd = inotify_add_watch_and_warn(s->inotify_fd, s->path, f);
if (wd < 0) {
if (cut)
*cut = tmp;
r = wd;
goto fail;
}
/* Hmm, we succeeded in adding the watch this time... let's continue. */
} }
/* This second call to inotify_add_watch() should fail like the previous
* one and is done for logging the error in a comprehensive way. */
r = inotify_add_watch_and_warn(s->inotify_fd, s->path, flags);
if (r < 0) {
if (cut)
*cut = tmp;
goto fail;
}
/* Hmm, we succeeded in adding the watch this time... let's continue. */
} }
if (incomplete) {
if (cut)
*cut = tmp;
break;
}
exists = true; exists = true;
/* Path exists, we don't need to watch parent too closely. */ /* Path exists, we don't need to watch parent too closely. */
@ -146,7 +122,7 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
oldslash = slash; oldslash = slash;
else { else {
/* whole path has been iterated over */ /* whole path has been iterated over */
s->primary_wd = wd; s->primary_wd = r;
break; break;
} }
} }
@ -175,8 +151,7 @@ int path_spec_fd_event(PathSpec *s, uint32_t revents) {
union inotify_event_buffer buffer; union inotify_event_buffer buffer;
struct inotify_event *e; struct inotify_event *e;
ssize_t l; ssize_t l;
int r = 0;
assert(s);
if (revents != EPOLLIN) if (revents != EPOLLIN)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
@ -190,12 +165,13 @@ int path_spec_fd_event(PathSpec *s, uint32_t revents) {
return log_error_errno(errno, "Failed to read inotify event: %m"); return log_error_errno(errno, "Failed to read inotify event: %m");
} }
if (IN_SET(s->type, PATH_CHANGED, PATH_MODIFIED)) FOREACH_INOTIFY_EVENT(e, buffer, l) {
FOREACH_INOTIFY_EVENT(e, buffer, l) if (IN_SET(s->type, PATH_CHANGED, PATH_MODIFIED) &&
if (s->primary_wd == e->wd) s->primary_wd == e->wd)
return 1; r = 1;
}
return 0; return r;
} }
static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_notify) { static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_notify) {

View File

@ -1245,11 +1245,12 @@ static int service_collect_fds(
rn_socket_fds = 1; rn_socket_fds = 1;
} else { } else {
void *v;
Unit *u; Unit *u;
/* Pass all our configured sockets for singleton services */ /* Pass all our configured sockets for singleton services */
UNIT_FOREACH_DEPENDENCY(u, UNIT(s), UNIT_ATOM_TRIGGERED_BY) { HASHMAP_FOREACH_KEY(v, u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY]) {
_cleanup_free_ int *cfds = NULL; _cleanup_free_ int *cfds = NULL;
Socket *sock; Socket *sock;
int cn_fds; int cn_fds;
@ -2936,7 +2937,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
r = extract_first_word(&value, &fdn, NULL, EXTRACT_CUNESCAPE | EXTRACT_UNQUOTE); r = extract_first_word(&value, &fdn, NULL, EXTRACT_CUNESCAPE | EXTRACT_UNQUOTE);
if (r <= 0) { if (r <= 0) {
log_unit_debug(u, "Failed to parse fd-store-fd value: %s", value); log_unit_debug_errno(u, r, "Failed to parse fd-store-fd value \"%s\": %m", value);
return 0; return 0;
} }

View File

@ -47,20 +47,25 @@ static void slice_set_state(Slice *t, SliceState state) {
} }
static int slice_add_parent_slice(Slice *s) { static int slice_add_parent_slice(Slice *s) {
Unit *u = UNIT(s); Unit *u = UNIT(s), *parent;
_cleanup_free_ char *a = NULL; _cleanup_free_ char *a = NULL;
int r; int r;
assert(s); assert(s);
if (UNIT_GET_SLICE(u)) if (UNIT_ISSET(u->slice))
return 0; return 0;
r = slice_build_parent_slice(u->id, &a); r = slice_build_parent_slice(u->id, &a);
if (r <= 0) /* 0 means root slice */ if (r <= 0) /* 0 means root slice */
return r; return r;
return unit_add_dependency_by_name(u, UNIT_IN_SLICE, a, true, UNIT_DEPENDENCY_IMPLICIT); r = manager_load_unit(u->manager, a, NULL, NULL, &parent);
if (r < 0)
return r;
unit_ref_set(&u->slice, u, parent);
return 0;
} }
static int slice_add_default_dependencies(Slice *s) { static int slice_add_default_dependencies(Slice *s) {
@ -96,7 +101,7 @@ static int slice_verify(Slice *s) {
if (r < 0) if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to determine parent slice: %m"); return log_unit_error_errno(UNIT(s), r, "Failed to determine parent slice: %m");
if (parent ? !unit_has_name(UNIT_GET_SLICE(UNIT(s)), parent) : !!UNIT_GET_SLICE(UNIT(s))) if (parent ? !unit_has_name(UNIT_DEREF(UNIT(s)->slice), parent) : UNIT_ISSET(UNIT(s)->slice))
return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Located outside of parent slice. Refusing."); return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Located outside of parent slice. Refusing.");
return 0; return 0;
@ -341,11 +346,15 @@ static void slice_enumerate_perpetual(Manager *m) {
static bool slice_freezer_action_supported_by_children(Unit *s) { static bool slice_freezer_action_supported_by_children(Unit *s) {
Unit *member; Unit *member;
int r; void *v;
assert(s); assert(s);
UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_SLICE_OF) { HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE]) {
int r;
if (UNIT_DEREF(member->slice) != s)
continue;
if (member->type == UNIT_SLICE) { if (member->type == UNIT_SLICE) {
r = slice_freezer_action_supported_by_children(member); r = slice_freezer_action_supported_by_children(member);
@ -362,6 +371,7 @@ static bool slice_freezer_action_supported_by_children(Unit *s) {
static int slice_freezer_action(Unit *s, FreezerAction action) { static int slice_freezer_action(Unit *s, FreezerAction action) {
Unit *member; Unit *member;
void *v;
int r; int r;
assert(s); assert(s);
@ -372,11 +382,15 @@ static int slice_freezer_action(Unit *s, FreezerAction action) {
return 0; return 0;
} }
UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_SLICE_OF) { HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE]) {
if (UNIT_DEREF(member->slice) != s)
continue;
if (action == FREEZER_FREEZE) if (action == FREEZER_FREEZE)
r = UNIT_VTABLE(member)->freeze(member); r = UNIT_VTABLE(member)->freeze(member);
else else
r = UNIT_VTABLE(member)->thaw(member); r = UNIT_VTABLE(member)->thaw(member);
if (r < 0) if (r < 0)
return r; return r;
} }

View File

@ -2340,9 +2340,11 @@ static void socket_enter_running(Socket *s, int cfd_in) {
if (cfd < 0) { if (cfd < 0) {
bool pending = false; bool pending = false;
Unit *other; Unit *other;
void *v;
/* If there's already a start pending don't bother to do anything */ /* If there's already a start pending don't bother to
UNIT_FOREACH_DEPENDENCY(other, UNIT(s), UNIT_ATOM_TRIGGERS) * do anything */
HASHMAP_FOREACH_KEY(v, other, UNIT(s)->dependencies[UNIT_TRIGGERS])
if (unit_active_or_pending(other)) { if (unit_active_or_pending(other)) {
pending = true; pending = true;
break; break;

View File

@ -35,31 +35,34 @@ static void target_set_state(Target *t, TargetState state) {
} }
static int target_add_default_dependencies(Target *t) { static int target_add_default_dependencies(Target *t) {
_cleanup_free_ Unit **others = NULL;
int r, n_others; static const UnitDependency deps[] = {
UNIT_REQUIRES,
UNIT_REQUISITE,
UNIT_WANTS,
UNIT_BINDS_TO,
UNIT_PART_OF
};
int r;
assert(t); assert(t);
if (!UNIT(t)->default_dependencies) if (!UNIT(t)->default_dependencies)
return 0; return 0;
/* Imply ordering for requirement dependencies on target units. Note that when the user created a /* Imply ordering for requirement dependencies on target units. Note that when the user created a contradicting
* contradicting ordering manually we won't add anything in here to make sure we don't create a * ordering manually we won't add anything in here to make sure we don't create a loop. */
* 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. */
n_others = unit_get_dependency_array(UNIT(t), UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE, &others); for (size_t k = 0; k < ELEMENTSOF(deps); k++) {
if (n_others < 0) Unit *other;
return n_others; void *v;
for (int i = 0; i < n_others; i++) { HASHMAP_FOREACH_KEY(v, other, UNIT(t)->dependencies[deps[k]]) {
r = unit_add_default_target_dependency(others[i], UNIT(t)); r = unit_add_default_target_dependency(other, UNIT(t));
if (r < 0) if (r < 0)
return r; return r;
}
} }
if (unit_has_name(UNIT(t), SPECIAL_SHUTDOWN_TARGET)) if (unit_has_name(UNIT(t), SPECIAL_SHUTDOWN_TARGET))

View File

@ -347,20 +347,21 @@ 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) { static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, sd_bus_error *e) {
Unit *u;
static const UnitDependencyAtom directions[] = { void *v;
UNIT_ATOM_BEFORE,
UNIT_ATOM_AFTER,
};
int r; int r;
static const UnitDependency directions[] = {
UNIT_BEFORE,
UNIT_AFTER,
};
size_t d;
assert(tr); assert(tr);
assert(j); assert(j);
assert(!j->transaction_prev); assert(!j->transaction_prev);
/* Does a recursive sweep through the ordering graph, looking for a cycle. If we find a cycle we try /* Does a recursive sweep through the ordering graph, looking
* to break it. */ * for a cycle. If we find a cycle we try to break it. */
/* Have we seen this before? */ /* Have we seen this before? */
if (j->generation == generation) { if (j->generation == generation) {
@ -368,14 +369,18 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
_cleanup_free_ char **array = NULL, *unit_ids = NULL; _cleanup_free_ char **array = NULL, *unit_ids = NULL;
char **unit_id, **job_type; char **unit_id, **job_type;
/* If the marker is NULL we have been here already and decided the job was loop-free from /* If the marker is NULL we have been here already and
* here. Hence shortcut things and return right-away. */ * decided the job was loop-free from here. Hence
* shortcut things and return right-away. */
if (!j->marker) if (!j->marker)
return 0; return 0;
/* So, the marker is not NULL and we already have been here. We have a cycle. Let's try to /* So, the marker is not NULL and we already have been here. We have
* break it. We go backwards in our path and try to find a suitable job to remove. We use the * a cycle. Let's try to break it. We go backwards in our path and
* marker to find our way back, since smart how we are we stored our way back in there. */ * 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 (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
/* For logging below */ /* For logging below */
@ -444,17 +449,16 @@ 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 * 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 * ordering dependencies and we test with job_compare() whether it is the 'before' edge in the job
* execution ordering. */ * execution ordering. */
for (size_t d = 0; d < ELEMENTSOF(directions); d++) { for (d = 0; d < ELEMENTSOF(directions); d++) {
Unit *u; HASHMAP_FOREACH_KEY(v, u, j->unit->dependencies[directions[d]]) {
UNIT_FOREACH_DEPENDENCY(u, j->unit, directions[d]) {
Job *o; Job *o;
/* Is there a job for this unit? */ /* Is there a job for this unit? */
o = hashmap_get(tr->jobs, u); o = hashmap_get(tr->jobs, u);
if (!o) { if (!o) {
/* Ok, there is no job for this in the transaction, but maybe there is /* Ok, there is no job for this in the
* already one running? */ * transaction, but maybe there is already one
* running? */
o = u->job; o = u->job;
if (!o) if (!o)
continue; continue;
@ -875,12 +879,13 @@ 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) { void transaction_add_propagate_reload_jobs(Transaction *tr, Unit *unit, Job *by, bool ignore_order, sd_bus_error *e) {
JobType nt; JobType nt;
Unit *dep; Unit *dep;
void *v;
int r; int r;
assert(tr); assert(tr);
assert(unit); assert(unit);
UNIT_FOREACH_DEPENDENCY(dep, unit, UNIT_ATOM_PROPAGATES_RELOAD_TO) { HASHMAP_FOREACH_KEY(v, dep, unit->dependencies[UNIT_PROPAGATES_RELOAD_TO]) {
nt = job_type_collapse(JOB_TRY_RELOAD, dep); nt = job_type_collapse(JOB_TRY_RELOAD, dep);
if (nt == JOB_NOP) if (nt == JOB_NOP)
continue; continue;
@ -909,6 +914,7 @@ int transaction_add_job_and_dependencies(
bool is_new; bool is_new;
Unit *dep; Unit *dep;
Job *ret; Job *ret;
void *v;
int r; int r;
assert(tr); assert(tr);
@ -1000,7 +1006,7 @@ int transaction_add_job_and_dependencies(
/* Finally, recursively add in all dependencies. */ /* Finally, recursively add in all dependencies. */
if (IN_SET(type, JOB_START, JOB_RESTART)) { if (IN_SET(type, JOB_START, JOB_RESTART)) {
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_START) { HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_REQUIRES]) {
r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e); r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e);
if (r < 0) { if (r < 0) {
if (r != -EBADR) /* job type not applicable */ if (r != -EBADR) /* job type not applicable */
@ -1010,7 +1016,17 @@ int transaction_add_job_and_dependencies(
} }
} }
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_START_IGNORED) { 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]) {
r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, ignore_order, e); r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, ignore_order, e);
if (r < 0) { if (r < 0) {
/* unit masked, job type not applicable and unit not found are not considered as errors. */ /* unit masked, job type not applicable and unit not found are not considered as errors. */
@ -1022,7 +1038,7 @@ int transaction_add_job_and_dependencies(
} }
} }
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_VERIFY) { HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_REQUISITE]) {
r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, false, false, ignore_order, e); r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, false, false, ignore_order, e);
if (r < 0) { if (r < 0) {
if (r != -EBADR) /* job type not applicable */ if (r != -EBADR) /* job type not applicable */
@ -1032,7 +1048,7 @@ int transaction_add_job_and_dependencies(
} }
} }
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_STOP) { HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_CONFLICTS]) {
r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, true, false, ignore_order, e); r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, true, false, ignore_order, e);
if (r < 0) { if (r < 0) {
if (r != -EBADR) /* job type not applicable */ if (r != -EBADR) /* job type not applicable */
@ -1042,7 +1058,7 @@ int transaction_add_job_and_dependencies(
} }
} }
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_STOP_IGNORED) { HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_CONFLICTED_BY]) {
r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, false, false, ignore_order, e); r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, false, false, ignore_order, e);
if (r < 0) { if (r < 0) {
log_unit_warning(dep, log_unit_warning(dep,
@ -1051,37 +1067,41 @@ int transaction_add_job_and_dependencies(
sd_bus_error_free(e); sd_bus_error_free(e);
} }
} }
} }
if (IN_SET(type, JOB_STOP, JOB_RESTART)) { if (IN_SET(type, JOB_STOP, JOB_RESTART)) {
UnitDependencyAtom atom; static const UnitDependency propagate_deps[] = {
UNIT_REQUIRED_BY,
UNIT_REQUISITE_OF,
UNIT_BOUND_BY,
UNIT_CONSISTS_OF,
};
JobType ptype; 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. */ * dependencies that are not around. */
if (type == JOB_RESTART) { ptype = type == JOB_RESTART ? JOB_TRY_RESTART : type;
atom = UNIT_ATOM_PROPAGATE_RESTART;
ptype = JOB_TRY_RESTART;
} else {
ptype = JOB_STOP;
atom = UNIT_ATOM_PROPAGATE_STOP;
}
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, atom) { for (j = 0; j < ELEMENTSOF(propagate_deps); j++)
JobType nt; HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[propagate_deps[j]]) {
JobType nt;
nt = job_type_collapse(ptype, dep); nt = job_type_collapse(ptype, dep);
if (nt == JOB_NOP) if (nt == JOB_NOP)
continue; continue;
r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e); r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e);
if (r < 0) { if (r < 0) {
if (r != -EBADR) /* job type not applicable */ if (r != -EBADR) /* job type not applicable */
goto fail; goto fail;
sd_bus_error_free(e); sd_bus_error_free(e);
}
} }
}
} }
if (type == JOB_RELOAD) if (type == JOB_RELOAD)
@ -1130,14 +1150,14 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
} }
int transaction_add_triggering_jobs(Transaction *tr, Unit *u) { int transaction_add_triggering_jobs(Transaction *tr, Unit *u) {
void *v;
Unit *trigger; Unit *trigger;
int r; int r;
assert(tr); assert(tr);
assert(u); assert(u);
UNIT_FOREACH_DEPENDENCY(trigger, u, UNIT_ATOM_TRIGGERED_BY) { HASHMAP_FOREACH_KEY(v, trigger, u->dependencies[UNIT_TRIGGERED_BY]) {
/* No need to stop inactive jobs */ /* No need to stop inactive jobs */
if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(trigger)) && !trigger->job) if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(trigger)) && !trigger->job)
continue; continue;

View File

@ -1,240 +0,0 @@
/* 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;
}
}

View File

@ -1,87 +0,0 @@
/* 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);

View File

@ -132,15 +132,18 @@ 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) { static int specifier_cgroup_slice(char specifier, const void *data, const void *userdata, char **ret) {
const Unit *u = userdata, *slice; const Unit *u = userdata;
char *n; char *n;
assert(u); assert(u);
bad_specifier(u, specifier); bad_specifier(u, specifier);
slice = UNIT_GET_SLICE(u); if (UNIT_ISSET(u->slice)) {
if (slice) { const Unit *slice;
slice = UNIT_DEREF(u->slice);
if (slice->cgroup_path) if (slice->cgroup_path)
n = strdup(slice->cgroup_path); n = strdup(slice->cgroup_path);
else else

View File

@ -604,7 +604,10 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
"%s\tNeed Daemon Reload: %s\n" "%s\tNeed Daemon Reload: %s\n"
"%s\tTransient: %s\n" "%s\tTransient: %s\n"
"%s\tPerpetual: %s\n" "%s\tPerpetual: %s\n"
"%s\tGarbage Collection Mode: %s\n", "%s\tGarbage Collection Mode: %s\n"
"%s\tSlice: %s\n"
"%s\tCGroup: %s\n"
"%s\tCGroup realized: %s\n",
prefix, unit_description(u), prefix, unit_description(u),
prefix, strna(u->instance), prefix, strna(u->instance),
prefix, unit_load_state_to_string(u->load_state), prefix, unit_load_state_to_string(u->load_state),
@ -618,7 +621,10 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
prefix, yes_no(unit_need_daemon_reload(u)), prefix, yes_no(unit_need_daemon_reload(u)),
prefix, yes_no(u->transient), prefix, yes_no(u->transient),
prefix, yes_no(u->perpetual), prefix, yes_no(u->perpetual),
prefix, collect_mode_to_string(u->collect_mode)); 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));
if (u->markers != 0) { if (u->markers != 0) {
fprintf(f, "%s\tMarkers:", prefix); fprintf(f, "%s\tMarkers:", prefix);
@ -629,47 +635,37 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
fputs("\n", f); fputs("\n", f);
} }
if (UNIT_HAS_CGROUP_CONTEXT(u)) { if (u->cgroup_realized_mask != 0) {
fprintf(f, _cleanup_free_ char *s = NULL;
"%s\tSlice: %s\n" (void) cg_mask_to_string(u->cgroup_realized_mask, &s);
"%s\tCGroup: %s\n" fprintf(f, "%s\tCGroup realized mask: %s\n", prefix, strnull(s));
"%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_realized_mask != 0) { if (u->cgroup_enabled_mask != 0) {
_cleanup_free_ char *s = NULL; _cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(u->cgroup_realized_mask, &s); (void) cg_mask_to_string(u->cgroup_enabled_mask, &s);
fprintf(f, "%s\tCGroup realized mask: %s\n", prefix, strnull(s)); fprintf(f, "%s\tCGroup enabled mask: %s\n", prefix, strnull(s));
} }
if (u->cgroup_enabled_mask != 0) { m = unit_get_own_mask(u);
_cleanup_free_ char *s = NULL; if (m != 0) {
(void) cg_mask_to_string(u->cgroup_enabled_mask, &s); _cleanup_free_ char *s = NULL;
fprintf(f, "%s\tCGroup enabled mask: %s\n", prefix, strnull(s)); (void) cg_mask_to_string(m, &s);
} fprintf(f, "%s\tCGroup own mask: %s\n", prefix, strnull(s));
}
m = unit_get_own_mask(u); m = unit_get_members_mask(u);
if (m != 0) { if (m != 0) {
_cleanup_free_ char *s = NULL; _cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(m, &s); (void) cg_mask_to_string(m, &s);
fprintf(f, "%s\tCGroup own mask: %s\n", prefix, strnull(s)); fprintf(f, "%s\tCGroup members mask: %s\n", prefix, strnull(s));
} }
m = unit_get_members_mask(u); m = unit_get_delegate_mask(u);
if (m != 0) { if (m != 0) {
_cleanup_free_ char *s = NULL; _cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(m, &s); (void) cg_mask_to_string(m, &s);
fprintf(f, "%s\tCGroup members mask: %s\n", prefix, strnull(s)); fprintf(f, "%s\tCGroup delegate 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)) if (!sd_id128_is_null(u->invocation_id))
@ -739,7 +735,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
UnitDependencyInfo di; UnitDependencyInfo di;
Unit *other; Unit *other;
HASHMAP_FOREACH_KEY(di.data, other, unit_get_dependencies(u, d)) { HASHMAP_FOREACH_KEY(di.data, other, u->dependencies[d]) {
bool space = false; bool space = false;
fprintf(f, "%s\t%s: %s (", prefix, unit_dependency_to_string(d), other->id); fprintf(f, "%s\t%s: %s (", prefix, unit_dependency_to_string(d), other->id);
@ -774,14 +770,12 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
"%s\tRefuseManualStart: %s\n" "%s\tRefuseManualStart: %s\n"
"%s\tRefuseManualStop: %s\n" "%s\tRefuseManualStop: %s\n"
"%s\tDefaultDependencies: %s\n" "%s\tDefaultDependencies: %s\n"
"%s\tOnSuccessJobMode: %s\n"
"%s\tOnFailureJobMode: %s\n" "%s\tOnFailureJobMode: %s\n"
"%s\tIgnoreOnIsolate: %s\n", "%s\tIgnoreOnIsolate: %s\n",
prefix, yes_no(u->stop_when_unneeded), prefix, yes_no(u->stop_when_unneeded),
prefix, yes_no(u->refuse_manual_start), prefix, yes_no(u->refuse_manual_start),
prefix, yes_no(u->refuse_manual_stop), prefix, yes_no(u->refuse_manual_stop),
prefix, yes_no(u->default_dependencies), 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, job_mode_to_string(u->on_failure_job_mode),
prefix, yes_no(u->ignore_on_isolate)); prefix, yes_no(u->ignore_on_isolate));

File diff suppressed because it is too large Load Diff

View File

@ -102,16 +102,6 @@ typedef union UnitDependencyInfo {
} _packed_; } _packed_;
} UnitDependencyInfo; } 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" #include "job.h"
struct UnitRef { struct UnitRef {
@ -135,13 +125,11 @@ typedef struct Unit {
Set *aliases; /* All the other names. */ Set *aliases; /* All the other names. */
/* For each dependency type we can look up another Hashmap with this, whose key is a Unit* object, /* For each dependency type we maintain a Hashmap whose key is the Unit* object, and the value encodes why the
* and whose value encodes why the dependency exists, using the UnitDependencyInfo type. i.e. a * dependency exists, using the UnitDependencyInfo type */
* Hashmap(UnitDependency Hashmap(Unit* UnitDependencyInfo)) */ Hashmap *dependencies[_UNIT_DEPENDENCY_MAX];
Hashmap *dependencies;
/* Similar, for RequiresMountsFor= path dependencies. The key is the path, the value the /* Similar, for RequiresMountsFor= path dependencies. The key is the path, the value the UnitDependencyInfo type */
* UnitDependencyInfo type */
Hashmap *requires_mounts_for; Hashmap *requires_mounts_for;
char *description; char *description;
@ -202,6 +190,8 @@ typedef struct Unit {
dual_timestamp active_exit_timestamp; dual_timestamp active_exit_timestamp;
dual_timestamp inactive_enter_timestamp; dual_timestamp inactive_enter_timestamp;
UnitRef slice;
/* Per type list */ /* Per type list */
LIST_FIELDS(Unit, units_by_type); LIST_FIELDS(Unit, units_by_type);
@ -229,15 +219,9 @@ typedef struct Unit {
/* Target dependencies queue */ /* Target dependencies queue */
LIST_FIELDS(Unit, target_deps_queue); LIST_FIELDS(Unit, target_deps_queue);
/* Queue of units with StopWhenUnneeded= set that shall be checked for clean-up. */ /* Queue of units with StopWhenUnneeded set that shell be checked for clean-up. */
LIST_FIELDS(Unit, stop_when_unneeded_queue); 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 /* PIDs we keep an eye on. Note that a unit might have many
* more, but these are the ones we care enough about to * more, but these are the ones we care enough about to
* process SIGCHLD for */ * process SIGCHLD for */
@ -266,8 +250,8 @@ typedef struct Unit {
int success_action_exit_status, failure_action_exit_status; int success_action_exit_status, failure_action_exit_status;
char *reboot_arg; char *reboot_arg;
/* Make sure we never enter endless loops with the StopWhenUnneeded=, BindsTo=, Uphold= logic */ /* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */
RateLimit auto_start_stop_ratelimit; RateLimit auto_stop_ratelimit;
/* Reference to a specific UID/GID */ /* Reference to a specific UID/GID */
uid_t ref_uid; uid_t ref_uid;
@ -340,8 +324,7 @@ typedef struct Unit {
* ones which might have appeared. */ * ones which might have appeared. */
sd_event_source *rewatch_pids_event_source; sd_event_source *rewatch_pids_event_source;
/* How to start OnSuccess=/OnFailure= units */ /* How to start OnFailure units */
JobMode on_success_job_mode;
JobMode on_failure_job_mode; JobMode on_failure_job_mode;
/* Tweaking the GC logic */ /* Tweaking the GC logic */
@ -389,8 +372,6 @@ typedef struct Unit {
bool in_cgroup_oom_queue:1; bool in_cgroup_oom_queue:1;
bool in_target_deps_queue:1; bool in_target_deps_queue:1;
bool in_stop_when_unneeded_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; bool sent_dbus_new_signal:1;
@ -702,19 +683,8 @@ 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_CGROUP_CONTEXT(u) (UNIT_VTABLE(u)->cgroup_context_offset > 0)
#define UNIT_HAS_KILL_CONTEXT(u) (UNIT_VTABLE(u)->kill_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) { static inline Unit* UNIT_TRIGGER(Unit *u) {
return unit_has_dependency(u, UNIT_ATOM_TRIGGERS, NULL); return hashmap_first_key(u->dependencies[UNIT_TRIGGERS]);
}
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); Unit* unit_new(Manager *m, size_t size);
@ -748,8 +718,6 @@ void unit_add_to_cleanup_queue(Unit *u);
void unit_add_to_gc_queue(Unit *u); void unit_add_to_gc_queue(Unit *u);
void unit_add_to_target_deps_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_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(Unit *u, Unit *other);
int unit_merge_by_name(Unit *u, const char *other); int unit_merge_by_name(Unit *u, const char *other);
@ -759,7 +727,7 @@ Unit *unit_follow_merge(Unit *u) _pure_;
int unit_load_fragment_and_dropin(Unit *u, bool fragment_required); int unit_load_fragment_and_dropin(Unit *u, bool fragment_required);
int unit_load(Unit *unit); int unit_load(Unit *unit);
int unit_set_slice(Unit *u, Unit *slice, UnitDependencyMask mask); int unit_set_slice(Unit *u, Unit *slice);
int unit_set_default_slice(Unit *u); int unit_set_default_slice(Unit *u);
const char *unit_description(Unit *u) _pure_; const char *unit_description(Unit *u) _pure_;
@ -839,7 +807,7 @@ bool unit_will_restart(Unit *u);
int unit_add_default_target_dependency(Unit *u, Unit *target); int unit_add_default_target_dependency(Unit *u, Unit *target);
void unit_start_on_failure(Unit *u, const char *dependency_name, UnitDependencyAtom atom, JobMode job_mode); void unit_start_on_failure(Unit *u);
void unit_trigger_notify(Unit *u); void unit_trigger_notify(Unit *u);
UnitFileState unit_get_unit_file_state(Unit *u); UnitFileState unit_get_unit_file_state(Unit *u);
@ -879,8 +847,6 @@ bool unit_type_supported(UnitType t);
bool unit_is_pristine(Unit *u); bool unit_is_pristine(Unit *u);
bool unit_is_unneeded(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_control_pid(Unit *u);
pid_t unit_main_pid(Unit *u); pid_t unit_main_pid(Unit *u);
@ -1024,54 +990,3 @@ int unit_thaw_vtable_common(Unit *u);
const char* collect_mode_to_string(CollectMode m) _const_; const char* collect_mode_to_string(CollectMode m) _const_;
CollectMode collect_mode_from_string(const char *s) _pure_; 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))

View File

@ -97,7 +97,7 @@ static int help(void) {
" Whether to require user verification to unlock the volume\n" " Whether to require user verification to unlock the volume\n"
" --tpm2-device=PATH\n" " --tpm2-device=PATH\n"
" Enroll a TPM2 device\n" " Enroll a TPM2 device\n"
" --tpm2-pcrs=PCR1+PCR2+PCR3,…\n" " --tpm2-pcrs=PCR1,PCR2,PCR3,…\n"
" Specify TPM2 PCRs to seal against\n" " Specify TPM2 PCRs to seal against\n"
" --wipe-slot=SLOT1,SLOT2,…\n" " --wipe-slot=SLOT1,SLOT2,…\n"
" Wipe specified slots\n" " Wipe specified slots\n"

View File

@ -1086,15 +1086,9 @@ static int terminate_user(int argc, char *argv[], void *userdata) {
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
uid_t uid; uid_t uid;
if (isempty(argv[i])) r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0);
uid = getuid(); if (r < 0)
else { return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
const char *u = argv[i];
r = get_user_creds(&u, &uid, NULL, NULL, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
}
r = bus_call_method(bus, bus_login_mgr, "TerminateUser", &error, NULL, "u", (uint32_t) uid); r = bus_call_method(bus, bus_login_mgr, "TerminateUser", &error, NULL, "u", (uint32_t) uid);
if (r < 0) if (r < 0)
@ -1120,15 +1114,9 @@ static int kill_user(int argc, char *argv[], void *userdata) {
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
uid_t uid; uid_t uid;
if (isempty(argv[i])) r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0);
uid = getuid(); if (r < 0)
else { return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
const char *u = argv[i];
r = get_user_creds(&u, &uid, NULL, NULL, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
}
r = bus_call_method( r = bus_call_method(
bus, bus,

View File

@ -4070,7 +4070,7 @@ static int help(void) {
" --definitions=DIR Find partition definitions in specified directory\n" " --definitions=DIR Find partition definitions in specified directory\n"
" --key-file=PATH Key to use when encrypting partitions\n" " --key-file=PATH Key to use when encrypting partitions\n"
" --tpm2-device=PATH Path to TPM2 device node to use\n" " --tpm2-device=PATH Path to TPM2 device node to use\n"
" --tpm2-pcrs=PCR1+PCR2+PCR3+\n" " --tpm2-pcrs=PCR1,PCR2,\n"
" TPM2 PCR indexes to use for TPM2 enrollment\n" " TPM2 PCR indexes to use for TPM2 enrollment\n"
" --seed=UUID 128bit seed UUID to derive all UUIDs from\n" " --seed=UUID 128bit seed UUID to derive all UUIDs from\n"
" --size=BYTES Grow loopback file to specified size\n" " --size=BYTES Grow loopback file to specified size\n"

View File

@ -1335,7 +1335,7 @@ int dns_name_apply_idna(const char *name, char **ret) {
return -EINVAL; return -EINVAL;
#elif HAVE_LIBIDN #elif HAVE_LIBIDN
_cleanup_free_ char *buf = NULL; _cleanup_free_ char *buf = NULL;
size_t n = 0; size_t n = 0, allocated = 0;
bool first = true; bool first = true;
int r, q; int r, q;
@ -1357,7 +1357,7 @@ int dns_name_apply_idna(const char *name, char **ret) {
if (q > 0) if (q > 0)
r = q; r = q;
if (!GREEDY_REALLOC(buf, n + !first + DNS_LABEL_ESCAPED_MAX)) if (!GREEDY_REALLOC(buf, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
return -ENOMEM; return -ENOMEM;
r = dns_label_escape(label, r, buf + n + !first, DNS_LABEL_ESCAPED_MAX); r = dns_label_escape(label, r, buf + n + !first, DNS_LABEL_ESCAPED_MAX);
@ -1375,7 +1375,7 @@ int dns_name_apply_idna(const char *name, char **ret) {
if (n > DNS_HOSTNAME_MAX) if (n > DNS_HOSTNAME_MAX)
return -EINVAL; return -EINVAL;
if (!GREEDY_REALLOC(buf, n + 1)) if (!GREEDY_REALLOC(buf, allocated, n + 1))
return -ENOMEM; return -ENOMEM;
buf[n] = 0; buf[n] = 0;

View File

@ -920,23 +920,13 @@ int tpm2_parse_pcrs(const char *s, uint32_t *ret) {
uint32_t mask = 0; uint32_t mask = 0;
int r; int r;
assert(s); /* Parses a comma-separated list of PCR indexes */
if (isempty(s)) {
*ret = 0;
return 0;
}
/* Parses a "," or "+" separated list of PCR indexes. We support "," since this is a list after all,
* and most other tools expect comma separated PCR specifications. We also support "+" since in
* /etc/crypttab the "," is already used to separate options, hence a different separator is nice to
* avoid escaping. */
for (;;) { for (;;) {
_cleanup_free_ char *pcr = NULL; _cleanup_free_ char *pcr = NULL;
unsigned n; unsigned n;
r = extract_first_word(&p, &pcr, ",+", EXTRACT_DONT_COALESCE_SEPARATORS); r = extract_first_word(&p, &pcr, ",", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r == 0) if (r == 0)
break; break;
if (r < 0) if (r < 0)

View File

@ -422,8 +422,6 @@ tests += [
[['src/test/test-sleep.c']], [['src/test/test-sleep.c']],
[['src/test/test-tpm2.c']],
[['src/test/test-replace-var.c']], [['src/test/test-replace-var.c']],
[['src/test/test-calendarspec.c']], [['src/test/test-calendarspec.c']],

View File

@ -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, "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, "nomem.slice", NULL, &nomem_parent) >= 0);
assert_se(manager_load_startable_unit_or_warn(m, "nomemleaf.service", NULL, &nomem_leaf) >= 0); assert_se(manager_load_startable_unit_or_warn(m, "nomemleaf.service", NULL, &nomem_leaf) >= 0);
assert_se(UNIT_GET_SLICE(son) == parent); assert_se(UNIT_DEREF(son->slice) == parent);
assert_se(UNIT_GET_SLICE(daughter) == parent); assert_se(UNIT_DEREF(daughter->slice) == parent);
assert_se(UNIT_GET_SLICE(parent_deep) == parent); assert_se(UNIT_DEREF(parent_deep->slice) == parent);
assert_se(UNIT_GET_SLICE(grandchild) == parent_deep); assert_se(UNIT_DEREF(grandchild->slice) == parent_deep);
assert_se(UNIT_GET_SLICE(nomem_leaf) == nomem_parent); assert_se(UNIT_DEREF(nomem_leaf->slice) == nomem_parent);
root = UNIT_GET_SLICE(parent); root = UNIT_DEREF(parent->slice);
assert_se(UNIT_GET_SLICE(nomem_parent) == root); assert_se(UNIT_DEREF(nomem_parent->slice) == root);
/* Verify per-unit cgroups settings. */ /* Verify per-unit cgroups settings. */
ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(son), CGROUP_MASK_CPU); ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(son), CGROUP_MASK_CPU);

View File

@ -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.slice", NULL, &dml) >= 0);
assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough.slice", NULL, &dml_passthrough) >= 0); assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough.slice", NULL, &dml_passthrough) >= 0);
assert_se(UNIT_GET_SLICE(dml_passthrough) == dml); assert_se(UNIT_DEREF(dml_passthrough->slice) == dml);
assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-empty.service", NULL, &dml_passthrough_empty) >= 0); assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-empty.service", NULL, &dml_passthrough_empty) >= 0);
assert_se(UNIT_GET_SLICE(dml_passthrough_empty) == dml_passthrough); assert_se(UNIT_DEREF(dml_passthrough_empty->slice) == dml_passthrough);
assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-dml.service", NULL, &dml_passthrough_set_dml) >= 0); assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-dml.service", NULL, &dml_passthrough_set_dml) >= 0);
assert_se(UNIT_GET_SLICE(dml_passthrough_set_dml) == dml_passthrough); assert_se(UNIT_DEREF(dml_passthrough_set_dml->slice) == dml_passthrough);
assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-ml.service", NULL, &dml_passthrough_set_ml) >= 0); assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-ml.service", NULL, &dml_passthrough_set_ml) >= 0);
assert_se(UNIT_GET_SLICE(dml_passthrough_set_ml) == dml_passthrough); assert_se(UNIT_DEREF(dml_passthrough_set_ml->slice) == dml_passthrough);
assert_se(manager_load_startable_unit_or_warn(m, "dml-override.slice", NULL, &dml_override) >= 0); assert_se(manager_load_startable_unit_or_warn(m, "dml-override.slice", NULL, &dml_override) >= 0);
assert_se(UNIT_GET_SLICE(dml_override) == dml); assert_se(UNIT_DEREF(dml_override->slice) == dml);
assert_se(manager_load_startable_unit_or_warn(m, "dml-override-empty.service", NULL, &dml_override_empty) >= 0); assert_se(manager_load_startable_unit_or_warn(m, "dml-override-empty.service", NULL, &dml_override_empty) >= 0);
assert_se(UNIT_GET_SLICE(dml_override_empty) == dml_override); assert_se(UNIT_DEREF(dml_override_empty->slice) == dml_override);
assert_se(manager_load_startable_unit_or_warn(m, "dml-discard.slice", NULL, &dml_discard) >= 0); assert_se(manager_load_startable_unit_or_warn(m, "dml-discard.slice", NULL, &dml_discard) >= 0);
assert_se(UNIT_GET_SLICE(dml_discard) == dml); assert_se(UNIT_DEREF(dml_discard->slice) == dml);
assert_se(manager_load_startable_unit_or_warn(m, "dml-discard-empty.service", NULL, &dml_discard_empty) >= 0); assert_se(manager_load_startable_unit_or_warn(m, "dml-discard-empty.service", NULL, &dml_discard_empty) >= 0);
assert_se(UNIT_GET_SLICE(dml_discard_empty) == dml_discard); assert_se(UNIT_DEREF(dml_discard_empty->slice) == dml_discard);
assert_se(manager_load_startable_unit_or_warn(m, "dml-discard-set-ml.service", NULL, &dml_discard_set_ml) >= 0); assert_se(manager_load_startable_unit_or_warn(m, "dml-discard-set-ml.service", NULL, &dml_discard_set_ml) >= 0);
assert_se(UNIT_GET_SLICE(dml_discard_set_ml) == dml_discard); assert_se(UNIT_DEREF(dml_discard_set_ml->slice) == dml_discard);
assert_se(root = UNIT_GET_SLICE(dml)); root = UNIT_DEREF(dml->slice);
assert_se(!UNIT_GET_SLICE(root)); assert_se(!UNIT_ISSET(root->slice));
assert_se(unit_get_ancestor_memory_low(root) == CGROUP_LIMIT_MIN); assert_se(unit_get_ancestor_memory_low(root) == CGROUP_LIMIT_MIN);

View File

@ -6,75 +6,16 @@
#include "bus-util.h" #include "bus-util.h"
#include "manager.h" #include "manager.h"
#include "rm-rf.h" #include "rm-rf.h"
#include "service.h"
#include "special.h"
#include "strv.h" #include "strv.h"
#include "tests.h" #include "tests.h"
#include "unit-serialize.h" #include "service.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[]) { int main(int argc, char *argv[]) {
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
_cleanup_(manager_freep) Manager *m = NULL; _cleanup_(manager_freep) Manager *m = NULL;
Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = 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, *stub = NULL; *h = NULL, *i = NULL, *a_conj = NULL, *unit_with_multiple_dashes = NULL;
Job *j; Job *j;
int r; int r;
@ -181,83 +122,37 @@ int main(int argc, char *argv[]) {
assert_se(manager_add_job(m, JOB_START, a_conj, JOB_REPLACE, NULL, NULL, &j) == -EDEADLK); assert_se(manager_add_job(m, JOB_START, a_conj, JOB_REPLACE, NULL, NULL, &j) == -EDEADLK);
manager_dump_jobs(m, stdout, "\t"); manager_dump_jobs(m, stdout, "\t");
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b)); assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b));
assert_se(!hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a)); assert_se(!hashmap_get(b->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c)); assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], c));
assert_se(!hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a)); assert_se(!hashmap_get(c->dependencies[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, 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(unit_add_dependency(a, UNIT_PROPAGATES_RELOAD_TO, c, true, UNIT_DEPENDENCY_PROC_SWAP) == 0);
assert_se(hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b)); assert_se(hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b));
assert_se(hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a)); assert_se(hashmap_get(b->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
assert_se(hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c)); assert_se(hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], c));
assert_se(hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a)); assert_se(hashmap_get(c->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
unit_remove_dependencies(a, UNIT_DEPENDENCY_UDEV); unit_remove_dependencies(a, UNIT_DEPENDENCY_UDEV);
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b)); assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b));
assert_se(!hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a)); assert_se(!hashmap_get(b->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
assert_se(hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c)); assert_se(hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], c));
assert_se(hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a)); assert_se(hashmap_get(c->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
unit_remove_dependencies(a, UNIT_DEPENDENCY_PROC_SWAP); unit_remove_dependencies(a, UNIT_DEPENDENCY_PROC_SWAP);
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b)); assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b));
assert_se(!hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a)); assert_se(!hashmap_get(b->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c)); assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], c));
assert_se(!hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a)); assert_se(!hashmap_get(c->dependencies[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(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(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")); 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; return 0;
} }

View File

@ -1,34 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "tpm2-util.h"
#include "tests.h"
static void test_tpm2_parse_pcrs(const char *s, uint32_t mask, int ret) {
uint32_t m;
assert_se(tpm2_parse_pcrs(s, &m) == ret);
if (ret >= 0)
assert_se(m == mask);
}
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
test_tpm2_parse_pcrs("", 0, 0);
test_tpm2_parse_pcrs("0", 1, 0);
test_tpm2_parse_pcrs("1", 2, 0);
test_tpm2_parse_pcrs("0,1", 3, 0);
test_tpm2_parse_pcrs("0+1", 3, 0);
test_tpm2_parse_pcrs("0-1", 0, -EINVAL);
test_tpm2_parse_pcrs("0,1,2", 7, 0);
test_tpm2_parse_pcrs("0+1+2", 7, 0);
test_tpm2_parse_pcrs("0+1,2", 7, 0);
test_tpm2_parse_pcrs("0,1+2", 7, 0);
test_tpm2_parse_pcrs("0,2", 5, 0);
test_tpm2_parse_pcrs("0+2", 5, 0);
test_tpm2_parse_pcrs("foo", 0, -EINVAL);
return 0;
}

View File

@ -1 +0,0 @@
../TEST-01-BASIC/Makefile

View File

@ -1,7 +0,0 @@
#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="test OnSuccess= + Uphold= + PropagatesStopTo= + BindsTo="
. $TEST_BASE_DIR/test-functions
do_test "$@" 57

View File

@ -76,13 +76,10 @@ JoinsNamespaceOf=
OnFailure= OnFailure=
OnFailureIsolate= OnFailureIsolate=
OnFailureJobMode= OnFailureJobMode=
OnSuccess=
OnSuccessJobMode=
PartOf= PartOf=
PropagateReloadFrom= PropagateReloadFrom=
PropagateReloadTo= PropagateReloadTo=
PropagatesReloadTo= PropagatesReloadTo=
PropagatesStopTo=
RebootArgument= RebootArgument=
RefuseManualStart= RefuseManualStart=
RefuseManualStop= RefuseManualStop=
@ -100,10 +97,8 @@ StartLimitBurst=
StartLimitInterval= StartLimitInterval=
StartLimitIntervalSec= StartLimitIntervalSec=
StopWhenUnneeded= StopWhenUnneeded=
StopPropagatedFrom=
SuccessAction= SuccessAction=
SuccessActionExitStatus= SuccessActionExitStatus=
Upholds=
Wants= Wants=
[Install] [Install]
Alias= Alias=

View File

@ -1,8 +0,0 @@
[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

View File

@ -1,5 +0,0 @@
[Unit]
Description=Unit with BoundBy=
[Service]
ExecStart=/bin/sleep 0.7

View File

@ -1,6 +0,0 @@
[Unit]
Description=Failing unit
OnFailure=testsuite-57-uphold.service
[Service]
ExecStart=/bin/false

View File

@ -1,9 +0,0 @@
[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

View File

@ -1,5 +0,0 @@
[Unit]
Description=Stop Propagation Sender
[Service]
ExecStart=/bin/sleep 1.5

View File

@ -1,10 +0,0 @@
[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

View File

@ -1,18 +0,0 @@
#!/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

View File

@ -1,6 +0,0 @@
[Unit]
Description=Succeeding unit
OnSuccess=testsuite-57-fail.service
[Service]
ExecStart=/bin/true

View File

@ -1,6 +0,0 @@
[Unit]
Description=Upholding Unit
Upholds=testsuite-57-short-lived.service
[Service]
ExecStart=/bin/sleep infinity

View File

@ -1,7 +0,0 @@
[Unit]
Description=TEST-57-ONSUCCESS-UPHOLD
[Service]
ExecStartPre=rm -f /failed /testok
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
Type=oneshot

View File

@ -1,68 +0,0 @@
#!/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