core: split dependency types into atoms
This commit is contained in:
+15
-12
@@ -1700,12 +1700,14 @@ CGroupMask unit_get_members_mask(Unit *u) {
|
||||
u->cgroup_members_mask = 0;
|
||||
|
||||
if (u->type == UNIT_SLICE) {
|
||||
void *v;
|
||||
Unit *member;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE])
|
||||
if (UNIT_DEREF(member->slice) == u)
|
||||
u->cgroup_members_mask |= unit_get_subtree_mask(member); /* note that this calls ourselves again, for the children */
|
||||
UNIT_FOREACH_DEPENDENCY(member, u, UNIT_ATOM_BEFORE) {
|
||||
if (UNIT_DEREF(member->slice) != u)
|
||||
continue;
|
||||
|
||||
u->cgroup_members_mask |= unit_get_subtree_mask(member); /* note that this calls ourselves again, for the children */
|
||||
}
|
||||
}
|
||||
|
||||
u->cgroup_members_mask_valid = true;
|
||||
@@ -2343,14 +2345,13 @@ static int unit_realize_cgroup_now_enable(Unit *u, ManagerState state) {
|
||||
* hierarchy upwards to the unit in question. */
|
||||
static int unit_realize_cgroup_now_disable(Unit *u, ManagerState state) {
|
||||
Unit *m;
|
||||
void *v;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (u->type != UNIT_SLICE)
|
||||
return 0;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, m, u->dependencies[UNIT_BEFORE]) {
|
||||
UNIT_FOREACH_DEPENDENCY(m, u, UNIT_ATOM_BEFORE) {
|
||||
CGroupMask target_mask, enable_mask, new_target_mask, new_enable_mask;
|
||||
int r;
|
||||
|
||||
@@ -2515,12 +2516,12 @@ void unit_add_family_to_cgroup_realize_queue(Unit *u) {
|
||||
|
||||
do {
|
||||
Unit *m;
|
||||
void *v;
|
||||
|
||||
/* Children of u likely changed when we're called */
|
||||
u->cgroup_members_mask_valid = false;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, m, u->dependencies[UNIT_BEFORE]) {
|
||||
UNIT_FOREACH_DEPENDENCY(m, u, UNIT_ATOM_BEFORE) {
|
||||
|
||||
/* Skip units that have a dependency on the slice but aren't actually in it. */
|
||||
if (UNIT_DEREF(m->slice) != u)
|
||||
continue;
|
||||
@@ -3782,11 +3783,13 @@ void unit_invalidate_cgroup_bpf(Unit *u) {
|
||||
* list of our children includes our own. */
|
||||
if (u->type == UNIT_SLICE) {
|
||||
Unit *member;
|
||||
void *v;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE])
|
||||
if (UNIT_DEREF(member->slice) == u)
|
||||
unit_invalidate_cgroup_bpf(member);
|
||||
UNIT_FOREACH_DEPENDENCY(member, u, UNIT_ATOM_BEFORE) {
|
||||
if (UNIT_DEREF(member->slice) != u)
|
||||
continue;
|
||||
|
||||
unit_invalidate_cgroup_bpf(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+31
-25
@@ -155,21 +155,27 @@ static int property_get_dependencies(
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Hashmap **h = userdata;
|
||||
Unit *u;
|
||||
Unit *u = userdata, *other;
|
||||
UnitDependency d;
|
||||
Hashmap *deps;
|
||||
void *v;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
assert(h);
|
||||
assert(u);
|
||||
|
||||
d = unit_dependency_from_string(property);
|
||||
assert_se(d >= 0);
|
||||
|
||||
deps = unit_get_dependencies(u, d);
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "s");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, u, *h) {
|
||||
r = sd_bus_message_append(reply, "s", u->id);
|
||||
HASHMAP_FOREACH_KEY(v, other, deps) {
|
||||
r = sd_bus_message_append(reply, "s", other->id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@@ -844,26 +850,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("Names", "as", property_get_names, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Following", "s", property_get_following, 0, 0),
|
||||
SD_BUS_PROPERTY("Requires", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Requisite", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Wants", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTS]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("BindsTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BINDS_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PartOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PART_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RequiredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RequisiteOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("WantedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("BoundBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BOUND_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ConsistsOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Conflicts", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONFLICTS]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ConflictedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONFLICTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Before", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BEFORE]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("After", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_AFTER]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("OnFailure", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_ON_FAILURE]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Triggers", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_TRIGGERS]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("TriggeredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ReloadPropagatedFrom", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_JOINS_NAMESPACE_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Requires", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Requisite", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Wants", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("BindsTo", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PartOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RequiredBy", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RequisiteOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("WantedBy", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("BoundBy", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ConsistsOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Conflicts", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ConflictedBy", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Before", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("After", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("OnFailure", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Triggers", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("TriggeredBy", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ReloadPropagatedFrom", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RequiresMountsFor", "as", property_get_requires_mounts_for, offsetof(Unit, requires_mounts_for), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
|
||||
+1
-1
@@ -468,7 +468,7 @@ static void device_upgrade_mount_deps(Unit *u) {
|
||||
|
||||
/* Let's upgrade Requires= to BindsTo= on us. (Used when SYSTEMD_MOUNT_DEVICE_BOUND is set) */
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUIRED_BY]) {
|
||||
HASHMAP_FOREACH_KEY(v, other, unit_get_dependencies(u, UNIT_REQUIRED_BY)) {
|
||||
if (other->type != UNIT_MOUNT)
|
||||
continue;
|
||||
|
||||
|
||||
+38
-46
@@ -455,7 +455,6 @@ int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u) {
|
||||
|
||||
static bool job_is_runnable(Job *j) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
assert(j);
|
||||
assert(j->installed);
|
||||
@@ -477,16 +476,16 @@ static bool job_is_runnable(Job *j) {
|
||||
if (j->type == JOB_NOP)
|
||||
return true;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER])
|
||||
if (other->job && job_compare(j, other->job, UNIT_AFTER) > 0) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_AFTER)
|
||||
if (other->job && job_compare(j, other->job, UNIT_ATOM_AFTER) > 0) {
|
||||
log_unit_debug(j->unit,
|
||||
"starting held back, waiting for: %s",
|
||||
other->id);
|
||||
return false;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE])
|
||||
if (other->job && job_compare(j, other->job, UNIT_BEFORE) > 0) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_BEFORE)
|
||||
if (other->job && job_compare(j, other->job, UNIT_ATOM_BEFORE) > 0) {
|
||||
log_unit_debug(j->unit,
|
||||
"stopping held back, waiting for: %s",
|
||||
other->id);
|
||||
@@ -951,13 +950,12 @@ static void job_emit_done_status_message(Unit *u, uint32_t job_id, JobType t, Jo
|
||||
job_print_done_status_message(u, t, result);
|
||||
}
|
||||
|
||||
static void job_fail_dependencies(Unit *u, UnitDependency d) {
|
||||
static void job_fail_dependencies(Unit *u, UnitDependencyAtom match_atom) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
assert(u);
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[d]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, match_atom) {
|
||||
Job *j = other->job;
|
||||
|
||||
if (!j)
|
||||
@@ -970,10 +968,8 @@ static void job_fail_dependencies(Unit *u, UnitDependency d) {
|
||||
}
|
||||
|
||||
int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already) {
|
||||
Unit *u;
|
||||
Unit *other;
|
||||
Unit *u, *other;
|
||||
JobType t;
|
||||
void *v;
|
||||
|
||||
assert(j);
|
||||
assert(j->installed);
|
||||
@@ -1012,12 +1008,10 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr
|
||||
|
||||
/* Fail depending jobs on failure */
|
||||
if (result != JOB_DONE && recursive) {
|
||||
if (IN_SET(t, JOB_START, JOB_VERIFY_ACTIVE)) {
|
||||
job_fail_dependencies(u, UNIT_REQUIRED_BY);
|
||||
job_fail_dependencies(u, UNIT_REQUISITE_OF);
|
||||
job_fail_dependencies(u, UNIT_BOUND_BY);
|
||||
} else if (t == JOB_STOP)
|
||||
job_fail_dependencies(u, UNIT_CONFLICTED_BY);
|
||||
if (IN_SET(t, JOB_START, JOB_VERIFY_ACTIVE))
|
||||
job_fail_dependencies(u, UNIT_ATOM_PROPAGATE_START_FAILURE);
|
||||
else if (t == JOB_STOP)
|
||||
job_fail_dependencies(u, UNIT_ATOM_PROPAGATE_STOP_FAILURE);
|
||||
}
|
||||
|
||||
/* A special check to make sure we take down anything RequisiteOf if we
|
||||
@@ -1041,14 +1035,13 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr
|
||||
* not happen (the verify-active job may trigger after it finishes), so
|
||||
* you get undeterministic results without this check.
|
||||
*/
|
||||
if (result == JOB_DONE && recursive && !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) {
|
||||
if (IN_SET(t, JOB_START, JOB_RELOAD))
|
||||
job_fail_dependencies(u, UNIT_REQUISITE_OF);
|
||||
}
|
||||
/* Trigger OnFailure dependencies that are not generated by
|
||||
* the unit itself. We don't treat JOB_CANCELED as failure in
|
||||
* this context. And JOB_FAILURE is already handled by the
|
||||
* unit itself. */
|
||||
if (result == JOB_DONE && recursive &&
|
||||
IN_SET(t, JOB_START, JOB_RELOAD) &&
|
||||
!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u)))
|
||||
job_fail_dependencies(u, UNIT_ATOM_PROPAGATE_INACTIVE_START_AS_FAILURE);
|
||||
|
||||
/* Trigger OnFailure= dependencies that are not generated by the unit itself. We don't treat
|
||||
* JOB_CANCELED as failure in this context. And JOB_FAILURE is already handled by the unit itself. */
|
||||
if (IN_SET(result, JOB_TIMEOUT, JOB_DEPENDENCY)) {
|
||||
log_unit_struct(u, LOG_NOTICE,
|
||||
"JOB_TYPE=%s", job_type_to_string(t),
|
||||
@@ -1065,12 +1058,12 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr
|
||||
|
||||
finish:
|
||||
/* Try to start the next jobs that can be started */
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_AFTER])
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_AFTER)
|
||||
if (other->job) {
|
||||
job_add_to_run_queue(other->job);
|
||||
job_add_to_gc_queue(other->job);
|
||||
}
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BEFORE])
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_BEFORE)
|
||||
if (other->job) {
|
||||
job_add_to_run_queue(other->job);
|
||||
job_add_to_gc_queue(other->job);
|
||||
@@ -1420,7 +1413,6 @@ int job_get_timeout(Job *j, usec_t *timeout) {
|
||||
|
||||
bool job_may_gc(Job *j) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
assert(j);
|
||||
|
||||
@@ -1449,12 +1441,12 @@ bool job_may_gc(Job *j) {
|
||||
return false;
|
||||
|
||||
/* The logic is inverse to job_is_runnable, we cannot GC as long as we block any job. */
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE])
|
||||
if (other->job && job_compare(j, other->job, UNIT_BEFORE) < 0)
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_BEFORE)
|
||||
if (other->job && job_compare(j, other->job, UNIT_ATOM_BEFORE) < 0)
|
||||
return false;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER])
|
||||
if (other->job && job_compare(j, other->job, UNIT_AFTER) < 0)
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_AFTER)
|
||||
if (other->job && job_compare(j, other->job, UNIT_ATOM_AFTER) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -1500,7 +1492,6 @@ int job_get_before(Job *j, Job*** ret) {
|
||||
_cleanup_free_ Job** list = NULL;
|
||||
Unit *other = NULL;
|
||||
size_t n = 0;
|
||||
void *v;
|
||||
|
||||
/* Returns a list of all pending jobs that need to finish before this job may be started. */
|
||||
|
||||
@@ -1512,10 +1503,10 @@ int job_get_before(Job *j, Job*** ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_AFTER) {
|
||||
if (!other->job)
|
||||
continue;
|
||||
if (job_compare(j, other->job, UNIT_AFTER) <= 0)
|
||||
if (job_compare(j, other->job, UNIT_ATOM_AFTER) <= 0)
|
||||
continue;
|
||||
|
||||
if (!GREEDY_REALLOC(list, n+1))
|
||||
@@ -1523,10 +1514,10 @@ int job_get_before(Job *j, Job*** ret) {
|
||||
list[n++] = other->job;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_BEFORE) {
|
||||
if (!other->job)
|
||||
continue;
|
||||
if (job_compare(j, other->job, UNIT_BEFORE) <= 0)
|
||||
if (job_compare(j, other->job, UNIT_ATOM_BEFORE) <= 0)
|
||||
continue;
|
||||
|
||||
if (!GREEDY_REALLOC(list, n+1))
|
||||
@@ -1545,21 +1536,20 @@ int job_get_after(Job *j, Job*** ret) {
|
||||
_cleanup_free_ Job** list = NULL;
|
||||
Unit *other = NULL;
|
||||
size_t n = 0;
|
||||
void *v;
|
||||
|
||||
assert(j);
|
||||
assert(ret);
|
||||
|
||||
/* Returns a list of all pending jobs that are waiting for this job to finish. */
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_BEFORE) {
|
||||
if (!other->job)
|
||||
continue;
|
||||
|
||||
if (other->job->ignore_order)
|
||||
continue;
|
||||
|
||||
if (job_compare(j, other->job, UNIT_BEFORE) >= 0)
|
||||
if (job_compare(j, other->job, UNIT_ATOM_BEFORE) >= 0)
|
||||
continue;
|
||||
|
||||
if (!GREEDY_REALLOC(list, n+1))
|
||||
@@ -1567,14 +1557,14 @@ int job_get_after(Job *j, Job*** ret) {
|
||||
list[n++] = other->job;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, j->unit, UNIT_ATOM_AFTER) {
|
||||
if (!other->job)
|
||||
continue;
|
||||
|
||||
if (other->job->ignore_order)
|
||||
continue;
|
||||
|
||||
if (job_compare(j, other->job, UNIT_AFTER) >= 0)
|
||||
if (job_compare(j, other->job, UNIT_ATOM_AFTER) >= 0)
|
||||
continue;
|
||||
|
||||
if (!GREEDY_REALLOC(list, n+1))
|
||||
@@ -1669,10 +1659,12 @@ const char* job_type_to_access_method(JobType t) {
|
||||
* This has the side effect that restarts are properly
|
||||
* synchronized too.
|
||||
*/
|
||||
int job_compare(Job *a, Job *b, UnitDependency assume_dep) {
|
||||
int job_compare(Job *a, Job *b, UnitDependencyAtom assume_dep) {
|
||||
assert(a);
|
||||
assert(b);
|
||||
assert(a->type < _JOB_TYPE_MAX_IN_TRANSACTION);
|
||||
assert(b->type < _JOB_TYPE_MAX_IN_TRANSACTION);
|
||||
assert(IN_SET(assume_dep, UNIT_AFTER, UNIT_BEFORE));
|
||||
assert(IN_SET(assume_dep, UNIT_ATOM_AFTER, UNIT_ATOM_BEFORE));
|
||||
|
||||
/* Trivial cases first */
|
||||
if (a->type == JOB_NOP || b->type == JOB_NOP)
|
||||
@@ -1681,8 +1673,8 @@ int job_compare(Job *a, Job *b, UnitDependency assume_dep) {
|
||||
if (a->ignore_order || b->ignore_order)
|
||||
return 0;
|
||||
|
||||
if (assume_dep == UNIT_AFTER)
|
||||
return -job_compare(b, a, UNIT_BEFORE);
|
||||
if (assume_dep == UNIT_ATOM_AFTER)
|
||||
return -job_compare(b, a, UNIT_ATOM_BEFORE);
|
||||
|
||||
/* Let's make it simple, JOB_STOP goes always first (in case both ua and ub stop,
|
||||
* then ub's stop goes first anyway).
|
||||
|
||||
+3
-1
@@ -6,7 +6,9 @@
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "list.h"
|
||||
#include "unit-dependency-atom.h"
|
||||
#include "unit-name.h"
|
||||
#include "unit.h"
|
||||
|
||||
typedef struct Job Job;
|
||||
typedef struct JobDependency JobDependency;
|
||||
@@ -240,4 +242,4 @@ JobResult job_result_from_string(const char *s) _pure_;
|
||||
|
||||
const char* job_type_to_access_method(JobType t);
|
||||
|
||||
int job_compare(Job *a, Job *b, UnitDependency assume_dep);
|
||||
int job_compare(Job *a, Job *b, UnitDependencyAtom assume_dep);
|
||||
|
||||
+15
-19
@@ -1141,12 +1141,11 @@ enum {
|
||||
|
||||
static void unit_gc_mark_good(Unit *u, unsigned gc_marker) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
u->gc_marker = gc_marker + GC_OFFSET_GOOD;
|
||||
|
||||
/* Recursively mark referenced units as GOOD as well */
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REFERENCES])
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_REFERENCES)
|
||||
if (other->gc_marker == gc_marker + GC_OFFSET_UNSURE)
|
||||
unit_gc_mark_good(other, gc_marker);
|
||||
}
|
||||
@@ -1154,7 +1153,6 @@ static void unit_gc_mark_good(Unit *u, unsigned gc_marker) {
|
||||
static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
|
||||
Unit *other;
|
||||
bool is_bad;
|
||||
void *v;
|
||||
|
||||
assert(u);
|
||||
|
||||
@@ -1172,7 +1170,7 @@ static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
|
||||
|
||||
is_bad = true;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REFERENCED_BY]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_REFERENCED_BY) {
|
||||
unit_gc_sweep(other, gc_marker);
|
||||
|
||||
if (other->gc_marker == gc_marker + GC_OFFSET_GOOD)
|
||||
@@ -1870,30 +1868,28 @@ static int manager_dispatch_target_deps_queue(Manager *m) {
|
||||
Unit *u;
|
||||
int r = 0;
|
||||
|
||||
static const UnitDependency deps[] = {
|
||||
UNIT_REQUIRED_BY,
|
||||
UNIT_REQUISITE_OF,
|
||||
UNIT_WANTED_BY,
|
||||
UNIT_BOUND_BY
|
||||
};
|
||||
|
||||
assert(m);
|
||||
|
||||
while ((u = m->target_deps_queue)) {
|
||||
_cleanup_free_ Unit **targets = NULL;
|
||||
int n_targets;
|
||||
|
||||
assert(u->in_target_deps_queue);
|
||||
|
||||
LIST_REMOVE(target_deps_queue, u->manager->target_deps_queue, u);
|
||||
u->in_target_deps_queue = false;
|
||||
|
||||
for (size_t k = 0; k < ELEMENTSOF(deps); k++) {
|
||||
Unit *target;
|
||||
void *v;
|
||||
/* Take an "atomic" snapshot of dependencies here, as the call below will likely modify the
|
||||
* dependencies, and we can't have it that hash tables we iterate through are modified while
|
||||
* we are iterating through them. */
|
||||
n_targets = unit_get_dependency_array(u, UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES, &targets);
|
||||
if (n_targets < 0)
|
||||
return n_targets;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, target, u->dependencies[deps[k]]) {
|
||||
r = unit_add_default_target_dependency(u, target);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
for (int i = 0; i < n_targets; i++) {
|
||||
r = unit_add_default_target_dependency(u, targets[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,8 @@ libcore_sources = '''
|
||||
timer.h
|
||||
transaction.c
|
||||
transaction.h
|
||||
unit-dependency-atom.c
|
||||
unit-dependency-atom.h
|
||||
unit-printf.c
|
||||
unit-printf.h
|
||||
unit-serialize.c
|
||||
|
||||
+1
-2
@@ -1245,12 +1245,11 @@ static int service_collect_fds(
|
||||
|
||||
rn_socket_fds = 1;
|
||||
} else {
|
||||
void *v;
|
||||
Unit *u;
|
||||
|
||||
/* Pass all our configured sockets for singleton services */
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY]) {
|
||||
UNIT_FOREACH_DEPENDENCY(u, UNIT(s), UNIT_ATOM_TRIGGERED_BY) {
|
||||
_cleanup_free_ int *cfds = NULL;
|
||||
Socket *sock;
|
||||
int cn_fds;
|
||||
|
||||
+2
-5
@@ -346,11 +346,10 @@ static void slice_enumerate_perpetual(Manager *m) {
|
||||
|
||||
static bool slice_freezer_action_supported_by_children(Unit *s) {
|
||||
Unit *member;
|
||||
void *v;
|
||||
|
||||
assert(s);
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE]) {
|
||||
UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_BEFORE) {
|
||||
int r;
|
||||
|
||||
if (UNIT_DEREF(member->slice) != s)
|
||||
@@ -371,7 +370,6 @@ static bool slice_freezer_action_supported_by_children(Unit *s) {
|
||||
|
||||
static int slice_freezer_action(Unit *s, FreezerAction action) {
|
||||
Unit *member;
|
||||
void *v;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
@@ -382,7 +380,7 @@ static int slice_freezer_action(Unit *s, FreezerAction action) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE]) {
|
||||
UNIT_FOREACH_DEPENDENCY(member, s, UNIT_ATOM_BEFORE) {
|
||||
if (UNIT_DEREF(member->slice) != s)
|
||||
continue;
|
||||
|
||||
@@ -390,7 +388,6 @@ static int slice_freezer_action(Unit *s, FreezerAction action) {
|
||||
r = UNIT_VTABLE(member)->freeze(member);
|
||||
else
|
||||
r = UNIT_VTABLE(member)->thaw(member);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
+2
-4
@@ -2340,11 +2340,9 @@ static void socket_enter_running(Socket *s, int cfd_in) {
|
||||
if (cfd < 0) {
|
||||
bool pending = false;
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
/* If there's already a start pending don't bother to
|
||||
* do anything */
|
||||
HASHMAP_FOREACH_KEY(v, other, UNIT(s)->dependencies[UNIT_TRIGGERS])
|
||||
/* If there's already a start pending don't bother to do anything */
|
||||
UNIT_FOREACH_DEPENDENCY(other, UNIT(s), UNIT_ATOM_TRIGGERS)
|
||||
if (unit_active_or_pending(other)) {
|
||||
pending = true;
|
||||
break;
|
||||
|
||||
+17
-20
@@ -35,34 +35,31 @@ static void target_set_state(Target *t, TargetState state) {
|
||||
}
|
||||
|
||||
static int target_add_default_dependencies(Target *t) {
|
||||
|
||||
static const UnitDependency deps[] = {
|
||||
UNIT_REQUIRES,
|
||||
UNIT_REQUISITE,
|
||||
UNIT_WANTS,
|
||||
UNIT_BINDS_TO,
|
||||
UNIT_PART_OF
|
||||
};
|
||||
|
||||
int r;
|
||||
_cleanup_free_ Unit **others = NULL;
|
||||
int r, n_others;
|
||||
|
||||
assert(t);
|
||||
|
||||
if (!UNIT(t)->default_dependencies)
|
||||
return 0;
|
||||
|
||||
/* Imply ordering for requirement dependencies on target units. Note that when the user created a contradicting
|
||||
* ordering manually we won't add anything in here to make sure we don't create a loop. */
|
||||
/* Imply ordering for requirement dependencies on target units. Note that when the user created a
|
||||
* contradicting ordering manually we won't add anything in here to make sure we don't create a
|
||||
* loop.
|
||||
*
|
||||
* Note that quite likely iterating through these dependencies will add new dependencies, which
|
||||
* conflicts with the hashmap-based iteration logic. Hence, instead of iterating through the
|
||||
* dependencies and acting on them as we go, first take an "atomic snapshot" of sorts and iterate
|
||||
* through that. */
|
||||
|
||||
for (size_t k = 0; k < ELEMENTSOF(deps); k++) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
n_others = unit_get_dependency_array(UNIT(t), UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE, &others);
|
||||
if (n_others < 0)
|
||||
return n_others;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, UNIT(t)->dependencies[deps[k]]) {
|
||||
r = unit_add_default_target_dependency(other, UNIT(t));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
for (int i = 0; i < n_others; i++) {
|
||||
r = unit_add_default_target_dependency(others[i], UNIT(t));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (unit_has_name(UNIT(t), SPECIAL_SHUTDOWN_TARGET))
|
||||
|
||||
+38
-53
@@ -347,14 +347,13 @@ static char* merge_unit_ids(const char* unit_log_field, char **pairs) {
|
||||
}
|
||||
|
||||
static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, sd_bus_error *e) {
|
||||
Unit *u;
|
||||
void *v;
|
||||
int r;
|
||||
static const UnitDependency directions[] = {
|
||||
UNIT_BEFORE,
|
||||
UNIT_AFTER,
|
||||
|
||||
static const UnitDependencyAtom directions[] = {
|
||||
UNIT_ATOM_BEFORE,
|
||||
UNIT_ATOM_AFTER,
|
||||
};
|
||||
size_t d;
|
||||
|
||||
int r;
|
||||
|
||||
assert(tr);
|
||||
assert(j);
|
||||
@@ -449,8 +448,10 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
|
||||
* the graph over 'before' edges in the actual job execution order. We traverse over both unit
|
||||
* ordering dependencies and we test with job_compare() whether it is the 'before' edge in the job
|
||||
* execution ordering. */
|
||||
for (d = 0; d < ELEMENTSOF(directions); d++) {
|
||||
HASHMAP_FOREACH_KEY(v, u, j->unit->dependencies[directions[d]]) {
|
||||
for (size_t d = 0; d < ELEMENTSOF(directions); d++) {
|
||||
Unit *u;
|
||||
|
||||
UNIT_FOREACH_DEPENDENCY(u, j->unit, directions[d]) {
|
||||
Job *o;
|
||||
|
||||
/* Is there a job for this unit? */
|
||||
@@ -879,13 +880,12 @@ static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependen
|
||||
void transaction_add_propagate_reload_jobs(Transaction *tr, Unit *unit, Job *by, bool ignore_order, sd_bus_error *e) {
|
||||
JobType nt;
|
||||
Unit *dep;
|
||||
void *v;
|
||||
int r;
|
||||
|
||||
assert(tr);
|
||||
assert(unit);
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, dep, unit->dependencies[UNIT_PROPAGATES_RELOAD_TO]) {
|
||||
UNIT_FOREACH_DEPENDENCY(dep, unit, UNIT_ATOM_PROPAGATES_RELOAD_TO) {
|
||||
nt = job_type_collapse(JOB_TRY_RELOAD, dep);
|
||||
if (nt == JOB_NOP)
|
||||
continue;
|
||||
@@ -914,7 +914,6 @@ int transaction_add_job_and_dependencies(
|
||||
bool is_new;
|
||||
Unit *dep;
|
||||
Job *ret;
|
||||
void *v;
|
||||
int r;
|
||||
|
||||
assert(tr);
|
||||
@@ -1006,7 +1005,7 @@ int transaction_add_job_and_dependencies(
|
||||
|
||||
/* Finally, recursively add in all dependencies. */
|
||||
if (IN_SET(type, JOB_START, JOB_RESTART)) {
|
||||
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_REQUIRES]) {
|
||||
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_START) {
|
||||
r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
if (r != -EBADR) /* job type not applicable */
|
||||
@@ -1016,17 +1015,7 @@ int transaction_add_job_and_dependencies(
|
||||
}
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_BINDS_TO]) {
|
||||
r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
if (r != -EBADR) /* job type not applicable */
|
||||
goto fail;
|
||||
|
||||
sd_bus_error_free(e);
|
||||
}
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_WANTS]) {
|
||||
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_START_IGNORED) {
|
||||
r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
/* unit masked, job type not applicable and unit not found are not considered as errors. */
|
||||
@@ -1038,7 +1027,7 @@ int transaction_add_job_and_dependencies(
|
||||
}
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_REQUISITE]) {
|
||||
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_VERIFY) {
|
||||
r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, false, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
if (r != -EBADR) /* job type not applicable */
|
||||
@@ -1048,7 +1037,7 @@ int transaction_add_job_and_dependencies(
|
||||
}
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_CONFLICTS]) {
|
||||
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_STOP) {
|
||||
r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, true, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
if (r != -EBADR) /* job type not applicable */
|
||||
@@ -1058,7 +1047,7 @@ int transaction_add_job_and_dependencies(
|
||||
}
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_CONFLICTED_BY]) {
|
||||
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_STOP_IGNORED) {
|
||||
r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, false, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
log_unit_warning(dep,
|
||||
@@ -1067,41 +1056,37 @@ int transaction_add_job_and_dependencies(
|
||||
sd_bus_error_free(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (IN_SET(type, JOB_STOP, JOB_RESTART)) {
|
||||
static const UnitDependency propagate_deps[] = {
|
||||
UNIT_REQUIRED_BY,
|
||||
UNIT_REQUISITE_OF,
|
||||
UNIT_BOUND_BY,
|
||||
UNIT_CONSISTS_OF,
|
||||
};
|
||||
|
||||
UnitDependencyAtom atom;
|
||||
JobType ptype;
|
||||
unsigned j;
|
||||
|
||||
/* We propagate STOP as STOP, but RESTART only
|
||||
* as TRY_RESTART, in order not to start
|
||||
/* We propagate STOP as STOP, but RESTART only as TRY_RESTART, in order not to start
|
||||
* dependencies that are not around. */
|
||||
ptype = type == JOB_RESTART ? JOB_TRY_RESTART : type;
|
||||
if (type == JOB_RESTART) {
|
||||
atom = UNIT_ATOM_PROPAGATE_RESTART;
|
||||
ptype = JOB_TRY_RESTART;
|
||||
} else {
|
||||
ptype = JOB_STOP;
|
||||
atom = UNIT_ATOM_PROPAGATE_STOP;
|
||||
}
|
||||
|
||||
for (j = 0; j < ELEMENTSOF(propagate_deps); j++)
|
||||
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[propagate_deps[j]]) {
|
||||
JobType nt;
|
||||
UNIT_FOREACH_DEPENDENCY(dep, ret->unit, atom) {
|
||||
JobType nt;
|
||||
|
||||
nt = job_type_collapse(ptype, dep);
|
||||
if (nt == JOB_NOP)
|
||||
continue;
|
||||
nt = job_type_collapse(ptype, dep);
|
||||
if (nt == JOB_NOP)
|
||||
continue;
|
||||
|
||||
r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
if (r != -EBADR) /* job type not applicable */
|
||||
goto fail;
|
||||
r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e);
|
||||
if (r < 0) {
|
||||
if (r != -EBADR) /* job type not applicable */
|
||||
goto fail;
|
||||
|
||||
sd_bus_error_free(e);
|
||||
}
|
||||
sd_bus_error_free(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type == JOB_RELOAD)
|
||||
@@ -1150,14 +1135,14 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
|
||||
}
|
||||
|
||||
int transaction_add_triggering_jobs(Transaction *tr, Unit *u) {
|
||||
void *v;
|
||||
Unit *trigger;
|
||||
int r;
|
||||
|
||||
assert(tr);
|
||||
assert(u);
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, trigger, u->dependencies[UNIT_TRIGGERED_BY]) {
|
||||
UNIT_FOREACH_DEPENDENCY(trigger, u, UNIT_ATOM_TRIGGERED_BY) {
|
||||
|
||||
/* No need to stop inactive jobs */
|
||||
if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(trigger)) && !trigger->job)
|
||||
continue;
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/* 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_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_DEFAULT_TARGET_DEPENDENCIES,
|
||||
|
||||
[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,
|
||||
|
||||
/* These are simple dependency types: they consist of a single atom only */
|
||||
[UNIT_BEFORE] = UNIT_ATOM_BEFORE,
|
||||
[UNIT_AFTER] = UNIT_ATOM_AFTER,
|
||||
[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,
|
||||
|
||||
/* 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,
|
||||
};
|
||||
|
||||
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_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_DEFAULT_TARGET_DEPENDENCIES:
|
||||
case UNIT_ATOM_RETROACTIVE_STOP_ON_STOP:
|
||||
return UNIT_BOUND_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_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;
|
||||
|
||||
default:
|
||||
return _UNIT_DEPENDENCY_INVALID;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/* 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 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_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_DEPENDENCY_ATOM_MAX = (UINT64_C(1) << 32) - 1,
|
||||
_UNIT_DEPENDENCY_ATOM_INVALID = -EINVAL,
|
||||
} UnitDependencyAtom;
|
||||
|
||||
UnitDependencyAtom unit_dependency_to_atom(UnitDependency d);
|
||||
UnitDependency unit_dependency_from_unique_atom(UnitDependencyAtom atom);
|
||||
@@ -735,7 +735,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
UnitDependencyInfo di;
|
||||
Unit *other;
|
||||
|
||||
HASHMAP_FOREACH_KEY(di.data, other, u->dependencies[d]) {
|
||||
HASHMAP_FOREACH_KEY(di.data, other, unit_get_dependencies(u, d)) {
|
||||
bool space = false;
|
||||
|
||||
fprintf(f, "%s\t%s: %s (", prefix, unit_dependency_to_string(d), other->id);
|
||||
|
||||
+379
-282
@@ -85,8 +85,6 @@ const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
|
||||
[UNIT_SCOPE] = &scope_vtable,
|
||||
};
|
||||
|
||||
static void maybe_warn_about_dependency(Unit *u, const char *other, UnitDependency dependency);
|
||||
|
||||
Unit* unit_new(Manager *m, size_t size) {
|
||||
Unit *u;
|
||||
|
||||
@@ -508,22 +506,26 @@ void unit_submit_to_stop_when_unneeded_queue(Unit *u) {
|
||||
u->in_stop_when_unneeded_queue = true;
|
||||
}
|
||||
|
||||
static void bidi_set_free(Unit *u, Hashmap *h) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
static void unit_clear_dependencies(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
/* Frees the hashmap and makes sure we are dropped from the inverse pointers */
|
||||
/* Removes all dependencies configured on u and their reverse dependencies. */
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, h) {
|
||||
for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
|
||||
hashmap_remove(other->dependencies[d], u);
|
||||
for (Hashmap *deps; (deps = hashmap_steal_first(u->dependencies));) {
|
||||
|
||||
unit_add_to_gc_queue(other);
|
||||
for (Unit *other; (other = hashmap_steal_first_key(deps));) {
|
||||
Hashmap *other_deps;
|
||||
|
||||
HASHMAP_FOREACH(other_deps, other->dependencies)
|
||||
hashmap_remove(other_deps, u);
|
||||
|
||||
unit_add_to_gc_queue(other);
|
||||
}
|
||||
|
||||
hashmap_free(deps);
|
||||
}
|
||||
|
||||
hashmap_free(h);
|
||||
u->dependencies = hashmap_free(u->dependencies);
|
||||
}
|
||||
|
||||
static void unit_remove_transient(Unit *u) {
|
||||
@@ -656,8 +658,7 @@ Unit* unit_free(Unit *u) {
|
||||
job_free(j);
|
||||
}
|
||||
|
||||
for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
|
||||
bidi_set_free(u, u->dependencies[d]);
|
||||
unit_clear_dependencies(u);
|
||||
|
||||
/* A unit is being dropped from the tree, make sure our family is realized properly. Do this after we
|
||||
* detach the unit from slice tree in order to eliminate its effect on controller masks. */
|
||||
@@ -809,22 +810,7 @@ const char* unit_sub_state_to_string(Unit *u) {
|
||||
return UNIT_VTABLE(u)->sub_state_to_string(u);
|
||||
}
|
||||
|
||||
static int hashmap_complete_move(Hashmap **s, Hashmap **other) {
|
||||
assert(s);
|
||||
assert(other);
|
||||
|
||||
if (!*other)
|
||||
return 0;
|
||||
|
||||
if (*s)
|
||||
return hashmap_move(*s, *other);
|
||||
else
|
||||
*s = TAKE_PTR(*other);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int merge_names(Unit *u, Unit *other) {
|
||||
static int unit_merge_names(Unit *u, Unit *other) {
|
||||
char *name;
|
||||
int r;
|
||||
|
||||
@@ -850,87 +836,252 @@ static int merge_names(Unit *u, Unit *other) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reserve_dependencies(Unit *u, Unit *other, UnitDependency d) {
|
||||
unsigned n_reserve;
|
||||
|
||||
assert(u);
|
||||
assert(other);
|
||||
assert(d < _UNIT_DEPENDENCY_MAX);
|
||||
|
||||
/*
|
||||
* If u does not have this dependency set allocated, there is no need
|
||||
* to reserve anything. In that case other's set will be transferred
|
||||
* as a whole to u by complete_move().
|
||||
*/
|
||||
if (!u->dependencies[d])
|
||||
return 0;
|
||||
|
||||
/* merge_dependencies() will skip a u-on-u dependency */
|
||||
n_reserve = hashmap_size(other->dependencies[d]) - !!hashmap_get(other->dependencies[d], u);
|
||||
|
||||
return hashmap_reserve(u->dependencies[d], n_reserve);
|
||||
}
|
||||
|
||||
static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitDependency d) {
|
||||
Unit *back;
|
||||
void *v;
|
||||
static int unit_reserve_dependencies(Unit *u, Unit *other) {
|
||||
size_t n_reserve;
|
||||
Hashmap* deps;
|
||||
void *d;
|
||||
int r;
|
||||
|
||||
/* Merges all dependencies of type 'd' of the unit 'other' into the deps of the unit 'u' */
|
||||
assert(u);
|
||||
assert(other);
|
||||
|
||||
/* Let's reserve some space in the dependency hashmaps so that later on merging the units cannot
|
||||
* fail.
|
||||
*
|
||||
* First make some room in the per dependency type hashmaps. Using the summed size of both unit's
|
||||
* hashmaps is an estimate that is likely too high since they probably use some of the same
|
||||
* types. But it's never too low, and that's all we need. */
|
||||
|
||||
n_reserve = MIN(hashmap_size(other->dependencies), LESS_BY((size_t) _UNIT_DEPENDENCY_MAX, hashmap_size(u->dependencies)));
|
||||
if (n_reserve > 0) {
|
||||
r = hashmap_ensure_allocated(&u->dependencies, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = hashmap_reserve(u->dependencies, n_reserve);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Now, enlarge our per dependency type hashmaps by the number of entries in the same hashmap of the
|
||||
* other unit's dependencies.
|
||||
*
|
||||
* NB: If u does not have a dependency set allocated for some dependency type, there is no need to
|
||||
* reserve anything for. In that case other's set will be transferred as a whole to u by
|
||||
* complete_move(). */
|
||||
|
||||
HASHMAP_FOREACH_KEY(deps, d, u->dependencies) {
|
||||
Hashmap *other_deps;
|
||||
|
||||
other_deps = hashmap_get(other->dependencies, d);
|
||||
|
||||
r = hashmap_reserve(deps, hashmap_size(other_deps));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unit_maybe_warn_about_dependency(
|
||||
Unit *u,
|
||||
const char *other_id,
|
||||
UnitDependency dependency) {
|
||||
|
||||
assert(u);
|
||||
|
||||
/* Only warn about some unit types */
|
||||
if (!IN_SET(dependency,
|
||||
UNIT_CONFLICTS,
|
||||
UNIT_CONFLICTED_BY,
|
||||
UNIT_BEFORE,
|
||||
UNIT_AFTER,
|
||||
UNIT_ON_FAILURE,
|
||||
UNIT_TRIGGERS,
|
||||
UNIT_TRIGGERED_BY))
|
||||
return;
|
||||
|
||||
if (streq_ptr(u->id, other_id))
|
||||
log_unit_warning(u, "Dependency %s=%s dropped", unit_dependency_to_string(dependency), u->id);
|
||||
else
|
||||
log_unit_warning(u, "Dependency %s=%s dropped, merged into %s", unit_dependency_to_string(dependency), strna(other_id), u->id);
|
||||
}
|
||||
|
||||
static int unit_per_dependency_type_hashmap_update(
|
||||
Hashmap *per_type,
|
||||
Unit *other,
|
||||
UnitDependencyMask origin_mask,
|
||||
UnitDependencyMask destination_mask) {
|
||||
|
||||
UnitDependencyInfo info;
|
||||
int r;
|
||||
|
||||
assert(other);
|
||||
assert_cc(sizeof(void*) == sizeof(info));
|
||||
|
||||
/* Acquire the UnitDependencyInfo entry for the Unit* we are interested in, and update it if it
|
||||
* exists, or insert it anew if not. */
|
||||
|
||||
info.data = hashmap_get(per_type, other);
|
||||
if (info.data) {
|
||||
/* Entry already exists. Add in our mask. */
|
||||
|
||||
if (FLAGS_SET(origin_mask, info.origin_mask) &&
|
||||
FLAGS_SET(destination_mask, info.destination_mask))
|
||||
return 0; /* NOP */
|
||||
|
||||
info.origin_mask |= origin_mask;
|
||||
info.destination_mask |= destination_mask;
|
||||
|
||||
r = hashmap_update(per_type, other, info.data);
|
||||
} else {
|
||||
info = (UnitDependencyInfo) {
|
||||
.origin_mask = origin_mask,
|
||||
.destination_mask = destination_mask,
|
||||
};
|
||||
|
||||
r = hashmap_put(per_type, other, info.data);
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int unit_add_dependency_hashmap(
|
||||
Hashmap **dependencies,
|
||||
UnitDependency d,
|
||||
Unit *other,
|
||||
UnitDependencyMask origin_mask,
|
||||
UnitDependencyMask destination_mask) {
|
||||
|
||||
Hashmap *per_type;
|
||||
int r;
|
||||
|
||||
assert(dependencies);
|
||||
assert(other);
|
||||
assert(origin_mask < _UNIT_DEPENDENCY_MASK_FULL);
|
||||
assert(destination_mask < _UNIT_DEPENDENCY_MASK_FULL);
|
||||
assert(origin_mask > 0 || destination_mask > 0);
|
||||
|
||||
/* Ensure the top-level dependency hashmap exists that maps UnitDependency → Hashmap(Unit* →
|
||||
* UnitDependencyInfo) */
|
||||
r = hashmap_ensure_allocated(dependencies, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Acquire the inner hashmap, that maps Unit* → UnitDependencyInfo, for the specified dependency
|
||||
* type, and if it's missing allocate it and insert it. */
|
||||
per_type = hashmap_get(*dependencies, UNIT_DEPENDENCY_TO_PTR(d));
|
||||
if (!per_type) {
|
||||
per_type = hashmap_new(NULL);
|
||||
if (!per_type)
|
||||
return -ENOMEM;
|
||||
|
||||
r = hashmap_put(*dependencies, UNIT_DEPENDENCY_TO_PTR(d), per_type);
|
||||
if (r < 0) {
|
||||
hashmap_free(per_type);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return unit_per_dependency_type_hashmap_update(per_type, other, origin_mask, destination_mask);
|
||||
}
|
||||
|
||||
static void unit_merge_dependencies(
|
||||
Unit *u,
|
||||
Unit *other) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(other);
|
||||
assert(d < _UNIT_DEPENDENCY_MAX);
|
||||
|
||||
/* Fix backwards pointers. Let's iterate through all dependent units of the other unit. */
|
||||
HASHMAP_FOREACH_KEY(v, back, other->dependencies[d])
|
||||
if (u == other)
|
||||
return;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_(hashmap_freep) Hashmap *other_deps = NULL;
|
||||
UnitDependencyInfo di_back;
|
||||
Unit *back;
|
||||
void *dt; /* Actually of type UnitDependency, except that we don't bother casting it here,
|
||||
* since the hashmaps all want it as void pointer. */
|
||||
|
||||
/* Let's focus on one dependency type at a time, that 'other' has defined. */
|
||||
other_deps = hashmap_steal_first_key_and_value(other->dependencies, &dt);
|
||||
if (!other_deps)
|
||||
break; /* done! */
|
||||
|
||||
/* Now iterate through all dependencies of this dependency type, of 'other'. We refer to the
|
||||
* referenced units as 'back'. */
|
||||
HASHMAP_FOREACH_KEY(di_back.data, back, other_deps) {
|
||||
Hashmap *back_deps;
|
||||
void *back_dt;
|
||||
|
||||
/* Let's now iterate through the dependencies of that dependencies of the other units,
|
||||
* looking for pointers back, and let's fix them up, to instead point to 'u'. */
|
||||
for (UnitDependency k = 0; k < _UNIT_DEPENDENCY_MAX; k++)
|
||||
if (back == u) {
|
||||
/* Do not add dependencies between u and itself. */
|
||||
if (hashmap_remove(back->dependencies[k], other))
|
||||
maybe_warn_about_dependency(u, other_id, k);
|
||||
} else {
|
||||
UnitDependencyInfo di_u, di_other;
|
||||
|
||||
/* Let's drop this dependency between "back" and "other", and let's create it between
|
||||
* "back" and "u" instead. Let's merge the bit masks of the dependency we are moving,
|
||||
* and any such dependency which might already exist */
|
||||
|
||||
di_other.data = hashmap_get(back->dependencies[k], other);
|
||||
if (!di_other.data)
|
||||
continue; /* dependency isn't set, let's try the next one */
|
||||
|
||||
di_u.data = hashmap_get(back->dependencies[k], u);
|
||||
|
||||
UnitDependencyInfo di_merged = {
|
||||
.origin_mask = di_u.origin_mask | di_other.origin_mask,
|
||||
.destination_mask = di_u.destination_mask | di_other.destination_mask,
|
||||
};
|
||||
|
||||
r = hashmap_remove_and_replace(back->dependencies[k], other, u, di_merged.data);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to remove/replace: back=%s other=%s u=%s: %m", back->id, other_id, u->id);
|
||||
assert(r >= 0);
|
||||
|
||||
/* assert_se(hashmap_remove_and_replace(back->dependencies[k], other, u, di_merged.data) >= 0); */
|
||||
/* This is a dependency pointing back to the unit we want to merge with?
|
||||
* Suppress it (but warn) */
|
||||
unit_maybe_warn_about_dependency(u, other->id, UNIT_DEPENDENCY_FROM_PTR(dt));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Also do not move dependencies on u to itself */
|
||||
back = hashmap_remove(other->dependencies[d], u);
|
||||
if (back)
|
||||
maybe_warn_about_dependency(u, other_id, d);
|
||||
/* Now iterate through all deps of 'back', and fix the ones pointing to 'other' to
|
||||
* point to 'u' instead. */
|
||||
HASHMAP_FOREACH_KEY(back_deps, back_dt, back->dependencies) {
|
||||
UnitDependencyInfo di_move;
|
||||
|
||||
/* The move cannot fail. The caller must have performed a reservation. */
|
||||
assert_se(hashmap_complete_move(&u->dependencies[d], &other->dependencies[d]) == 0);
|
||||
di_move.data = hashmap_remove(back_deps, other);
|
||||
if (!di_move.data)
|
||||
continue;
|
||||
|
||||
other->dependencies[d] = hashmap_free(other->dependencies[d]);
|
||||
assert_se(unit_per_dependency_type_hashmap_update(
|
||||
back_deps,
|
||||
u,
|
||||
di_move.origin_mask,
|
||||
di_move.destination_mask) >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now all references towards 'other' of the current type 'dt' are corrected to point to
|
||||
* 'u'. Lets's now move the deps of type 'dt' from 'other' to 'u'. First, let's try to move
|
||||
* them per type wholesale. */
|
||||
r = hashmap_put(u->dependencies, dt, other_deps);
|
||||
if (r == -EEXIST) {
|
||||
Hashmap *deps;
|
||||
|
||||
/* The target unit already has dependencies of this type, let's then merge this individually. */
|
||||
|
||||
assert_se(deps = hashmap_get(u->dependencies, dt));
|
||||
|
||||
for (;;) {
|
||||
UnitDependencyInfo di_move;
|
||||
|
||||
/* Get first dep */
|
||||
di_move.data = hashmap_steal_first_key_and_value(other_deps, (void**) &back);
|
||||
if (!di_move.data)
|
||||
break; /* done */
|
||||
if (back == u) {
|
||||
/* Would point back to us, ignore */
|
||||
unit_maybe_warn_about_dependency(u, other->id, UNIT_DEPENDENCY_FROM_PTR(dt));
|
||||
continue;
|
||||
}
|
||||
|
||||
assert_se(unit_per_dependency_type_hashmap_update(deps, back, di_move.origin_mask, di_move.destination_mask) >= 0);
|
||||
}
|
||||
} else {
|
||||
assert_se(r >= 0);
|
||||
TAKE_PTR(other_deps);
|
||||
|
||||
if (hashmap_remove(other_deps, u))
|
||||
unit_maybe_warn_about_dependency(u, other->id, UNIT_DEPENDENCY_FROM_PTR(dt));
|
||||
}
|
||||
}
|
||||
|
||||
other->dependencies = hashmap_free(other->dependencies);
|
||||
}
|
||||
|
||||
int unit_merge(Unit *u, Unit *other) {
|
||||
const char *other_id = NULL;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
@@ -964,22 +1115,14 @@ int unit_merge(Unit *u, Unit *other) {
|
||||
if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other)))
|
||||
return -EEXIST;
|
||||
|
||||
if (other->id)
|
||||
other_id = strdupa(other->id);
|
||||
|
||||
/* Make reservations to ensure merge_dependencies() won't fail */
|
||||
for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
|
||||
r = reserve_dependencies(u, other, d);
|
||||
/*
|
||||
* We don't rollback reservations if we fail. We don't have
|
||||
* a way to undo reservations. A reservation is not a leak.
|
||||
*/
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
/* Make reservations to ensure merge_dependencies() won't fail. We don't rollback reservations if we
|
||||
* fail. We don't have a way to undo reservations. A reservation is not a leak. */
|
||||
r = unit_reserve_dependencies(u, other);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Merge names */
|
||||
r = merge_names(u, other);
|
||||
r = unit_merge_names(u, other);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -988,8 +1131,7 @@ int unit_merge(Unit *u, Unit *other) {
|
||||
unit_ref_set(other->refs_by_target, other->refs_by_target->source, u);
|
||||
|
||||
/* Merge dependencies */
|
||||
for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
|
||||
merge_dependencies(u, other, other_id, d);
|
||||
unit_merge_dependencies(u, other);
|
||||
|
||||
other->load_state = UNIT_MERGED;
|
||||
other->merged_into = u;
|
||||
@@ -1248,7 +1390,7 @@ int unit_add_default_target_dependency(Unit *u, Unit *target) {
|
||||
return 0;
|
||||
|
||||
/* Don't create loops */
|
||||
if (hashmap_get(target->dependencies[UNIT_BEFORE], u))
|
||||
if (unit_has_dependency(target, UNIT_ATOM_BEFORE, u))
|
||||
return 0;
|
||||
|
||||
return unit_add_dependency(target, UNIT_AFTER, u, true, UNIT_DEPENDENCY_DEFAULT);
|
||||
@@ -1416,10 +1558,18 @@ int unit_load(Unit *u) {
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (u->on_failure_job_mode == JOB_ISOLATE && hashmap_size(u->dependencies[UNIT_ON_FAILURE]) > 1) {
|
||||
r = log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOEXEC),
|
||||
"More than one OnFailure= dependencies specified but OnFailureJobMode=isolate set. Refusing.");
|
||||
goto fail;
|
||||
if (u->on_failure_job_mode == JOB_ISOLATE) {
|
||||
Unit *other, *found = NULL;
|
||||
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_ON_FAILURE) {
|
||||
if (!found)
|
||||
found = other;
|
||||
else if (found != other) {
|
||||
r = log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOEXEC),
|
||||
"More than one OnFailure= dependencies specified but OnFailureJobMode=isolate set. Refusing.");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (u->job_running_timeout != USEC_INFINITY && u->job_running_timeout > u->job_timeout)
|
||||
@@ -1577,7 +1727,6 @@ bool unit_shall_confirm_spawn(Unit *u) {
|
||||
|
||||
static bool unit_verify_deps(Unit *u) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
assert(u);
|
||||
|
||||
@@ -1586,9 +1735,9 @@ static bool unit_verify_deps(Unit *u) {
|
||||
* processing, but do not have any effect afterwards. We don't check BindsTo= dependencies that are not used in
|
||||
* conjunction with After= as for them any such check would make things entirely racy. */
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BINDS_TO]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_CANNOT_BE_ACTIVE_WITHOUT) {
|
||||
|
||||
if (!hashmap_contains(u->dependencies[UNIT_AFTER], other))
|
||||
if (!unit_has_dependency(u, UNIT_ATOM_AFTER, other))
|
||||
continue;
|
||||
|
||||
if (!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(other))) {
|
||||
@@ -1802,20 +1951,14 @@ bool unit_can_reload(Unit *u) {
|
||||
if (UNIT_VTABLE(u)->can_reload)
|
||||
return UNIT_VTABLE(u)->can_reload(u);
|
||||
|
||||
if (!hashmap_isempty(u->dependencies[UNIT_PROPAGATES_RELOAD_TO]))
|
||||
if (unit_has_dependency(u, UNIT_ATOM_PROPAGATES_RELOAD_TO, NULL))
|
||||
return true;
|
||||
|
||||
return UNIT_VTABLE(u)->reload;
|
||||
}
|
||||
|
||||
bool unit_is_unneeded(Unit *u) {
|
||||
static const UnitDependency deps[] = {
|
||||
UNIT_REQUIRED_BY,
|
||||
UNIT_REQUISITE_OF,
|
||||
UNIT_WANTED_BY,
|
||||
UNIT_BOUND_BY,
|
||||
};
|
||||
|
||||
Unit *other;
|
||||
assert(u);
|
||||
|
||||
if (!u->stop_when_unneeded)
|
||||
@@ -1827,55 +1970,37 @@ bool unit_is_unneeded(Unit *u) {
|
||||
if (u->job)
|
||||
return false;
|
||||
|
||||
for (size_t j = 0; j < ELEMENTSOF(deps); j++) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_PINS_STOP_WHEN_UNNEEDED) {
|
||||
/* If a dependent unit has a job queued, is active or transitioning, or is marked for
|
||||
* restart, then don't clean this one up. */
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[deps[j]]) {
|
||||
if (other->job)
|
||||
return false;
|
||||
if (other->job)
|
||||
return false;
|
||||
|
||||
if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other)))
|
||||
return false;
|
||||
if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other)))
|
||||
return false;
|
||||
|
||||
if (unit_will_restart(other))
|
||||
return false;
|
||||
}
|
||||
if (unit_will_restart(other))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void check_unneeded_dependencies(Unit *u) {
|
||||
|
||||
static const UnitDependency deps[] = {
|
||||
UNIT_REQUIRES,
|
||||
UNIT_REQUISITE,
|
||||
UNIT_WANTS,
|
||||
UNIT_BINDS_TO,
|
||||
};
|
||||
|
||||
Unit *other;
|
||||
assert(u);
|
||||
|
||||
/* Add all units this unit depends on to the queue that processes StopWhenUnneeded= behaviour. */
|
||||
|
||||
for (size_t j = 0; j < ELEMENTSOF(deps); j++) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[deps[j]])
|
||||
unit_submit_to_stop_when_unneeded_queue(other);
|
||||
}
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_ADD_STOP_WHEN_UNNEEDED_QUEUE)
|
||||
unit_submit_to_stop_when_unneeded_queue(other);
|
||||
}
|
||||
|
||||
static void unit_check_binds_to(Unit *u) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
bool stop = false;
|
||||
Unit *other;
|
||||
void *v;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
@@ -1886,7 +2011,7 @@ static void unit_check_binds_to(Unit *u) {
|
||||
if (unit_active_state(u) != UNIT_ACTIVE)
|
||||
return;
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BINDS_TO]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_CANNOT_BE_ACTIVE_WITHOUT) {
|
||||
if (other->job)
|
||||
continue;
|
||||
|
||||
@@ -1923,63 +2048,52 @@ static void unit_check_binds_to(Unit *u) {
|
||||
|
||||
static void retroactively_start_dependencies(Unit *u) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
assert(u);
|
||||
assert(UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)));
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUIRES])
|
||||
if (!hashmap_get(u->dependencies[UNIT_AFTER], other) &&
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_RETROACTIVE_START_REPLACE) /* Requires= + BindsTo= */
|
||||
if (!unit_has_dependency(u, UNIT_ATOM_AFTER, other) &&
|
||||
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
|
||||
manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL, NULL);
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BINDS_TO])
|
||||
if (!hashmap_get(u->dependencies[UNIT_AFTER], other) &&
|
||||
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
|
||||
manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL, NULL);
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_WANTS])
|
||||
if (!hashmap_get(u->dependencies[UNIT_AFTER], other) &&
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_RETROACTIVE_START_FAIL) /* Wants= */
|
||||
if (!unit_has_dependency(u, UNIT_ATOM_AFTER, other) &&
|
||||
!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
|
||||
manager_add_job(u->manager, JOB_START, other, JOB_FAIL, NULL, NULL, NULL);
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_CONFLICTS])
|
||||
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
|
||||
manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL, NULL);
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_CONFLICTED_BY])
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_RETROACTIVE_STOP_ON_START) /* Conflicts= (and inverse) */
|
||||
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
|
||||
manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void retroactively_stop_dependencies(Unit *u) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
assert(u);
|
||||
assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)));
|
||||
|
||||
/* Pull down units which are bound to us recursively if enabled */
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BOUND_BY])
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_RETROACTIVE_STOP_ON_STOP) /* BoundBy= */
|
||||
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
|
||||
manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void unit_start_on_failure(Unit *u) {
|
||||
bool logged = false;
|
||||
Unit *other;
|
||||
void *v;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (hashmap_size(u->dependencies[UNIT_ON_FAILURE]) <= 0)
|
||||
return;
|
||||
|
||||
log_unit_info(u, "Triggering OnFailure= dependencies.");
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_ON_FAILURE]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_ON_FAILURE) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
if (!logged) {
|
||||
log_unit_info(u, "Triggering OnFailure= dependencies.");
|
||||
logged = true;
|
||||
}
|
||||
|
||||
r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, NULL, &error, NULL);
|
||||
if (r < 0)
|
||||
log_unit_warning_errno(u, r, "Failed to enqueue OnFailure= job, ignoring: %s", bus_error_message(&error, r));
|
||||
@@ -1988,11 +2102,10 @@ void unit_start_on_failure(Unit *u) {
|
||||
|
||||
void unit_trigger_notify(Unit *u) {
|
||||
Unit *other;
|
||||
void *v;
|
||||
|
||||
assert(u);
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_TRIGGERED_BY])
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_TRIGGERED_BY)
|
||||
if (UNIT_VTABLE(other)->trigger_notify)
|
||||
UNIT_VTABLE(other)->trigger_notify(other, u);
|
||||
}
|
||||
@@ -2733,66 +2846,6 @@ bool unit_job_is_applicable(Unit *u, JobType j) {
|
||||
}
|
||||
}
|
||||
|
||||
static void maybe_warn_about_dependency(Unit *u, const char *other, UnitDependency dependency) {
|
||||
assert(u);
|
||||
|
||||
/* Only warn about some unit types */
|
||||
if (!IN_SET(dependency, UNIT_CONFLICTS, UNIT_CONFLICTED_BY, UNIT_BEFORE, UNIT_AFTER, UNIT_ON_FAILURE, UNIT_TRIGGERS, UNIT_TRIGGERED_BY))
|
||||
return;
|
||||
|
||||
if (streq_ptr(u->id, other))
|
||||
log_unit_warning(u, "Dependency %s=%s dropped", unit_dependency_to_string(dependency), u->id);
|
||||
else
|
||||
log_unit_warning(u, "Dependency %s=%s dropped, merged into %s", unit_dependency_to_string(dependency), strna(other), u->id);
|
||||
}
|
||||
|
||||
static int unit_add_dependency_hashmap(
|
||||
Hashmap **h,
|
||||
Unit *other,
|
||||
UnitDependencyMask origin_mask,
|
||||
UnitDependencyMask destination_mask) {
|
||||
|
||||
UnitDependencyInfo info;
|
||||
int r;
|
||||
|
||||
assert(h);
|
||||
assert(other);
|
||||
assert(origin_mask < _UNIT_DEPENDENCY_MASK_FULL);
|
||||
assert(destination_mask < _UNIT_DEPENDENCY_MASK_FULL);
|
||||
assert(origin_mask > 0 || destination_mask > 0);
|
||||
|
||||
r = hashmap_ensure_allocated(h, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert_cc(sizeof(void*) == sizeof(info));
|
||||
|
||||
info.data = hashmap_get(*h, other);
|
||||
if (info.data) {
|
||||
/* Entry already exists. Add in our mask. */
|
||||
|
||||
if (FLAGS_SET(origin_mask, info.origin_mask) &&
|
||||
FLAGS_SET(destination_mask, info.destination_mask))
|
||||
return 0; /* NOP */
|
||||
|
||||
info.origin_mask |= origin_mask;
|
||||
info.destination_mask |= destination_mask;
|
||||
|
||||
r = hashmap_update(*h, other, info.data);
|
||||
} else {
|
||||
info = (UnitDependencyInfo) {
|
||||
.origin_mask = origin_mask,
|
||||
.destination_mask = destination_mask,
|
||||
};
|
||||
|
||||
r = hashmap_put(*h, other, info.data);
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int unit_add_dependency(
|
||||
Unit *u,
|
||||
UnitDependency d,
|
||||
@@ -2825,10 +2878,12 @@ int unit_add_dependency(
|
||||
[UNIT_JOINS_NAMESPACE_OF] = UNIT_JOINS_NAMESPACE_OF,
|
||||
};
|
||||
Unit *original_u = u, *original_other = other;
|
||||
UnitDependencyAtom a;
|
||||
int r;
|
||||
/* Helper to know whether sending a notification is necessary or not:
|
||||
* if the dependency is already there, no need to notify! */
|
||||
bool noop = true;
|
||||
|
||||
/* Helper to know whether sending a notification is necessary or not: if the dependency is already
|
||||
* there, no need to notify! */
|
||||
bool noop;
|
||||
|
||||
assert(u);
|
||||
assert(d >= 0 && d < _UNIT_DEPENDENCY_MAX);
|
||||
@@ -2836,63 +2891,64 @@ int unit_add_dependency(
|
||||
|
||||
u = unit_follow_merge(u);
|
||||
other = unit_follow_merge(other);
|
||||
a = unit_dependency_to_atom(d);
|
||||
assert(a >= 0);
|
||||
|
||||
/* We won't allow dependencies on ourselves. We will not
|
||||
* consider them an error however. */
|
||||
/* We won't allow dependencies on ourselves. We will not consider them an error however. */
|
||||
if (u == other) {
|
||||
maybe_warn_about_dependency(original_u, original_other->id, d);
|
||||
unit_maybe_warn_about_dependency(original_u, original_other->id, d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Note that ordering a device unit after a unit is permitted since it
|
||||
* allows to start its job running timeout at a specific time. */
|
||||
if (d == UNIT_BEFORE && other->type == UNIT_DEVICE) {
|
||||
/* Note that ordering a device unit after a unit is permitted since it allows to start its job
|
||||
* running timeout at a specific time. */
|
||||
if (FLAGS_SET(a, UNIT_ATOM_BEFORE) && other->type == UNIT_DEVICE) {
|
||||
log_unit_warning(u, "Dependency Before=%s ignored (.device units cannot be delayed)", other->id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (d == UNIT_ON_FAILURE && !UNIT_VTABLE(u)->can_fail) {
|
||||
if (FLAGS_SET(a, UNIT_ATOM_ON_FAILURE) && !UNIT_VTABLE(u)->can_fail) {
|
||||
log_unit_warning(u, "Requested dependency OnFailure=%s ignored (%s units cannot fail).", other->id, unit_type_to_string(u->type));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (d == UNIT_TRIGGERS && !UNIT_VTABLE(u)->can_trigger)
|
||||
if (FLAGS_SET(a, UNIT_ATOM_TRIGGERS) && !UNIT_VTABLE(u)->can_trigger)
|
||||
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Requested dependency Triggers=%s refused (%s units cannot trigger other units).", other->id, unit_type_to_string(u->type));
|
||||
if (d == UNIT_TRIGGERED_BY && !UNIT_VTABLE(other)->can_trigger)
|
||||
if (FLAGS_SET(a, UNIT_ATOM_TRIGGERED_BY) && !UNIT_VTABLE(other)->can_trigger)
|
||||
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Requested dependency TriggeredBy=%s refused (%s units cannot trigger other units).", other->id, unit_type_to_string(other->type));
|
||||
|
||||
r = unit_add_dependency_hashmap(u->dependencies + d, other, mask, 0);
|
||||
r = unit_add_dependency_hashmap(&u->dependencies, d, other, mask, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if (r > 0)
|
||||
noop = false;
|
||||
noop = !r;
|
||||
|
||||
if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID && inverse_table[d] != d) {
|
||||
r = unit_add_dependency_hashmap(other->dependencies + inverse_table[d], u, 0, mask);
|
||||
r = unit_add_dependency_hashmap(&other->dependencies, inverse_table[d], u, 0, mask);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if (r > 0)
|
||||
if (r)
|
||||
noop = false;
|
||||
}
|
||||
|
||||
if (add_reference) {
|
||||
r = unit_add_dependency_hashmap(u->dependencies + UNIT_REFERENCES, other, mask, 0);
|
||||
r = unit_add_dependency_hashmap(&u->dependencies, UNIT_REFERENCES, other, mask, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if (r > 0)
|
||||
if (r)
|
||||
noop = false;
|
||||
|
||||
r = unit_add_dependency_hashmap(other->dependencies + UNIT_REFERENCED_BY, u, 0, mask);
|
||||
r = unit_add_dependency_hashmap(&other->dependencies, UNIT_REFERENCED_BY, u, 0, mask);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if (r > 0)
|
||||
if (r)
|
||||
noop = false;
|
||||
}
|
||||
|
||||
if (!noop)
|
||||
unit_add_to_dbus_queue(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4443,7 +4499,6 @@ int unit_setup_exec_runtime(Unit *u) {
|
||||
ExecRuntime **rt;
|
||||
size_t offset;
|
||||
Unit *other;
|
||||
void *v;
|
||||
int r;
|
||||
|
||||
offset = UNIT_VTABLE(u)->exec_runtime_offset;
|
||||
@@ -4455,7 +4510,7 @@ int unit_setup_exec_runtime(Unit *u) {
|
||||
return 0;
|
||||
|
||||
/* Try to get it from somebody else */
|
||||
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_JOINS_NAMESPACE_OF]) {
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, UNIT_ATOM_JOINS_NAMESPACE_OF) {
|
||||
r = exec_runtime_acquire(u->manager, NULL, other->id, false, rt);
|
||||
if (r == 1)
|
||||
return 1;
|
||||
@@ -4830,22 +4885,20 @@ int unit_fork_and_watch_rm_rf(Unit *u, char **paths, pid_t *ret_pid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unit_update_dependency_mask(Unit *u, UnitDependency d, Unit *other, UnitDependencyInfo di) {
|
||||
assert(u);
|
||||
assert(d >= 0);
|
||||
assert(d < _UNIT_DEPENDENCY_MAX);
|
||||
static void unit_update_dependency_mask(Hashmap *deps, Unit *other, UnitDependencyInfo di) {
|
||||
assert(deps);
|
||||
assert(other);
|
||||
|
||||
if (di.origin_mask == 0 && di.destination_mask == 0) {
|
||||
if (di.origin_mask == 0 && di.destination_mask == 0)
|
||||
/* No bit set anymore, let's drop the whole entry */
|
||||
assert_se(hashmap_remove(u->dependencies[d], other));
|
||||
log_unit_debug(u, "lost dependency %s=%s", unit_dependency_to_string(d), other->id);
|
||||
} else
|
||||
assert_se(hashmap_remove(deps, other));
|
||||
else
|
||||
/* Mask was reduced, let's update the entry */
|
||||
assert_se(hashmap_update(u->dependencies[d], other, di.data) == 0);
|
||||
assert_se(hashmap_update(deps, other, di.data) == 0);
|
||||
}
|
||||
|
||||
void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) {
|
||||
Hashmap *deps;
|
||||
assert(u);
|
||||
|
||||
/* Removes all dependencies u has on other units marked for ownership by 'mask'. */
|
||||
@@ -4853,7 +4906,7 @@ void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) {
|
||||
if (mask == 0)
|
||||
return;
|
||||
|
||||
for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
|
||||
HASHMAP_FOREACH(deps, u->dependencies) {
|
||||
bool done;
|
||||
|
||||
do {
|
||||
@@ -4862,26 +4915,29 @@ void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) {
|
||||
|
||||
done = true;
|
||||
|
||||
HASHMAP_FOREACH_KEY(di.data, other, u->dependencies[d]) {
|
||||
HASHMAP_FOREACH_KEY(di.data, other, deps) {
|
||||
Hashmap *other_deps;
|
||||
|
||||
if (FLAGS_SET(~mask, di.origin_mask))
|
||||
continue;
|
||||
|
||||
di.origin_mask &= ~mask;
|
||||
unit_update_dependency_mask(u, d, other, di);
|
||||
unit_update_dependency_mask(deps, other, di);
|
||||
|
||||
/* We updated the dependency from our unit to the other unit now. But most dependencies
|
||||
* imply a reverse dependency. Hence, let's delete that one too. For that we go through
|
||||
* all dependency types on the other unit and delete all those which point to us and
|
||||
* have the right mask set. */
|
||||
|
||||
for (UnitDependency q = 0; q < _UNIT_DEPENDENCY_MAX; q++) {
|
||||
HASHMAP_FOREACH(other_deps, other->dependencies) {
|
||||
UnitDependencyInfo dj;
|
||||
|
||||
dj.data = hashmap_get(other->dependencies[q], u);
|
||||
dj.data = hashmap_get(other_deps, u);
|
||||
if (FLAGS_SET(~mask, dj.destination_mask))
|
||||
continue;
|
||||
dj.destination_mask &= ~mask;
|
||||
|
||||
unit_update_dependency_mask(other, q, u, dj);
|
||||
dj.destination_mask &= ~mask;
|
||||
unit_update_dependency_mask(other_deps, u, dj);
|
||||
}
|
||||
|
||||
unit_add_to_gc_queue(other);
|
||||
@@ -5589,3 +5645,44 @@ static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(collect_mode, CollectMode);
|
||||
|
||||
Unit* unit_has_dependency(const Unit *u, UnitDependencyAtom atom, Unit *other) {
|
||||
Unit *i;
|
||||
|
||||
assert(u);
|
||||
|
||||
/* Checks if the unit has a dependency on 'other' with the specified dependency atom. If 'other' is
|
||||
* NULL checks if the unit has *any* dependency of that atom. Returns 'other' if found (or if 'other'
|
||||
* is NULL the first entry found), or NULL if not found. */
|
||||
|
||||
UNIT_FOREACH_DEPENDENCY(i, u, atom)
|
||||
if (!other || other == i)
|
||||
return i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int unit_get_dependency_array(const Unit *u, UnitDependencyAtom atom, Unit ***ret_array) {
|
||||
_cleanup_free_ Unit **array = NULL;
|
||||
size_t n = 0;
|
||||
Unit *other;
|
||||
|
||||
assert(u);
|
||||
assert(ret_array);
|
||||
|
||||
/* Gets a list of units matching a specific atom as array. This is useful when iterating through
|
||||
* dependencies while modifying them: the array is an "atomic snapshot" of sorts, that can be read
|
||||
* while the dependency table is continously updated. */
|
||||
|
||||
UNIT_FOREACH_DEPENDENCY(other, u, atom) {
|
||||
if (!GREEDY_REALLOC(array, n + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
array[n++] = other;
|
||||
}
|
||||
|
||||
*ret_array = TAKE_PTR(array);
|
||||
|
||||
assert(n <= INT_MAX);
|
||||
return (int) n;
|
||||
}
|
||||
|
||||
+75
-5
@@ -102,6 +102,16 @@ typedef union UnitDependencyInfo {
|
||||
} _packed_;
|
||||
} UnitDependencyInfo;
|
||||
|
||||
/* Newer LLVM versions don't like implicit casts from large pointer types to smaller enums, hence let's add
|
||||
* explicit type-safe helpers for that. */
|
||||
static inline UnitDependency UNIT_DEPENDENCY_FROM_PTR(const void *p) {
|
||||
return PTR_TO_INT(p);
|
||||
}
|
||||
|
||||
static inline void* UNIT_DEPENDENCY_TO_PTR(UnitDependency d) {
|
||||
return INT_TO_PTR(d);
|
||||
}
|
||||
|
||||
#include "job.h"
|
||||
|
||||
struct UnitRef {
|
||||
@@ -125,11 +135,13 @@ typedef struct Unit {
|
||||
|
||||
Set *aliases; /* All the other names. */
|
||||
|
||||
/* For each dependency type we maintain a Hashmap whose key is the Unit* object, and the value encodes why the
|
||||
* dependency exists, using the UnitDependencyInfo type */
|
||||
Hashmap *dependencies[_UNIT_DEPENDENCY_MAX];
|
||||
/* For each dependency type we can look up another Hashmap with this, whose key is a Unit* object,
|
||||
* and whose value encodes why the dependency exists, using the UnitDependencyInfo type. i.e. a
|
||||
* Hashmap(UnitDependency → Hashmap(Unit* → UnitDependencyInfo)) */
|
||||
Hashmap *dependencies;
|
||||
|
||||
/* Similar, for RequiresMountsFor= path dependencies. The key is the path, the value the UnitDependencyInfo type */
|
||||
/* Similar, for RequiresMountsFor= path dependencies. The key is the path, the value the
|
||||
* UnitDependencyInfo type */
|
||||
Hashmap *requires_mounts_for;
|
||||
|
||||
char *description;
|
||||
@@ -683,8 +695,15 @@ static inline const UnitVTable* UNIT_VTABLE(const Unit *u) {
|
||||
#define UNIT_HAS_CGROUP_CONTEXT(u) (UNIT_VTABLE(u)->cgroup_context_offset > 0)
|
||||
#define UNIT_HAS_KILL_CONTEXT(u) (UNIT_VTABLE(u)->kill_context_offset > 0)
|
||||
|
||||
Unit* unit_has_dependency(const Unit *u, UnitDependencyAtom atom, Unit *other);
|
||||
int unit_get_dependency_array(const Unit *u, UnitDependencyAtom atom, Unit ***ret_array);
|
||||
|
||||
static inline Hashmap* unit_get_dependencies(Unit *u, UnitDependency d) {
|
||||
return hashmap_get(u->dependencies, UNIT_DEPENDENCY_TO_PTR(d));
|
||||
}
|
||||
|
||||
static inline Unit* UNIT_TRIGGER(Unit *u) {
|
||||
return hashmap_first_key(u->dependencies[UNIT_TRIGGERS]);
|
||||
return unit_has_dependency(u, UNIT_ATOM_TRIGGERS, NULL);
|
||||
}
|
||||
|
||||
Unit* unit_new(Manager *m, size_t size);
|
||||
@@ -990,3 +1009,54 @@ int unit_thaw_vtable_common(Unit *u);
|
||||
|
||||
const char* collect_mode_to_string(CollectMode m) _const_;
|
||||
CollectMode collect_mode_from_string(const char *s) _pure_;
|
||||
|
||||
typedef struct UnitForEachDependencyData {
|
||||
/* Stores state for the FOREACH macro below for iterating through all deps that have any of the
|
||||
* specified dependency atom bits set */
|
||||
UnitDependencyAtom match_atom;
|
||||
Hashmap *by_type, *by_unit;
|
||||
void *current_type;
|
||||
Iterator by_type_iterator, by_unit_iterator;
|
||||
Unit **current_unit;
|
||||
} UnitForEachDependencyData;
|
||||
|
||||
/* Iterates through all dependencies that have a specific atom in the dependency type set. This tries to be
|
||||
* smart: if the atom is unique, we'll directly go to right entry. Otherwise we'll iterate through the
|
||||
* per-dependency type hashmap and match all dep that have the right atom set. */
|
||||
#define _UNIT_FOREACH_DEPENDENCY(other, u, ma, data) \
|
||||
for (UnitForEachDependencyData data = { \
|
||||
.match_atom = (ma), \
|
||||
.by_type = (u)->dependencies, \
|
||||
.by_type_iterator = ITERATOR_FIRST, \
|
||||
.current_unit = &(other), \
|
||||
}; \
|
||||
({ \
|
||||
UnitDependency _dt = _UNIT_DEPENDENCY_INVALID; \
|
||||
bool _found; \
|
||||
\
|
||||
if (data.by_type && ITERATOR_IS_FIRST(data.by_type_iterator)) { \
|
||||
_dt = unit_dependency_from_unique_atom(data.match_atom); \
|
||||
if (_dt >= 0) { \
|
||||
data.by_unit = hashmap_get(data.by_type, UNIT_DEPENDENCY_TO_PTR(_dt)); \
|
||||
data.current_type = UNIT_DEPENDENCY_TO_PTR(_dt); \
|
||||
data.by_type = NULL; \
|
||||
_found = !!data.by_unit; \
|
||||
} \
|
||||
} \
|
||||
if (_dt < 0) \
|
||||
_found = hashmap_iterate(data.by_type, \
|
||||
&data.by_type_iterator, \
|
||||
(void**)&(data.by_unit), \
|
||||
(const void**) &(data.current_type)); \
|
||||
_found; \
|
||||
}); ) \
|
||||
if ((unit_dependency_to_atom(UNIT_DEPENDENCY_FROM_PTR(data.current_type)) & data.match_atom) != 0) \
|
||||
for (data.by_unit_iterator = ITERATOR_FIRST; \
|
||||
hashmap_iterate(data.by_unit, \
|
||||
&data.by_unit_iterator, \
|
||||
NULL, \
|
||||
(const void**) data.current_unit); )
|
||||
|
||||
/* Note: this matches deps that have *any* of the atoms specified in match_atom set */
|
||||
#define UNIT_FOREACH_DEPENDENCY(other, u, match_atom) \
|
||||
_UNIT_FOREACH_DEPENDENCY(other, u, match_atom, UNIQ_T(data, UNIQ))
|
||||
|
||||
+16
-16
@@ -122,32 +122,32 @@ int main(int argc, char *argv[]) {
|
||||
assert_se(manager_add_job(m, JOB_START, a_conj, JOB_REPLACE, NULL, NULL, &j) == -EDEADLK);
|
||||
manager_dump_jobs(m, stdout, "\t");
|
||||
|
||||
assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b));
|
||||
assert_se(!hashmap_get(b->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], c));
|
||||
assert_se(!hashmap_get(c->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
|
||||
assert_se(unit_add_dependency(a, UNIT_PROPAGATES_RELOAD_TO, b, true, UNIT_DEPENDENCY_UDEV) == 0);
|
||||
assert_se(unit_add_dependency(a, UNIT_PROPAGATES_RELOAD_TO, c, true, UNIT_DEPENDENCY_PROC_SWAP) == 0);
|
||||
|
||||
assert_se(hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b));
|
||||
assert_se(hashmap_get(b->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], c));
|
||||
assert_se(hashmap_get(c->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b));
|
||||
assert_se(hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
assert_se(hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c));
|
||||
assert_se(hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
|
||||
unit_remove_dependencies(a, UNIT_DEPENDENCY_UDEV);
|
||||
|
||||
assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b));
|
||||
assert_se(!hashmap_get(b->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], c));
|
||||
assert_se(hashmap_get(c->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
assert_se(hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c));
|
||||
assert_se(hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
|
||||
unit_remove_dependencies(a, UNIT_DEPENDENCY_PROC_SWAP);
|
||||
|
||||
assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b));
|
||||
assert_se(!hashmap_get(b->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], c));
|
||||
assert_se(!hashmap_get(c->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c));
|
||||
assert_se(!hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a));
|
||||
|
||||
assert_se(manager_load_unit(m, "unit-with-multiple-dashes.service", NULL, NULL, &unit_with_multiple_dashes) >= 0);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user