core: track why unit dependencies came to be

This replaces the dependencies Set* objects by Hashmap* objects, where
the key is the depending Unit, and the value is a bitmask encoding why
the specific dependency was created.

The bitmask contains a number of different, defined bits, that indicate
why dependencies exist, for example whether they are created due to
explicitly configured deps in files, by udev rules or implicitly.

Note that memory usage is not increased by this change, even though we
store more information, as we manage to encode the bit mask inside the
value pointer each Hashmap entry contains.

Why this all? When we know how a dependency came to be, we can update
dependencies correctly when a configuration source changes but others
are left unaltered. Specifically:

1. We can fix UDEV_WANTS dependency generation: so far we kept adding
   dependencies configured that way, but if a device lost such a
   dependency we couldn't them again as there was no scheme for removing
   of dependencies in place.

2. We can implement "pin-pointed" reload of unit files. If we know what
   dependencies were created as result of configuration in a unit file,
   then we know what to flush out when we want to reload it.

3. It's useful for debugging: "systemd-analyze dump" now shows
   this information, helping substantially with understanding how
   systemd's dependency tree came to be the way it came to be.
This commit is contained in:
Lennart Poettering 2017-10-25 20:46:01 +02:00
parent 8f50e86a86
commit eef85c4a3f
20 changed files with 598 additions and 347 deletions

View File

@ -130,7 +130,20 @@ static void automount_done(Unit *u) {
a->expire_event_source = sd_event_source_unref(a->expire_event_source);
}
static int automount_add_mount_links(Automount *a) {
static int automount_add_trigger_dependencies(Automount *a) {
Unit *x;
int r;
assert(a);
r = unit_load_related_unit(UNIT(a), ".mount", &x);
if (r < 0)
return r;
return unit_add_two_dependencies(UNIT(a), UNIT_BEFORE, UNIT_TRIGGERS, x, true, UNIT_DEPENDENCY_IMPLICIT);
}
static int automount_add_mount_dependencies(Automount *a) {
_cleanup_free_ char *parent = NULL;
assert(a);
@ -139,7 +152,7 @@ static int automount_add_mount_links(Automount *a) {
if (!parent)
return -ENOMEM;
return unit_require_mounts_for(UNIT(a), parent);
return unit_require_mounts_for(UNIT(a), parent, UNIT_DEPENDENCY_IMPLICIT);
}
static int automount_add_default_dependencies(Automount *a) {
@ -153,7 +166,7 @@ static int automount_add_default_dependencies(Automount *a) {
if (!MANAGER_IS_SYSTEM(UNIT(a)->manager))
return 0;
r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
@ -215,21 +228,15 @@ static int automount_load(Unit *u) {
return r;
if (u->load_state == UNIT_LOADED) {
Unit *x;
r = automount_set_where(a);
if (r < 0)
return r;
r = unit_load_related_unit(u, ".mount", &x);
r = automount_add_trigger_dependencies(a);
if (r < 0)
return r;
r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
if (r < 0)
return r;
r = automount_add_mount_links(a);
r = automount_add_mount_dependencies(a);
if (r < 0)
return r;

View File

@ -1095,10 +1095,11 @@ CGroupMask unit_get_members_mask(Unit *u) {
u->cgroup_members_mask = 0;
if (u->type == UNIT_SLICE) {
void *v;
Unit *member;
Iterator i;
SET_FOREACH(member, u->dependencies[UNIT_BEFORE], i) {
HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE], i) {
if (member == u)
continue;
@ -1575,8 +1576,9 @@ static void unit_add_siblings_to_cgroup_realize_queue(Unit *u) {
while ((slice = UNIT_DEREF(u->slice))) {
Iterator i;
Unit *m;
void *v;
SET_FOREACH(m, slice->dependencies[UNIT_BEFORE], i) {
HASHMAP_FOREACH_KEY(v, m, u->dependencies[UNIT_BEFORE], i) {
if (m == u)
continue;
@ -2426,8 +2428,9 @@ void unit_invalidate_cgroup_bpf(Unit *u) {
if (u->type == UNIT_SLICE) {
Unit *member;
Iterator i;
void *v;
SET_FOREACH(member, u->dependencies[UNIT_BEFORE], i) {
HASHMAP_FOREACH_KEY(v, member, u->dependencies[UNIT_BEFORE], i) {
if (member == u)
continue;

View File

@ -100,9 +100,10 @@ static int property_get_dependencies(
void *userdata,
sd_bus_error *error) {
Set *s = *(Set**) userdata;
Hashmap *h = *(Hashmap**) userdata;
Iterator j;
Unit *u;
void *v;
int r;
assert(bus);
@ -112,7 +113,7 @@ static int property_get_dependencies(
if (r < 0)
return r;
SET_FOREACH(u, s, j) {
HASHMAP_FOREACH_KEY(v, u, h, j) {
r = sd_bus_message_append(reply, "s", u->id);
if (r < 0)
return r;
@ -1395,7 +1396,7 @@ static int bus_unit_set_transient_property(
if (mode != UNIT_CHECK) {
_cleanup_free_ char *label = NULL;
r = unit_add_dependency_by_name(u, d, other, NULL, true);
r = unit_add_dependency_by_name(u, d, other, NULL, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;

View File

@ -279,7 +279,7 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
if (r < 0)
return log_unit_error_errno(u, r, "Failed to mangle unit name \"%s\": %m", word);
r = unit_add_dependency_by_name(u, UNIT_WANTS, k, NULL, true);
r = unit_add_dependency_by_name(u, UNIT_WANTS, k, NULL, true, UNIT_DEPENDENCY_UDEV);
if (r < 0)
return log_unit_error_errno(u, r, "Failed to add wants dependency: %m");
}
@ -303,13 +303,16 @@ static bool device_is_bound_by_mounts(Unit *d, struct udev_device *dev) {
static int device_upgrade_mount_deps(Unit *u) {
Unit *other;
Iterator i;
void *v;
int r;
SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i) {
/* 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], i) {
if (other->type != UNIT_MOUNT)
continue;
r = unit_add_dependency(other, UNIT_BINDS_TO, u, true);
r = unit_add_dependency(other, UNIT_BINDS_TO, u, true, UNIT_DEPENDENCY_UDEV);
if (r < 0)
return r;
}
@ -380,11 +383,9 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
(void) device_add_udev_wants(u, dev);
}
/* So the user wants the mount units to be bound to the device but a
* mount unit might has been seen by systemd before the device appears
* on its radar. In this case the device unit is partially initialized
* and includes the deps on the mount unit but at that time the "bind
* mounts" flag wasn't not present. Fix this up now. */
/* So the user wants the mount units to be bound to the device but a mount unit might has been seen by systemd
* before the device appears on its radar. In this case the device unit is partially initialized and includes
* the deps on the mount unit but at that time the "bind mounts" flag wasn't not present. Fix this up now. */
if (dev && device_is_bound_by_mounts(u, dev))
device_upgrade_mount_deps(u);

View File

@ -437,6 +437,7 @@ int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u) {
static bool job_is_runnable(Job *j) {
Iterator i;
Unit *other;
void *v;
assert(j);
assert(j->installed);
@ -459,13 +460,12 @@ static bool job_is_runnable(Job *j) {
return true;
if (IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD)) {
/* Immediate result is that the job is or might be
* started. In this case let's wait for the
* dependencies, regardless whether they are
* starting or stopping something. */
SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i)
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i)
if (other->job)
return false;
}
@ -473,7 +473,7 @@ static bool job_is_runnable(Job *j) {
/* Also, if something else is being stopped and we should
* change state after it, then let's wait. */
SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i)
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i)
if (other->job &&
IN_SET(other->job->type, JOB_STOP, JOB_RESTART))
return false;
@ -832,10 +832,11 @@ static void job_emit_status_message(Unit *u, JobType t, JobResult result) {
static void job_fail_dependencies(Unit *u, UnitDependency d) {
Unit *other;
Iterator i;
void *v;
assert(u);
SET_FOREACH(other, u->dependencies[d], i) {
HASHMAP_FOREACH_KEY(v, other, u->dependencies[d], i) {
Job *j = other->job;
if (!j)
@ -852,6 +853,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr
Unit *other;
JobType t;
Iterator i;
void *v;
assert(j);
assert(j->installed);
@ -919,12 +921,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 */
SET_FOREACH(other, u->dependencies[UNIT_AFTER], i)
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_AFTER], i)
if (other->job) {
job_add_to_run_queue(other->job);
job_add_to_gc_queue(other->job);
}
SET_FOREACH(other, u->dependencies[UNIT_BEFORE], i)
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BEFORE], i)
if (other->job) {
job_add_to_run_queue(other->job);
job_add_to_gc_queue(other->job);
@ -1273,6 +1275,7 @@ int job_get_timeout(Job *j, usec_t *timeout) {
bool job_check_gc(Job *j) {
Unit *other;
Iterator i;
void *v;
assert(j);
@ -1301,7 +1304,7 @@ bool job_check_gc(Job *j) {
/* If a job is ordered after ours, and is to be started, then it needs to wait for us, regardless if we stop or
* start, hence let's not GC in that case. */
SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i) {
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i) {
if (!other->job)
continue;
@ -1314,7 +1317,7 @@ bool job_check_gc(Job *j) {
/* If we are going down, but something else is ordered After= us, then it needs to wait for us */
if (IN_SET(j->type, JOB_STOP, JOB_RESTART))
SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i) {
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
if (!other->job)
continue;
@ -1392,6 +1395,7 @@ int job_get_before(Job *j, Job*** ret) {
size_t n = 0, n_allocated = 0;
Unit *other = NULL;
Iterator i;
void *v;
/* Returns a list of all pending jobs that need to finish before this job may be started. */
@ -1405,7 +1409,7 @@ int job_get_before(Job *j, Job*** ret) {
if (IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD)) {
SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i) {
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
if (!other->job)
continue;
@ -1415,7 +1419,7 @@ int job_get_before(Job *j, Job*** ret) {
}
}
SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i) {
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i) {
if (!other->job)
continue;
@ -1439,6 +1443,7 @@ int job_get_after(Job *j, Job*** ret) {
_cleanup_free_ Job** list = NULL;
size_t n = 0, n_allocated = 0;
Unit *other = NULL;
void *v;
Iterator i;
assert(j);
@ -1446,7 +1451,7 @@ int job_get_after(Job *j, Job*** ret) {
/* Returns a list of all pending jobs that are waiting for this job to finish. */
SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i) {
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i) {
if (!other->job)
continue;
@ -1463,7 +1468,7 @@ int job_get_after(Job *j, Job*** ret) {
if (IN_SET(j->type, JOB_STOP, JOB_RESTART)) {
SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i) {
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
if (!other->job)
continue;

View File

@ -115,7 +115,7 @@ static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suff
log_unit_warning(u, "%s dependency dropin %s target %s has different name",
unit_dependency_to_string(dependency), *p, target);
r = unit_add_dependency_by_name(u, dependency, entry, *p, true);
r = unit_add_dependency_by_name(u, dependency, entry, *p, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
log_unit_error_errno(u, r, "cannot add %s dependency on %s, ignoring: %m",
unit_dependency_to_string(dependency), entry);

View File

@ -142,7 +142,7 @@ int config_parse_unit_deps(
continue;
}
r = unit_add_dependency_by_name(u, d, k, NULL, true);
r = unit_add_dependency_by_name(u, d, k, NULL, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
}
@ -1538,7 +1538,7 @@ int config_parse_trigger_unit(
assert(rvalue);
assert(data);
if (!set_isempty(u->dependencies[UNIT_TRIGGERS])) {
if (!hashmap_isempty(u->dependencies[UNIT_TRIGGERS])) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Multiple units to trigger specified, ignoring: %s", rvalue);
return 0;
}
@ -1560,7 +1560,7 @@ int config_parse_trigger_unit(
return 0;
}
r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true);
r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_TRIGGERS, p, NULL, true, UNIT_DEPENDENCY_FILE);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add trigger on %s, ignoring: %m", p);
return 0;
@ -1760,11 +1760,11 @@ int config_parse_service_sockets(
continue;
}
r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true);
r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_WANTS, UNIT_AFTER, k, NULL, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true);
r = unit_add_dependency_by_name(UNIT(s), UNIT_TRIGGERED_BY, k, NULL, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add dependency on %s, ignoring: %m", k);
}
@ -2569,7 +2569,7 @@ int config_parse_unit_requires_mounts_for(
continue;
}
r = unit_require_mounts_for(u, resolved);
r = unit_require_mounts_for(u, resolved, UNIT_DEPENDENCY_FILE);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to add required mount \"%s\", ignoring: %m", resolved);
continue;

View File

@ -967,21 +967,23 @@ enum {
};
static void unit_gc_mark_good(Unit *u, unsigned gc_marker) {
Iterator i;
Unit *other;
Iterator i;
void *v;
u->gc_marker = gc_marker + GC_OFFSET_GOOD;
/* Recursively mark referenced units as GOOD as well */
SET_FOREACH(other, u->dependencies[UNIT_REFERENCES], i)
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REFERENCES], i)
if (other->gc_marker == gc_marker + GC_OFFSET_UNSURE)
unit_gc_mark_good(other, gc_marker);
}
static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
Iterator i;
Unit *other;
bool is_bad;
Iterator i;
void *v;
assert(u);
@ -999,7 +1001,7 @@ static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
is_bad = true;
SET_FOREACH(other, u->dependencies[UNIT_REFERENCED_BY], i) {
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REFERENCED_BY], i) {
unit_gc_sweep(other, gc_marker);
if (other->gc_marker == gc_marker + GC_OFFSET_GOOD)

View File

@ -280,8 +280,7 @@ _pure_ static MountParameters* get_mount_parameters(Mount *m) {
return get_mount_parameters_fragment(m);
}
static int mount_add_mount_links(Mount *m) {
_cleanup_free_ char *parent = NULL;
static int mount_add_mount_dependencies(Mount *m) {
const char *fstype;
MountParameters *pm;
Unit *other;
@ -292,33 +291,32 @@ static int mount_add_mount_links(Mount *m) {
assert(m);
if (!path_equal(m->where, "/")) {
/* Adds in links to other mount points that might lie further
* up in the hierarchy */
_cleanup_free_ char *parent = NULL;
/* Adds in links to other mount points that might lie further up in the hierarchy */
parent = dirname_malloc(m->where);
if (!parent)
return -ENOMEM;
r = unit_require_mounts_for(UNIT(m), parent);
r = unit_require_mounts_for(UNIT(m), parent, UNIT_DEPENDENCY_IMPLICIT);
if (r < 0)
return r;
}
/* Adds in links to other mount points that might be needed
* for the source path (if this is a bind mount or a loop mount) to be
* available. */
/* Adds in dependencies to other mount points that might be needed for the source path (if this is a bind mount
* or a loop mount) to be available. */
pm = get_mount_parameters_fragment(m);
if (pm && pm->what &&
path_is_absolute(pm->what) &&
(mount_is_bind(pm) || mount_is_loop(pm) || !mount_is_network(pm))) {
r = unit_require_mounts_for(UNIT(m), pm->what);
r = unit_require_mounts_for(UNIT(m), pm->what, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
}
/* Adds in links to other units that use this path or paths
* further down in the hierarchy */
/* Adds in dependencies to other units that use this path or paths further down in the hierarchy */
s = manager_get_units_requiring_mounts_for(UNIT(m)->manager, m->where);
SET_FOREACH(other, s, i) {
@ -328,13 +326,13 @@ static int mount_add_mount_links(Mount *m) {
if (other == UNIT(m))
continue;
r = unit_add_dependency(other, UNIT_AFTER, UNIT(m), true);
r = unit_add_dependency(other, UNIT_AFTER, UNIT(m), true, UNIT_DEPENDENCY_PATH);
if (r < 0)
return r;
if (UNIT(m)->fragment_path) {
/* If we have fragment configuration, then make this dependency required */
r = unit_add_dependency(other, UNIT_REQUIRES, UNIT(m), true);
r = unit_add_dependency(other, UNIT_REQUIRES, UNIT(m), true, UNIT_DEPENDENCY_PATH);
if (r < 0)
return r;
}
@ -343,7 +341,7 @@ static int mount_add_mount_links(Mount *m) {
/* If this is a tmpfs mount then we have to unmount it before we try to deactivate swaps */
fstype = mount_get_fstype(m);
if (streq(fstype, "tmpfs")) {
r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_SWAP_TARGET, NULL, true);
r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_SWAP_TARGET, NULL, true, UNIT_DEPENDENCY_IMPLICIT);
if (r < 0)
return r;
}
@ -351,9 +349,10 @@ static int mount_add_mount_links(Mount *m) {
return 0;
}
static int mount_add_device_links(Mount *m) {
MountParameters *p;
static int mount_add_device_dependencies(Mount *m) {
bool device_wants_mount = false;
UnitDependencyMask mask;
MountParameters *p;
UnitDependency dep;
int r;
@ -391,16 +390,19 @@ static int mount_add_device_links(Mount *m) {
* automatically stopped when the device disappears suddenly. */
dep = mount_is_bound_to_device(m) ? UNIT_BINDS_TO : UNIT_REQUIRES;
r = unit_add_node_link(UNIT(m), p->what, device_wants_mount, dep);
mask = m->from_fragment ? UNIT_DEPENDENCY_FILE : UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT;
r = unit_add_node_dependency(UNIT(m), p->what, device_wants_mount, dep, mask);
if (r < 0)
return r;
return 0;
}
static int mount_add_quota_links(Mount *m) {
int r;
static int mount_add_quota_dependencies(Mount *m) {
UnitDependencyMask mask;
MountParameters *p;
int r;
assert(m);
@ -414,11 +416,13 @@ static int mount_add_quota_links(Mount *m) {
if (!needs_quota(p))
return 0;
r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTACHECK_SERVICE, NULL, true);
mask = m->from_fragment ? UNIT_DEPENDENCY_FILE : UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT;
r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTACHECK_SERVICE, NULL, true, mask);
if (r < 0)
return r;
r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTAON_SERVICE, NULL, true);
r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_WANTS, SPECIAL_QUOTAON_SERVICE, NULL, true, mask);
if (r < 0)
return r;
@ -457,9 +461,10 @@ static bool mount_is_extrinsic(Mount *m) {
}
static int mount_add_default_dependencies(Mount *m) {
UnitDependencyMask mask;
int r;
MountParameters *p;
const char *after;
int r;
assert(m);
@ -476,6 +481,8 @@ static int mount_add_default_dependencies(Mount *m) {
if (!p)
return 0;
mask = m->from_fragment ? UNIT_DEPENDENCY_FILE : UNIT_DEPENDENCY_MOUNTINFO_DEFAULT;
if (mount_is_network(p)) {
/* We order ourselves after network.target. This is
* primarily useful at shutdown: services that take
@ -483,7 +490,7 @@ static int mount_add_default_dependencies(Mount *m) {
* network.target, so that they are shut down only
* after this mount unit is stopped. */
r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, NULL, true);
r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, NULL, true, mask);
if (r < 0)
return r;
@ -494,7 +501,7 @@ static int mount_add_default_dependencies(Mount *m) {
* whose purpose it is to delay this until the network
* is "up". */
r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, SPECIAL_NETWORK_ONLINE_TARGET, NULL, true);
r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, SPECIAL_NETWORK_ONLINE_TARGET, NULL, true, mask);
if (r < 0)
return r;
@ -502,11 +509,11 @@ static int mount_add_default_dependencies(Mount *m) {
} else
after = SPECIAL_LOCAL_FS_PRE_TARGET;
r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true);
r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, NULL, true, mask);
if (r < 0)
return r;
r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true, mask);
if (r < 0)
return r;
@ -577,15 +584,15 @@ static int mount_add_extras(Mount *m) {
return r;
}
r = mount_add_device_links(m);
r = mount_add_device_dependencies(m);
if (r < 0)
return r;
r = mount_add_mount_links(m);
r = mount_add_mount_dependencies(m);
if (r < 0)
return r;
r = mount_add_quota_links(m);
r = mount_add_quota_dependencies(m);
if (r < 0)
return r;
@ -1453,11 +1460,11 @@ static int mount_setup_new_unit(
int r;
target = mount_is_network(p) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET;
r = unit_add_dependency_by_name(u, UNIT_BEFORE, target, NULL, true);
r = unit_add_dependency_by_name(u, UNIT_BEFORE, target, NULL, true, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT);
if (r < 0)
return r;
r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT);
if (r < 0)
return r;
}
@ -1515,7 +1522,7 @@ static int mount_setup_existing_unit(
* in the dependency "Set*" objects who created a
* dependency), we can only add deps, never lose them,
* until the next full daemon-reload. */
unit_add_dependency_by_name(u, UNIT_BEFORE, SPECIAL_REMOTE_FS_TARGET, NULL, true);
unit_add_dependency_by_name(u, UNIT_BEFORE, SPECIAL_REMOTE_FS_TARGET, NULL, true, UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT);
load_extras = true;
}

View File

@ -277,14 +277,14 @@ static void path_done(Unit *u) {
path_free_specs(p);
}
static int path_add_mount_links(Path *p) {
static int path_add_mount_dependencies(Path *p) {
PathSpec *s;
int r;
assert(p);
LIST_FOREACH(spec, s, p->specs) {
r = unit_require_mounts_for(UNIT(p), s->path);
r = unit_require_mounts_for(UNIT(p), s->path, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
}
@ -314,17 +314,33 @@ static int path_add_default_dependencies(Path *p) {
if (!UNIT(p)->default_dependencies)
return 0;
r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_PATHS_TARGET, NULL, true);
r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_PATHS_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
if (MANAGER_IS_SYSTEM(UNIT(p)->manager)) {
r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
}
return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
}
static int path_add_trigger_dependencies(Path *p) {
Unit *x;
int r;
assert(p);
if (!hashmap_isempty(UNIT(p)->dependencies[UNIT_TRIGGERS]))
return 0;
r = unit_load_related_unit(UNIT(p), ".service", &x);
if (r < 0)
return r;
return unit_add_two_dependencies(UNIT(p), UNIT_BEFORE, UNIT_TRIGGERS, x, true, UNIT_DEPENDENCY_IMPLICIT);
}
static int path_load(Unit *u) {
@ -340,19 +356,11 @@ static int path_load(Unit *u) {
if (u->load_state == UNIT_LOADED) {
if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
Unit *x;
r = path_add_trigger_dependencies(p);
if (r < 0)
return r;
r = unit_load_related_unit(u, ".service", &x);
if (r < 0)
return r;
r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
if (r < 0)
return r;
}
r = path_add_mount_links(p);
r = path_add_mount_dependencies(p);
if (r < 0)
return r;

View File

@ -124,7 +124,8 @@ static int scope_add_default_dependencies(Scope *s) {
r = unit_add_two_dependencies_by_name(
UNIT(s),
UNIT_BEFORE, UNIT_CONFLICTS,
SPECIAL_SHUTDOWN_TARGET, NULL, true);
SPECIAL_SHUTDOWN_TARGET, NULL, true,
UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;

View File

@ -562,7 +562,7 @@ static int service_add_default_dependencies(Service *s) {
* require it, so that we fail if we can't acquire
* it. */
r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
} else {
@ -570,7 +570,7 @@ static int service_add_default_dependencies(Service *s) {
/* In the --user instance there's no sysinit.target,
* in that case require basic.target instead. */
r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true);
r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
}
@ -578,12 +578,12 @@ static int service_add_default_dependencies(Service *s) {
/* Second, if the rest of the base system is in the same
* transaction, order us after it, but do not pull it in or
* even require it. */
r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_BASIC_TARGET, NULL, true);
r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_BASIC_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
/* Third, add us in for normal shutdown. */
return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
}
static void service_fix_output(Service *s) {
@ -612,12 +612,12 @@ static int service_setup_bus_name(Service *s) {
if (!s->bus_name)
return 0;
r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true);
r = unit_add_dependency_by_name(UNIT(s), UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to add dependency on " SPECIAL_DBUS_SOCKET ": %m");
/* We always want to be ordered against dbus.socket if both are in the transaction. */
r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_DBUS_SOCKET, NULL, true);
r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_DBUS_SOCKET, NULL, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to add dependency on " SPECIAL_DBUS_SOCKET ": %m");
@ -1103,11 +1103,12 @@ static int service_collect_fds(Service *s,
rn_socket_fds = 1;
} else {
Iterator i;
void *v;
Unit *u;
/* Pass all our configured sockets for singleton services */
SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) {
HASHMAP_FOREACH_KEY(v, u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) {
_cleanup_free_ int *cfds = NULL;
Socket *sock;
int cn_fds;
@ -3617,7 +3618,7 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context
return r;
}
r = unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false);
r = unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false, UNIT_DEPENDENCY_IMPLICIT);
if (r < 0)
return r;

View File

@ -97,7 +97,7 @@ static int slice_add_default_dependencies(Slice *s) {
r = unit_add_two_dependencies_by_name(
UNIT(s),
UNIT_BEFORE, UNIT_CONFLICTS,
SPECIAL_SHUTDOWN_TARGET, NULL, true);
SPECIAL_SHUTDOWN_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;

View File

@ -250,7 +250,7 @@ int socket_instantiate_service(Socket *s) {
unit_ref_set(&s->service, u);
return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false);
return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false, UNIT_DEPENDENCY_IMPLICIT);
}
static bool have_non_accept_socket(Socket *s) {
@ -273,7 +273,7 @@ static bool have_non_accept_socket(Socket *s) {
return false;
}
static int socket_add_mount_links(Socket *s) {
static int socket_add_mount_dependencies(Socket *s) {
SocketPort *p;
int r;
@ -290,7 +290,7 @@ static int socket_add_mount_links(Socket *s) {
if (!path)
continue;
r = unit_require_mounts_for(UNIT(s), path);
r = unit_require_mounts_for(UNIT(s), path, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
}
@ -298,7 +298,7 @@ static int socket_add_mount_links(Socket *s) {
return 0;
}
static int socket_add_device_link(Socket *s) {
static int socket_add_device_dependencies(Socket *s) {
char *t;
assert(s);
@ -307,7 +307,7 @@ static int socket_add_device_link(Socket *s) {
return 0;
t = strjoina("/sys/subsystem/net/devices/", s->bind_to_device);
return unit_add_node_link(UNIT(s), t, false, UNIT_BINDS_TO);
return unit_add_node_dependency(UNIT(s), t, false, UNIT_BINDS_TO, UNIT_DEPENDENCY_FILE);
}
static int socket_add_default_dependencies(Socket *s) {
@ -317,17 +317,17 @@ static int socket_add_default_dependencies(Socket *s) {
if (!UNIT(s)->default_dependencies)
return 0;
r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true);
r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SOCKETS_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) {
r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
}
return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
}
_pure_ static bool socket_has_exec(Socket *s) {
@ -378,16 +378,16 @@ static int socket_add_extras(Socket *s) {
unit_ref_set(&s->service, x);
}
r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(s->service), true);
r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(s->service), true, UNIT_DEPENDENCY_IMPLICIT);
if (r < 0)
return r;
}
r = socket_add_mount_links(s);
r = socket_add_mount_dependencies(s);
if (r < 0)
return r;
r = socket_add_device_link(s);
r = socket_add_device_dependencies(s);
if (r < 0)
return r;
@ -2261,13 +2261,14 @@ static void socket_enter_running(Socket *s, int cfd) {
}
if (cfd < 0) {
Iterator i;
Unit *other;
bool pending = false;
Unit *other;
Iterator i;
void *v;
/* If there's already a start pending don't bother to
* do anything */
SET_FOREACH(other, UNIT(s)->dependencies[UNIT_TRIGGERS], i)
HASHMAP_FOREACH_KEY(v, other, UNIT(s)->dependencies[UNIT_TRIGGERS], i)
if (unit_active_or_pending(other)) {
pending = true;
break;

View File

@ -197,7 +197,7 @@ static int swap_arm_timer(Swap *s, usec_t usec) {
return 0;
}
static int swap_add_device_links(Swap *s) {
static int swap_add_device_dependencies(Swap *s) {
assert(s);
if (!s->what)
@ -207,12 +207,12 @@ static int swap_add_device_links(Swap *s) {
return 0;
if (is_device_path(s->what))
return unit_add_node_link(UNIT(s), s->what, MANAGER_IS_SYSTEM(UNIT(s)->manager), UNIT_BINDS_TO);
return unit_add_node_dependency(UNIT(s), s->what, MANAGER_IS_SYSTEM(UNIT(s)->manager), UNIT_BINDS_TO, UNIT_DEPENDENCY_FILE);
else
/* File based swap devices need to be ordered after
* systemd-remount-fs.service, since they might need a
* writable file system. */
return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, NULL, true);
return unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, NULL, true, UNIT_DEPENDENCY_FILE);
}
static int swap_add_default_dependencies(Swap *s) {
@ -231,11 +231,11 @@ static int swap_add_default_dependencies(Swap *s) {
/* swap units generated for the swap dev links are missing the
* ordering dep against the swap target. */
r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SWAP_TARGET, NULL, true);
r = unit_add_dependency_by_name(UNIT(s), UNIT_BEFORE, SPECIAL_SWAP_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
}
static int swap_verify(Swap *s) {
@ -323,11 +323,11 @@ static int swap_load(Unit *u) {
return r;
}
r = unit_require_mounts_for(UNIT(s), s->what);
r = unit_require_mounts_for(UNIT(s), s->what, UNIT_DEPENDENCY_IMPLICIT);
if (r < 0)
return r;
r = swap_add_device_links(s);
r = swap_add_device_dependencies(s);
if (r < 0)
return r;

View File

@ -56,8 +56,6 @@ static int target_add_default_dependencies(Target *t) {
UNIT_PART_OF
};
Iterator i;
Unit *other;
int r;
unsigned k;
@ -66,23 +64,26 @@ static int target_add_default_dependencies(Target *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. */
for (k = 0; k < ELEMENTSOF(deps); k++)
SET_FOREACH(other, UNIT(t)->dependencies[deps[k]], i) {
for (k = 0; k < ELEMENTSOF(deps); k++) {
Unit *other;
Iterator i;
void *v;
HASHMAP_FOREACH_KEY(v, other, UNIT(t)->dependencies[deps[k]], i) {
r = unit_add_default_target_dependency(other, UNIT(t));
if (r < 0)
return r;
}
}
if (unit_has_name(UNIT(t), SPECIAL_SHUTDOWN_TARGET))
return 0;
/* Make sure targets are unloaded on shutdown */
return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
}
static int target_load(Unit *u) {

View File

@ -105,18 +105,18 @@ static int timer_add_default_dependencies(Timer *t) {
if (!UNIT(t)->default_dependencies)
return 0;
r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true);
r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true);
r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
LIST_FOREACH(value, v, t->values) {
if (v->base == TIMER_CALENDAR) {
r = unit_add_dependency_by_name(UNIT(t), UNIT_AFTER, SPECIAL_TIME_SYNC_TARGET, NULL, true);
r = unit_add_dependency_by_name(UNIT(t), UNIT_AFTER, SPECIAL_TIME_SYNC_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
if (r < 0)
return r;
break;
@ -124,7 +124,23 @@ static int timer_add_default_dependencies(Timer *t) {
}
}
return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
}
static int timer_add_trigger_dependencies(Timer *t) {
Unit *x;
int r;
assert(t);
if (!hashmap_isempty(UNIT(t)->dependencies[UNIT_TRIGGERS]))
return 0;
r = unit_load_related_unit(UNIT(t), ".service", &x);
if (r < 0)
return r;
return unit_add_two_dependencies(UNIT(t), UNIT_BEFORE, UNIT_TRIGGERS, x, true, UNIT_DEPENDENCY_IMPLICIT);
}
static int timer_setup_persistent(Timer *t) {
@ -137,7 +153,7 @@ static int timer_setup_persistent(Timer *t) {
if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers");
r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers", UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
@ -179,17 +195,9 @@ static int timer_load(Unit *u) {
if (u->load_state == UNIT_LOADED) {
if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
Unit *x;
r = unit_load_related_unit(u, ".service", &x);
if (r < 0)
return r;
r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
if (r < 0)
return r;
}
r = timer_add_trigger_dependencies(t);
if (r < 0)
return r;
r = timer_setup_persistent(t);
if (r < 0)

View File

@ -361,6 +361,7 @@ 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) {
Iterator i;
Unit *u;
void *v;
int r;
assert(tr);
@ -452,7 +453,7 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
/* We assume that the dependencies are bidirectional, and
* hence can ignore UNIT_AFTER */
SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) {
HASHMAP_FOREACH_KEY(v, u, j->unit->dependencies[UNIT_BEFORE], i) {
Job *o;
/* Is there a job for this unit? */
@ -860,14 +861,15 @@ 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) {
Iterator i;
Unit *dep;
JobType nt;
Unit *dep;
void *v;
int r;
assert(tr);
assert(unit);
SET_FOREACH(dep, unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) {
HASHMAP_FOREACH_KEY(v, dep, unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) {
nt = job_type_collapse(JOB_TRY_RELOAD, dep);
if (nt == JOB_NOP)
continue;
@ -892,11 +894,13 @@ int transaction_add_job_and_dependencies(
bool ignore_requirements,
bool ignore_order,
sd_bus_error *e) {
Job *ret;
bool is_new;
Iterator i;
Unit *dep;
Job *ret;
void *v;
int r;
bool is_new;
assert(tr);
assert(type < _JOB_TYPE_MAX);
@ -969,7 +973,7 @@ int transaction_add_job_and_dependencies(
/* Finally, recursively add in all dependencies. */
if (IN_SET(type, JOB_START, JOB_RESTART)) {
SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
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 */
@ -979,7 +983,7 @@ int transaction_add_job_and_dependencies(
}
}
SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) {
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_BINDS_TO], i) {
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 */
@ -989,7 +993,7 @@ int transaction_add_job_and_dependencies(
}
}
SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) {
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_WANTS], i) {
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. */
@ -1001,7 +1005,7 @@ int transaction_add_job_and_dependencies(
}
}
SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) {
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_REQUISITE], i) {
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 */
@ -1011,7 +1015,7 @@ int transaction_add_job_and_dependencies(
}
}
SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) {
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_CONFLICTS], i) {
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 */
@ -1021,7 +1025,7 @@ int transaction_add_job_and_dependencies(
}
}
SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) {
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) {
r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, false, false, ignore_order, e);
if (r < 0) {
log_unit_warning(dep,
@ -1050,7 +1054,7 @@ int transaction_add_job_and_dependencies(
ptype = type == JOB_RESTART ? JOB_TRY_RESTART : type;
for (j = 0; j < ELEMENTSOF(propagate_deps); j++)
SET_FOREACH(dep, ret->unit->dependencies[propagate_deps[j]], i) {
HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[propagate_deps[j]], i) {
JobType nt;
nt = job_type_collapse(ptype, dep);

View File

@ -421,25 +421,25 @@ void unit_add_to_dbus_queue(Unit *u) {
u->in_dbus_queue = true;
}
static void bidi_set_free(Unit *u, Set *s) {
Iterator i;
static void bidi_set_free(Unit *u, Hashmap *h) {
Unit *other;
Iterator i;
void *v;
assert(u);
/* Frees the set and makes sure we are dropped from the
* inverse pointers */
/* Frees the hashmap and makes sure we are dropped from the inverse pointers */
SET_FOREACH(other, s, i) {
HASHMAP_FOREACH_KEY(v, other, h, i) {
UnitDependency d;
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
set_remove(other->dependencies[d], u);
hashmap_remove(other->dependencies[d], u);
unit_add_to_gc_queue(other);
}
set_free(s);
hashmap_free(h);
}
static void unit_remove_transient(Unit *u) {
@ -474,30 +474,37 @@ static void unit_remove_transient(Unit *u) {
}
static void unit_free_requires_mounts_for(Unit *u) {
char **j;
assert(u);
STRV_FOREACH(j, u->requires_mounts_for) {
char s[strlen(*j) + 1];
for (;;) {
_cleanup_free_ char *path;
PATH_FOREACH_PREFIX_MORE(s, *j) {
char *y;
Set *x;
path = hashmap_steal_first_key(u->requires_mounts_for);
if (!path)
break;
else {
char s[strlen(path) + 1];
x = hashmap_get2(u->manager->units_requiring_mounts_for, s, (void**) &y);
if (!x)
continue;
PATH_FOREACH_PREFIX_MORE(s, path) {
char *y;
Set *x;
set_remove(x, u);
x = hashmap_get2(u->manager->units_requiring_mounts_for, s, (void**) &y);
if (!x)
continue;
if (set_isempty(x)) {
hashmap_remove(u->manager->units_requiring_mounts_for, y);
free(y);
set_free(x);
(void) set_remove(x, u);
if (set_isempty(x)) {
(void) hashmap_remove(u->manager->units_requiring_mounts_for, y);
free(y);
set_free(x);
}
}
}
}
u->requires_mounts_for = strv_free(u->requires_mounts_for);
u->requires_mounts_for = hashmap_free(u->requires_mounts_for);
}
static void unit_done(Unit *u) {
@ -651,20 +658,33 @@ const char* unit_sub_state_to_string(Unit *u) {
return UNIT_VTABLE(u)->sub_state_to_string(u);
}
static int complete_move(Set **s, Set **other) {
int r;
static int set_complete_move(Set **s, Set **other) {
assert(s);
assert(other);
if (!other)
return 0;
if (*s)
return set_move(*s, *other);
else {
*s = *other;
*other = NULL;
}
return 0;
}
static int hashmap_complete_move(Hashmap **s, Hashmap **other) {
assert(s);
assert(other);
if (!*other)
return 0;
if (*s) {
r = set_move(*s, *other);
if (r < 0)
return r;
} else {
if (*s)
return hashmap_move(*s, *other);
else {
*s = *other;
*other = NULL;
}
@ -680,7 +700,7 @@ static int merge_names(Unit *u, Unit *other) {
assert(u);
assert(other);
r = complete_move(&u->names, &other->names);
r = set_complete_move(&u->names, &other->names);
if (r < 0)
return r;
@ -710,48 +730,73 @@ static int reserve_dependencies(Unit *u, Unit *other, UnitDependency d) {
return 0;
/* merge_dependencies() will skip a u-on-u dependency */
n_reserve = set_size(other->dependencies[d]) - !!set_get(other->dependencies[d], u);
n_reserve = hashmap_size(other->dependencies[d]) - !!hashmap_get(other->dependencies[d], u);
return set_reserve(u->dependencies[d], n_reserve);
return hashmap_reserve(u->dependencies[d], n_reserve);
}
static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitDependency d) {
Iterator i;
Unit *back;
void *v;
int r;
/* Merges all dependencies of type 'd' of the unit 'other' into the deps of the unit 'u' */
assert(u);
assert(other);
assert(d < _UNIT_DEPENDENCY_MAX);
/* Fix backwards pointers */
SET_FOREACH(back, other->dependencies[d], i) {
/* Fix backwards pointers. Let's iterate through all dependendent units of the other unit. */
HASHMAP_FOREACH_KEY(v, back, other->dependencies[d], i) {
UnitDependency k;
/* 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 (k = 0; k < _UNIT_DEPENDENCY_MAX; k++) {
/* Do not add dependencies between u and itself */
if (back == u) {
if (set_remove(back->dependencies[k], other))
/* Do not add dependencies between u and itself. */
if (hashmap_remove(back->dependencies[k], other))
maybe_warn_about_dependency(u, other_id, k);
} else {
r = set_remove_and_put(back->dependencies[k], other, u);
if (r == -EEXIST)
set_remove(back->dependencies[k], other);
else
assert(r >= 0 || r == -ENOENT);
UnitDependencyInfo di_u, di_other, di_merged;
/* 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);
di_merged = (UnitDependencyInfo) {
.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); */
}
}
}
/* Also do not move dependencies on u to itself */
back = set_remove(other->dependencies[d], u);
back = hashmap_remove(other->dependencies[d], u);
if (back)
maybe_warn_about_dependency(u, other_id, d);
/* The move cannot fail. The caller must have performed a reservation. */
assert_se(complete_move(&u->dependencies[d], &other->dependencies[d]) == 0);
assert_se(hashmap_complete_move(&u->dependencies[d], &other->dependencies[d]) == 0);
other->dependencies[d] = set_free(other->dependencies[d]);
other->dependencies[d] = hashmap_free(other->dependencies[d]);
}
int unit_merge(Unit *u, Unit *other) {
@ -876,19 +921,19 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
assert(c);
if (c->working_directory) {
r = unit_require_mounts_for(u, c->working_directory);
r = unit_require_mounts_for(u, c->working_directory, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
}
if (c->root_directory) {
r = unit_require_mounts_for(u, c->root_directory);
r = unit_require_mounts_for(u, c->root_directory, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
}
if (c->root_image) {
r = unit_require_mounts_for(u, c->root_image);
r = unit_require_mounts_for(u, c->root_image, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
}
@ -904,7 +949,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
if (!p)
return -ENOMEM;
r = unit_require_mounts_for(u, p);
r = unit_require_mounts_for(u, p, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
}
@ -917,12 +962,12 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
const char *p;
FOREACH_STRING(p, "/tmp", "/var/tmp") {
r = unit_require_mounts_for(u, p);
r = unit_require_mounts_for(u, p, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
}
r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_TMPFILES_SETUP_SERVICE, NULL, true);
r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_TMPFILES_SETUP_SERVICE, NULL, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
}
@ -940,7 +985,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
/* If syslog or kernel logging is requested, make sure our own
* logging daemon is run first. */
r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true);
r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, NULL, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
@ -956,6 +1001,48 @@ const char *unit_description(Unit *u) {
return strna(u->id);
}
static void print_unit_dependency_mask(FILE *f, const char *kind, UnitDependencyMask mask, bool *space) {
const struct {
UnitDependencyMask mask;
const char *name;
} table[] = {
{ UNIT_DEPENDENCY_FILE, "file" },
{ UNIT_DEPENDENCY_IMPLICIT, "implicit" },
{ UNIT_DEPENDENCY_DEFAULT, "default" },
{ UNIT_DEPENDENCY_UDEV, "udev" },
{ UNIT_DEPENDENCY_PATH, "path" },
{ UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT, "mountinfo-implicit" },
{ UNIT_DEPENDENCY_MOUNTINFO_DEFAULT, "mountinfo-default" },
{ UNIT_DEPENDENCY_PROC_SWAP, "proc-swap" },
};
size_t i;
assert(f);
assert(kind);
assert(space);
for (i = 0; i < ELEMENTSOF(table); i++) {
if (mask == 0)
break;
if ((mask & table[i].mask) == table[i].mask) {
if (*space)
fputc(' ', f);
else
*space = true;
fputs(kind, f);
fputs("-", f);
fputs(table[i].name, f);
mask &= ~table[i].mask;
}
}
assert(mask == 0);
}
void unit_dump(Unit *u, FILE *f, const char *prefix) {
char *t, **j;
UnitDependency d;
@ -1084,20 +1171,35 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
prefix, yes_no(u->assert_result));
for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
UnitDependencyInfo di;
Unit *other;
SET_FOREACH(other, u->dependencies[d], i)
fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), other->id);
HASHMAP_FOREACH_KEY(di.data, other, u->dependencies[d], i) {
bool space = false;
fprintf(f, "%s\t%s: %s (", prefix, unit_dependency_to_string(d), other->id);
print_unit_dependency_mask(f, "origin", di.origin_mask, &space);
print_unit_dependency_mask(f, "destination", di.destination_mask, &space);
fputs(")\n", f);
}
}
if (!strv_isempty(u->requires_mounts_for)) {
fprintf(f,
"%s\tRequiresMountsFor:", prefix);
if (!hashmap_isempty(u->requires_mounts_for)) {
UnitDependencyInfo di;
const char *path;
STRV_FOREACH(j, u->requires_mounts_for)
fprintf(f, " %s", *j);
HASHMAP_FOREACH_KEY(di.data, path, u->requires_mounts_for, i) {
bool space = false;
fputs("\n", f);
fprintf(f, "%s\tRequiresMountsFor: %s (", prefix, path);
print_unit_dependency_mask(f, "origin", di.origin_mask, &space);
print_unit_dependency_mask(f, "destination", di.destination_mask, &space);
fputs(")\n", f);
}
}
if (u->load_state == UNIT_LOADED) {
@ -1198,10 +1300,10 @@ int unit_add_default_target_dependency(Unit *u, Unit *target) {
return 0;
/* Don't create loops */
if (set_get(target->dependencies[UNIT_BEFORE], u))
if (hashmap_get(target->dependencies[UNIT_BEFORE], u))
return 0;
return unit_add_dependency(target, UNIT_AFTER, u, true);
return unit_add_dependency(target, UNIT_AFTER, u, true, UNIT_DEPENDENCY_DEFAULT);
}
static int unit_add_target_dependencies(Unit *u) {
@ -1213,48 +1315,59 @@ static int unit_add_target_dependencies(Unit *u) {
UNIT_BOUND_BY
};
Unit *target;
Iterator i;
unsigned k;
int r = 0;
assert(u);
for (k = 0; k < ELEMENTSOF(deps); k++)
SET_FOREACH(target, u->dependencies[deps[k]], i) {
for (k = 0; k < ELEMENTSOF(deps); k++) {
Unit *target;
Iterator i;
void *v;
HASHMAP_FOREACH_KEY(v, target, u->dependencies[deps[k]], i) {
r = unit_add_default_target_dependency(u, target);
if (r < 0)
return r;
}
}
return r;
}
static int unit_add_slice_dependencies(Unit *u) {
UnitDependencyMask mask;
assert(u);
if (!UNIT_HAS_CGROUP_CONTEXT(u))
return 0;
/* Slice units are implicitly ordered against their parent slices (as this relationship is encoded in the
name), while all other units are ordered based on configuration (as in their case Slice= configures the
relationship). */
mask = u->type == UNIT_SLICE ? UNIT_DEPENDENCY_IMPLICIT : UNIT_DEPENDENCY_FILE;
if (UNIT_ISSET(u->slice))
return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT_DEREF(u->slice), true);
return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT_DEREF(u->slice), true, mask);
if (unit_has_name(u, SPECIAL_ROOT_SLICE))
return 0;
return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_ROOT_SLICE, NULL, true);
return unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_ROOT_SLICE, NULL, true, mask);
}
static int unit_add_mount_dependencies(Unit *u) {
char **i;
UnitDependencyInfo di;
const char *path;
Iterator i;
int r;
assert(u);
STRV_FOREACH(i, u->requires_mounts_for) {
char prefix[strlen(*i) + 1];
HASHMAP_FOREACH_KEY(di.data, path, u->requires_mounts_for, i) {
char prefix[strlen(path) + 1];
PATH_FOREACH_PREFIX_MORE(prefix, *i) {
PATH_FOREACH_PREFIX_MORE(prefix, path) {
_cleanup_free_ char *p = NULL;
Unit *m;
@ -1278,12 +1391,12 @@ static int unit_add_mount_dependencies(Unit *u) {
if (m->load_state != UNIT_LOADED)
continue;
r = unit_add_dependency(u, UNIT_AFTER, m, true);
r = unit_add_dependency(u, UNIT_AFTER, m, true, di.origin_mask);
if (r < 0)
return r;
if (m->fragment_path) {
r = unit_add_dependency(u, UNIT_REQUIRES, m, true);
r = unit_add_dependency(u, UNIT_REQUIRES, m, true, di.origin_mask);
if (r < 0)
return r;
}
@ -1369,7 +1482,7 @@ int unit_load(Unit *u) {
if (r < 0)
goto fail;
if (u->on_failure_job_mode == JOB_ISOLATE && set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) {
if (u->on_failure_job_mode == JOB_ISOLATE && hashmap_size(u->dependencies[UNIT_ON_FAILURE]) > 1) {
log_unit_error(u, "More than one OnFailure= dependencies specified but OnFailureJobMode=isolate set. Refusing.");
r = -EINVAL;
goto fail;
@ -1584,6 +1697,7 @@ bool unit_shall_confirm_spawn(Unit *u) {
static bool unit_verify_deps(Unit *u) {
Unit *other;
Iterator j;
void *v;
assert(u);
@ -1592,9 +1706,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. */
SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], j) {
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BINDS_TO], j) {
if (!set_contains(u->dependencies[UNIT_AFTER], other))
if (!hashmap_contains(u->dependencies[UNIT_AFTER], other))
continue;
if (!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(other))) {
@ -1797,7 +1911,7 @@ bool unit_can_reload(Unit *u) {
if (UNIT_VTABLE(u)->can_reload)
return UNIT_VTABLE(u)->can_reload(u);
if (!set_isempty(u->dependencies[UNIT_PROPAGATES_RELOAD_TO]))
if (!hashmap_isempty(u->dependencies[UNIT_PROPAGATES_RELOAD_TO]))
return true;
return UNIT_VTABLE(u)->reload;
@ -1814,8 +1928,6 @@ static void unit_check_unneeded(Unit *u) {
UNIT_BOUND_BY,
};
Unit *other;
Iterator i;
unsigned j;
int r;
@ -1830,10 +1942,15 @@ static void unit_check_unneeded(Unit *u) {
if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)))
return;
for (j = 0; j < ELEMENTSOF(needed_dependencies); j++)
SET_FOREACH(other, u->dependencies[needed_dependencies[j]], i)
for (j = 0; j < ELEMENTSOF(needed_dependencies); j++) {
Unit *other;
Iterator i;
void *v;
HASHMAP_FOREACH_KEY(v, other, u->dependencies[needed_dependencies[j]], i)
if (unit_active_or_pending(other))
return;
}
/* If stopping a unit fails continuously we might enter a stop
* loop here, hence stop acting on the service being
@ -1856,6 +1973,7 @@ static void unit_check_binds_to(Unit *u) {
bool stop = false;
Unit *other;
Iterator i;
void *v;
int r;
assert(u);
@ -1866,7 +1984,7 @@ static void unit_check_binds_to(Unit *u) {
if (unit_active_state(u) != UNIT_ACTIVE)
return;
SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i) {
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BINDS_TO], i) {
if (other->job)
continue;
@ -1904,65 +2022,68 @@ static void unit_check_binds_to(Unit *u) {
static void retroactively_start_dependencies(Unit *u) {
Iterator i;
Unit *other;
void *v;
assert(u);
assert(UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)));
SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUIRES], i)
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);
SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BINDS_TO], i)
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);
SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
if (!set_get(u->dependencies[UNIT_AFTER], other) &&
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_WANTS], i)
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_FAIL, NULL, NULL);
SET_FOREACH(other, u->dependencies[UNIT_CONFLICTS], i)
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_CONFLICTS], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL);
SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i)
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_CONFLICTED_BY], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL);
}
static void retroactively_stop_dependencies(Unit *u) {
Iterator i;
Unit *other;
Iterator i;
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 */
SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BOUND_BY], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL);
}
static void check_unneeded_dependencies(Unit *u) {
Iterator i;
Unit *other;
Iterator i;
void *v;
assert(u);
assert(UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(u)));
/* Garbage collect services that might not be needed anymore, if enabled */
SET_FOREACH(other, u->dependencies[UNIT_REQUIRES], i)
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUIRES], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_WANTS], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
SET_FOREACH(other, u->dependencies[UNIT_REQUISITE], i)
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUISITE], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], i)
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BINDS_TO], i)
if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other)))
unit_check_unneeded(other);
}
@ -1970,15 +2091,16 @@ static void check_unneeded_dependencies(Unit *u) {
void unit_start_on_failure(Unit *u) {
Unit *other;
Iterator i;
void *v;
assert(u);
if (set_size(u->dependencies[UNIT_ON_FAILURE]) <= 0)
if (hashmap_size(u->dependencies[UNIT_ON_FAILURE]) <= 0)
return;
log_unit_info(u, "Triggering OnFailure= dependencies.");
SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) {
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_ON_FAILURE], i) {
int r;
r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, NULL, NULL);
@ -1990,10 +2112,11 @@ void unit_start_on_failure(Unit *u) {
void unit_trigger_notify(Unit *u) {
Unit *other;
Iterator i;
void *v;
assert(u);
SET_FOREACH(other, u->dependencies[UNIT_TRIGGERED_BY], i)
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_TRIGGERED_BY], i)
if (UNIT_VTABLE(other)->trigger_notify)
UNIT_VTABLE(other)->trigger_notify(other, u);
}
@ -2465,7 +2588,59 @@ static void maybe_warn_about_dependency(Unit *u, const char *other, UnitDependen
log_unit_warning(u, "Dependency %s=%s dropped, merged into %s", unit_dependency_to_string(dependency), strna(other), u->id);
}
int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference) {
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 ((info.origin_mask & origin_mask) == info.origin_mask &&
(info.destination_mask & 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,
Unit *other,
bool add_reference,
UnitDependencyMask mask) {
static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = {
[UNIT_REQUIRES] = UNIT_REQUIRED_BY,
@ -2491,8 +2666,8 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen
[UNIT_RELOAD_PROPAGATED_FROM] = UNIT_PROPAGATES_RELOAD_TO,
[UNIT_JOINS_NAMESPACE_OF] = UNIT_JOINS_NAMESPACE_OF,
};
int r, q = 0, v = 0, w = 0;
Unit *orig_u = u, *orig_other = other;
Unit *original_u = u, *original_other = other;
int r;
assert(u);
assert(d >= 0 && d < _UNIT_DEPENDENCY_MAX);
@ -2504,85 +2679,50 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen
/* We won't allow dependencies on ourselves. We will not
* consider them an error however. */
if (u == other) {
maybe_warn_about_dependency(orig_u, orig_other->id, d);
maybe_warn_about_dependency(original_u, original_other->id, d);
return 0;
}
if (d == UNIT_BEFORE && other->type == UNIT_DEVICE) {
if ((d == UNIT_BEFORE && other->type == UNIT_DEVICE) ||
(d == UNIT_AFTER && u->type == UNIT_DEVICE)) {
log_unit_warning(u, "Dependency Before=%s ignored (.device units cannot be delayed)", other->id);
return 0;
}
r = set_ensure_allocated(&u->dependencies[d], NULL);
r = unit_add_dependency_hashmap(u->dependencies + d, other, mask, 0);
if (r < 0)
return r;
if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) {
r = set_ensure_allocated(&other->dependencies[inverse_table[d]], NULL);
if (r < 0)
return r;
}
if (add_reference) {
r = set_ensure_allocated(&u->dependencies[UNIT_REFERENCES], NULL);
if (r < 0)
return r;
r = set_ensure_allocated(&other->dependencies[UNIT_REFERENCED_BY], NULL);
if (r < 0)
return r;
}
q = set_put(u->dependencies[d], other);
if (q < 0)
return q;
if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID && inverse_table[d] != d) {
v = set_put(other->dependencies[inverse_table[d]], u);
if (v < 0) {
r = v;
goto fail;
}
r = unit_add_dependency_hashmap(other->dependencies + inverse_table[d], u, 0, mask);
if (r < 0)
return r;
}
if (add_reference) {
w = set_put(u->dependencies[UNIT_REFERENCES], other);
if (w < 0) {
r = w;
goto fail;
}
r = set_put(other->dependencies[UNIT_REFERENCED_BY], u);
r = unit_add_dependency_hashmap(u->dependencies + UNIT_REFERENCES, other, mask, 0);
if (r < 0)
goto fail;
return r;
r = unit_add_dependency_hashmap(other->dependencies + UNIT_REFERENCED_BY, u, 0, mask);
if (r < 0)
return r;
}
unit_add_to_dbus_queue(u);
return 0;
fail:
if (q > 0)
set_remove(u->dependencies[d], other);
if (v > 0)
set_remove(other->dependencies[inverse_table[d]], u);
if (w > 0)
set_remove(u->dependencies[UNIT_REFERENCES], other);
return r;
}
int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference) {
int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference, UnitDependencyMask mask) {
int r;
assert(u);
r = unit_add_dependency(u, d, other, add_reference);
r = unit_add_dependency(u, d, other, add_reference, mask);
if (r < 0)
return r;
return unit_add_dependency(u, e, other, add_reference);
return unit_add_dependency(u, e, other, add_reference, mask);
}
static int resolve_template(Unit *u, const char *name, const char*path, char **buf, const char **ret) {
@ -2620,7 +2760,7 @@ static int resolve_template(Unit *u, const char *name, const char*path, char **b
return 0;
}
int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference) {
int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *path, bool add_reference, UnitDependencyMask mask) {
_cleanup_free_ char *buf = NULL;
Unit *other;
int r;
@ -2636,10 +2776,10 @@ int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, con
if (r < 0)
return r;
return unit_add_dependency(u, d, other, add_reference);
return unit_add_dependency(u, d, other, add_reference, mask);
}
int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference) {
int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference, UnitDependencyMask mask) {
_cleanup_free_ char *buf = NULL;
Unit *other;
int r;
@ -2655,7 +2795,7 @@ int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency
if (r < 0)
return r;
return unit_add_two_dependencies(u, d, e, other, add_reference);
return unit_add_two_dependencies(u, d, e, other, add_reference, mask);
}
int set_unit_path(const char *p) {
@ -3365,7 +3505,7 @@ void unit_deserialize_skip(FILE *f) {
}
int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep) {
int unit_add_node_dependency(Unit *u, const char *what, bool wants, UnitDependency dep, UnitDependencyMask mask) {
Unit *device;
_cleanup_free_ char *e = NULL;
int r;
@ -3397,12 +3537,12 @@ int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep
r = unit_add_two_dependencies(u, UNIT_AFTER,
MANAGER_IS_SYSTEM(u->manager) ? dep : UNIT_WANTS,
device, true);
device, true, mask);
if (r < 0)
return r;
if (wants) {
r = unit_add_dependency(device, UNIT_WANTS, u, false);
r = unit_add_dependency(device, UNIT_WANTS, u, false, mask);
if (r < 0)
return r;
}
@ -4222,23 +4362,26 @@ int unit_kill_context(
return wait_for_exit;
}
int unit_require_mounts_for(Unit *u, const char *path) {
int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask) {
char prefix[strlen(path) + 1], *p;
UnitDependencyInfo di;
int r;
assert(u);
assert(path);
/* Registers a unit for requiring a certain path and all its
* prefixes. We keep a simple array of these paths in the
* unit, since its usually short. However, we build a prefix
* table for all possible prefixes so that new appearing mount
* units can easily determine which units to make themselves a
* dependency of. */
/* Registers a unit for requiring a certain path and all its prefixes. We keep a hashtable of these paths in
* the unit (from the path to the UnitDependencyInfo structure indicating how to the dependency came to
* be). However, we build a prefix table for all possible prefixes so that new appearing mount units can easily
* determine which units to make themselves a dependency of. */
if (!path_is_absolute(path))
return -EINVAL;
r = hashmap_ensure_allocated(&u->requires_mounts_for, &string_hash_ops);
if (r < 0)
return r;
p = strdup(path);
if (!p)
return -ENOMEM;
@ -4250,14 +4393,20 @@ int unit_require_mounts_for(Unit *u, const char *path) {
return -EPERM;
}
if (strv_contains(u->requires_mounts_for, p)) {
if (hashmap_contains(u->requires_mounts_for, p)) {
free(p);
return 0;
}
r = strv_consume(&u->requires_mounts_for, p);
if (r < 0)
di = (UnitDependencyInfo) {
.origin_mask = mask
};
r = hashmap_put(u->requires_mounts_for, p, di.data);
if (r < 0) {
free(p);
return r;
}
PATH_FOREACH_PREFIX_MORE(prefix, p) {
Set *x;
@ -4299,8 +4448,9 @@ int unit_require_mounts_for(Unit *u, const char *path) {
int unit_setup_exec_runtime(Unit *u) {
ExecRuntime **rt;
size_t offset;
Iterator i;
Unit *other;
Iterator i;
void *v;
offset = UNIT_VTABLE(u)->exec_runtime_offset;
assert(offset > 0);
@ -4311,7 +4461,7 @@ int unit_setup_exec_runtime(Unit *u) {
return 0;
/* Try to get it from somebody else */
SET_FOREACH(other, u->dependencies[UNIT_JOINS_NAMESPACE_OF], i) {
HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_JOINS_NAMESPACE_OF], i) {
*rt = unit_get_exec_runtime(other);
if (*rt) {

View File

@ -61,6 +61,53 @@ static inline bool UNIT_IS_INACTIVE_OR_FAILED(UnitActiveState t) {
return IN_SET(t, UNIT_INACTIVE, UNIT_FAILED);
}
/* Stores the 'reason' a dependency was created as a bit mask, i.e. due to which configuration source it came to be. We
* use this so that we can selectively flush out parts of dependencies again. Note that the same dependency might be
* created as a result of multiple "reasons", hence the bitmask. */
typedef enum UnitDependencyMask {
/* Configured directly by the unit file, .wants/.requries symlink or drop-in, or as an immediate result of a
* non-dependency option configured that way. */
UNIT_DEPENDENCY_FILE = 1 << 0,
/* As unconditional implicit dependency (not affected by unit configuration — except by the unit name and
* type) */
UNIT_DEPENDENCY_IMPLICIT = 1 << 1,
/* A dependency effected by DefaultDependencies=yes. Note that dependencies marked this way are conceptually
* just a subset of UNIT_DEPENDENCY_FILE, as DefaultDependencies= is itself a unit file setting that can only
* be set in unit files. We make this two separate bits only to help debugging how dependencies came to be. */
UNIT_DEPENDENCY_DEFAULT = 1 << 2,
/* A dependency created from udev rules */
UNIT_DEPENDENCY_UDEV = 1 << 3,
/* A dependency created because of some unit's RequiresMountsFor= setting */
UNIT_DEPENDENCY_PATH = 1 << 4,
/* A dependency created because of data read from /proc/self/mountinfo and no other configuration source */
UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT = 1 << 5,
/* A dependency created because of data read from /proc/self/mountinfo, but conditionalized by
* DefaultDependencies= and thus also involving configuration from UNIT_DEPENDENCY_FILE sources */
UNIT_DEPENDENCY_MOUNTINFO_DEFAULT = 1 << 6,
/* A dependency created because of data read from /proc/swaps and no other configuration source */
UNIT_DEPENDENCY_PROC_SWAP = 1 << 7,
_UNIT_DEPENDENCY_MASK_FULL = (1 << 8) - 1,
} UnitDependencyMask;
/* The Unit's dependencies[] hashmaps use this structure as value. It has the same size as a void pointer, and thus can
* be stored directly as hashmap value, without any indirection. Note that this stores two masks, as both the origin
* and the destination of a dependency might have created it. */
typedef union UnitDependencyInfo {
void *data;
struct {
UnitDependencyMask origin_mask:16;
UnitDependencyMask destination_mask:16;
} _packed_;
} UnitDependencyInfo;
#include "job.h"
struct UnitRef {
@ -89,9 +136,13 @@ struct Unit {
char *instance;
Set *names;
Set *dependencies[_UNIT_DEPENDENCY_MAX];
char **requires_mounts_for;
/* 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];
/* Similar, for RequiresMountsFor= path dependencies. The key is the path, the value the UnitDependencyInfo type */
Hashmap *requires_mounts_for;
char *description;
char **documentation;
@ -492,7 +543,7 @@ extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX];
#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_TRIGGER(u) ((Unit*) set_first((u)->dependencies[UNIT_TRIGGERS]))
#define UNIT_TRIGGER(u) ((Unit*) hashmap_first_key((u)->dependencies[UNIT_TRIGGERS]))
DEFINE_CAST(SERVICE, Service);
DEFINE_CAST(SOCKET, Socket);
@ -512,11 +563,11 @@ void unit_free(Unit *u);
int unit_new_for_name(Manager *m, size_t size, const char *name, Unit **ret);
int unit_add_name(Unit *u, const char *name);
int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference);
int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference);
int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference, UnitDependencyMask mask);
int unit_add_two_dependencies(Unit *u, UnitDependency d, UnitDependency e, Unit *other, bool add_reference, UnitDependencyMask mask);
int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference);
int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference);
int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name, const char *filename, bool add_reference, UnitDependencyMask mask);
int unit_add_two_dependencies_by_name(Unit *u, UnitDependency d, UnitDependency e, const char *name, const char *path, bool add_reference, UnitDependencyMask mask);
int unit_add_exec_dependencies(Unit *u, ExecContext *c);
@ -596,7 +647,7 @@ int unit_serialize_item_escaped(Unit *u, FILE *f, const char *key, const char *v
int unit_serialize_item_fd(Unit *u, FILE *f, FDSet *fds, const char *key, int fd);
void unit_serialize_item_format(Unit *u, FILE *f, const char *key, const char *value, ...) _printf_(4,5);
int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency d);
int unit_add_node_dependency(Unit *u, const char *what, bool wants, UnitDependency d, UnitDependencyMask mask);
int unit_coldplug(Unit *u);
@ -651,7 +702,7 @@ int unit_kill_context(Unit *u, KillContext *c, KillOperation k, pid_t main_pid,
int unit_make_transient(Unit *u);
int unit_require_mounts_for(Unit *u, const char *path);
int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask);
bool unit_type_supported(UnitType t);