diff --git a/.mkosi/mkosi.fedora b/.mkosi/mkosi.fedora index 4cfdcb0b36..f54cbe08f3 100644 --- a/.mkosi/mkosi.fedora +++ b/.mkosi/mkosi.fedora @@ -74,3 +74,6 @@ BuildPackages= Packages= libidn2 + +BuildDirectory=mkosi.builddir +Cache=mkosi.cache diff --git a/TODO b/TODO index 4cf7eec7cd..63b8635a01 100644 --- a/TODO +++ b/TODO @@ -24,6 +24,48 @@ Janitorial Clean-ups: Features: +* let's log the "tainted" string at boot + +* Add NetworkNamespacePath= to specify a path to a network namespace + +* Add StandardInputData= and StandardInputText= for putting together data to + pass to a service through stdin + +* Add StandardInputPath=, StandardOutputPath=, StandardErrorPath= to connect a + service to a specific file. Be smart, and if the specified path refers to an + AF_UNIX socket, connect() to it. + +* maybe use SOURCE_DATE_EPOCH (i.e. the env var the reproducible builds folks + introduced) as the RTC epoch, instead of the mtime of NEWS. + +* Introduce GCMode= as unit file property or so, for tweaking the GC + logic. Specifically, there should be a way to tell systemd to collect + individual units even on failure. Then, make systemd-run --wait use this, so + that failed transient units in that case don't stick around. + +* add a way to lock down cgroup migration: a boolean, which when set for a unit + makes sure the processes in it can never migrate out of it + +* complain if a unit starts up and there are already processes in its cgroup + +* blog about fd store and restartable services + +* document Environment=SYSTEMD_LOG_LEVEL=debug drop-in in debugging document + +* add a way to remove fds from the fdstore by name, and make logind use it + +* in the long run: permit a system with /etc/machine-id linked to /dev/null, to + make it lose its identity, i.e. be anonymous. For this we'd have to patch + through the whole tree to make all code deal with the case where no machine + ID is available. + +* optionally, collect cgroup resource data, and store it in per-unit RRD files, + suitable for processing with rrdtool. Add bus API to access this data, and + possibly implement a CPULoad property based on it. + +* In journalctl add a way how "-o verbose" and suchlike can be tweaked to show + only a specific set of properties + * export UID ranges nspawns's --private-user and DynamicUser= uses in the systemd.pc pkg-config file, the same way we already expose the system user boundary there diff --git a/man/systemd-escape.xml b/man/systemd-escape.xml index fb20d2d94f..a2f2b9242f 100644 --- a/man/systemd-escape.xml +++ b/man/systemd-escape.xml @@ -73,6 +73,9 @@ special mode of escaping is applied instead, which assumes the string is already escaped but will escape everything that appears obviously non-escaped. + + For details on the escaping and unescaping algorithms see the relevant section in + systemd.unit5. @@ -107,11 +110,12 @@ - When escaping or unescaping a string, assume - it refers to a file system path. This eliminates leading, - trailing or duplicate / characters - and rejects . and .. - path components. + When escaping or unescaping a string, assume it refers to a file system path. This eliminates + leading, trailing or duplicate / characters and rejects . and + .. path components. This is particularly useful for generating strings suitable for + unescaping with the %f specifier in unit files, see + systemd.unit5. + @@ -172,6 +176,7 @@ systemd-nspawn@My\x20Container\x201.service systemd-nspawn@containerb.service sy See Also systemd1, + systemd.unit5, systemctl1 diff --git a/man/systemd.device.xml b/man/systemd.device.xml index 6edf1090d0..e121e151bc 100644 --- a/man/systemd.device.xml +++ b/man/systemd.device.xml @@ -112,35 +112,36 @@ The udev Database - The settings of device units may either be configured via - unit files, or directly from the udev database (which is - recommended). The following udev device properties are understood - by systemd: + Unit settings of device units may either be configured via unit files, or directly from the udev + database. The following udev device properties are understood by the service manager: SYSTEMD_WANTS= SYSTEMD_USER_WANTS= - Adds dependencies of type - Wants from the device unit to all listed - units. The first form is used by the system systemd instance, - the second by user systemd instances. Those settings may be - used to activate arbitrary units when a specific device - becomes available. + Adds dependencies of type Wants= from the device unit to the specified + units. SYSTEMD_WANTS= is read by the system service manager, + SYSTEMD_USER_WANTS= by user service manager instances. These properties may be used to + activate arbitrary units when a specific device becomes available. - Note that this and the other tags are not taken into - account unless the device is tagged with the - systemd string in the udev database, - because otherwise the device is not exposed as a systemd unit - (see above). + Note that this and the other udev device properties are not taken into account unless the device is + tagged with the systemd tag in the udev database, because otherwise the device is not + exposed as a systemd unit (see above). - Note that systemd will only act on - Wants dependencies when a device first - becomes active. It will not act on them if they are added to - devices that are already active. Use - SYSTEMD_READY= (see below) to influence on - which udev event to trigger the dependencies. - + Note that systemd will only act on Wants= dependencies when a device first becomes + active. It will not act on them if they are added to devices that are already active. Use + SYSTEMD_READY= (see below) to configure when a udev device shall be considered active, and + thus when to trigger the dependencies. + + + + The specified property value should be a space-separated list of valid unit names. If a unit template + name is specified (that is, a unit name containing an @ character indicating a unit name to + use for multiple instantiation, but with an empty instance name following the @), it will be + automatically instantiated by the device's sysfs path (that is: the path is escaped and + inserted as instance name into the template unit name). This is useful in order to instantiate a specific + template unit once for each device that appears and matches specific properties. @@ -152,20 +153,14 @@ SYSTEMD_READY= - If set to 0, systemd will consider this device - unplugged even if it shows up in the udev tree. If this - property is unset or set to 1, the device will be considered - plugged if it is visible in the udev tree. This property has - no influence on the behavior when a device disappears from the - udev tree. + If set to 0, systemd will consider this device unplugged even if it shows up in the udev + tree. If this property is unset or set to 1, the device will be considered plugged if it is visible in the udev + tree. - This option is useful to support devices that initially - show up in an uninitialized state in the tree, and for which a - changed event is generated the moment they - are fully set up. Note that SYSTEMD_WANTS= - (see above) is not acted on as long as - SYSTEMD_READY=0 is set for a - device. + This option is useful for devices that initially show up in an uninitialized state in the tree, and for + which a changed event is generated the moment they are fully set up. Note that + SYSTEMD_WANTS= (see above) is not acted on as long as SYSTEMD_READY=0 is + set for a device. diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index b9991bee76..d6feaa1817 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -213,22 +213,6 @@ socket-based activation which make dependencies implicit, resulting in a both simpler and more flexible system. - Some unit names reflect paths existing in the file system - namespace. Example: a device unit - dev-sda.device refers to a device with the - device node /dev/sda in the - file system namespace. If this applies, a special way to escape - the path name is used, so that the result is usable as part of a - filename. Basically, given a path, "/" is replaced by "-", and all - other characters which are not ASCII alphanumerics are replaced by - C-style "\x2d" escapes (except that "_" is never replaced and "." - is only replaced when it would be the first character in the - escaped path). The root directory "/" is encoded as single dash, - while otherwise the initial and ending "/" are removed from all - paths during transformation. This escaping is reversible. Properly - escaped paths can be generated using the - systemd-escape1 - command. Optionally, units may be instantiated from a template file at runtime. This allows creation of @@ -265,6 +249,32 @@ + + String Escaping for Inclusion in Unit Names + + Sometimes it is useful to convert arbitrary strings into unit names. To facilitate this, a method of string + escaping is used, in order to map strings containing arbitrary byte values (except NUL) into valid unit names and + their restricted character set. A common special case are unit names that reflect paths to objects in the file + system hierarchy. Example: a device unit dev-sda.device refers to a device with the device + node /dev/sda in the file system. + + The escaping algorithm operates as follows: given a string, any / character is replaced by + -, and all other characters which are not ASCII alphanumerics or _ are + replaced by C-style \x2d escapes. In addition, . is replaced with such a + C-style escape when it would appear as the first character in the escaped string. + + When the input qualifies as absolute file system path, this algorithm is extended slightly: the path to the + root directory / is encoded as single dash -. In addition, any leading, + trailing or duplicate / characters are removed from the string before transformation. Example: + /foo//bar/baz/ becomes foo-bar-baz. + + This escaping is fully reversible, as long as it is known whether the escaped string was a path (the + unescaping results are different for paths and non-path strings). The + systemd-escape1 command may be + used to apply and reverse escaping on arbitrary strings. Use systemd-escape --path to escape + path strings, and systemd-escape without otherwise. + + Implicit Dependencies @@ -1241,7 +1251,7 @@ %N Unescaped full unit name - Same as %n, but with escaping undone + Same as %n, but with escaping undone. This undoes the escaping used when generating unit names from arbitrary strings (see above). %p @@ -1266,7 +1276,7 @@ %f Unescaped filename - This is either the unescaped instance name (if applicable) with / prepended (if applicable), or the unescaped prefix name prepended with /. + This is either the unescaped instance name (if applicable) with / prepended (if applicable), or the unescaped prefix name prepended with /. This implements unescaping according to the rules for escaping absolute file system paths discussed above. %t diff --git a/mkosi.build b/mkosi.build index 456217fa5d..71251920ed 100755 --- a/mkosi.build +++ b/mkosi.build @@ -26,7 +26,9 @@ export LC_CTYPE=en_US.UTF-8 -[ -f "$BUILDDIR"/build.ninja ] || meson "$BUILDDIR" +sysvinit_path=`realpath /etc/init.d` + +[ -f "$BUILDDIR"/build.ninja ] || meson "$BUILDDIR" -D "sysvinit-path=$sysvinit_path" ninja -C "$BUILDDIR" all [ "$WITH_TESTS" = 0 ] || ninja -C "$BUILDDIR" test || ( RET="$?" ; cat "$BUILDDIR"/meson-logs/testlog.txt ; exit "$RET" ) ninja -C "$BUILDDIR" install diff --git a/src/basic/virt.c b/src/basic/virt.c index d5665edc1b..bacaabc5c0 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -216,6 +216,7 @@ static int detect_vm_dmi(void) { } static int detect_vm_xen(void) { + /* Check for Dom0 will be executed later in detect_vm_xen_dom0 Thats why we dont check the content of /proc/xen/capabilities here. */ if (access("/proc/xen/capabilities", F_OK) < 0) { @@ -224,8 +225,7 @@ static int detect_vm_xen(void) { } log_debug("Virtualization XEN found (/proc/xen/capabilities exists)"); - return VIRTUALIZATION_XEN; - + return VIRTUALIZATION_XEN; } static bool detect_vm_xen_dom0(void) { diff --git a/src/core/automount.c b/src/core/automount.c index 9b0d1ca429..befa3131ee 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -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; diff --git a/src/core/cgroup.c b/src/core/cgroup.c index e9f9447118..dff7d1dddc 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -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; diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index f87b52a266..c9da05335f 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -139,8 +139,10 @@ static int property_get_tainted( void *userdata, sd_bus_error *error) { - char buf[sizeof("split-usr:cgroups-missing:local-hwclock:")] = "", *e = buf; + char buf[sizeof("split-usr:cgroups-missing:local-hwclock:var-run-bad:")] = "", *e = buf; + _cleanup_free_ char *destination = NULL; Manager *m = userdata; + int r; assert(bus); assert(reply); @@ -155,6 +157,10 @@ static int property_get_tainted( if (clock_is_localtime(NULL) > 0) e = stpcpy(e, "local-hwclock:"); + r = readlink_malloc("/var/run", &destination); + if (r < 0 || !PATH_IN_SET(destination, "../run", "/run")) + e = stpcpy(e, "var-run-bad:"); + /* remove the last ':' */ if (e != buf) e[-1] = 0; diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c index 1f9c254c39..69a04fe2ae 100644 --- a/src/core/dbus-mount.c +++ b/src/core/dbus-mount.c @@ -36,7 +36,7 @@ static int property_get_what( sd_bus_error *error) { Mount *m = userdata; - const char *d; + const char *d = NULL; assert(bus); assert(reply); @@ -46,8 +46,6 @@ static int property_get_what( d = m->parameters_proc_self_mountinfo.what; else if (m->from_fragment && m->parameters_fragment.what) d = m->parameters_fragment.what; - else - d = ""; return sd_bus_message_append(reply, "s", d); } @@ -62,7 +60,7 @@ static int property_get_options( sd_bus_error *error) { Mount *m = userdata; - const char *d; + const char *d = NULL; assert(bus); assert(reply); @@ -72,8 +70,6 @@ static int property_get_options( d = m->parameters_proc_self_mountinfo.options; else if (m->from_fragment && m->parameters_fragment.options) d = m->parameters_fragment.options; - else - d = ""; return sd_bus_message_append(reply, "s", d); } @@ -87,13 +83,19 @@ static int property_get_type( void *userdata, sd_bus_error *error) { + const char *fstype = NULL; Mount *m = userdata; assert(bus); assert(reply); assert(m); - return sd_bus_message_append(reply, "s", mount_get_fstype(m)); + if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.fstype) + fstype = m->parameters_proc_self_mountinfo.fstype; + else if (m->from_fragment && m->parameters_fragment.fstype) + fstype = m->parameters_fragment.fstype; + + return sd_bus_message_append(reply, "s", fstype); } static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, mount_result, MountResult); diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index a9a68826b4..8fc02ab744 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -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; diff --git a/src/core/device.c b/src/core/device.c index 3915b2637d..a0150751cd 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -256,60 +256,89 @@ static int device_update_description(Unit *u, struct udev_device *dev, const cha } static int device_add_udev_wants(Unit *u, struct udev_device *dev) { - const char *wants, *property, *p; + const char *wants, *property; int r; assert(u); assert(dev); property = MANAGER_IS_USER(u->manager) ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS"; + wants = udev_device_get_property_value(dev, property); - for (p = wants;;) { + if (!wants) + return 0; + + for (;;) { _cleanup_free_ char *word = NULL, *k = NULL; - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + r = extract_first_word(&wants, &word, NULL, EXTRACT_QUOTES); if (r == 0) return 0; if (r == -ENOMEM) return log_oom(); if (r < 0) - return log_unit_error_errno(u, r, "Failed to add parse %s: %m", property); + return log_unit_error_errno(u, r, "Failed to parse property %s with value %s: %m", property, wants); - r = unit_name_mangle(word, UNIT_NAME_NOGLOB, &k); - if (r < 0) - return log_unit_error_errno(u, r, "Failed to mangle unit name \"%s\": %m", word); + if (unit_name_is_valid(word, UNIT_NAME_TEMPLATE) && DEVICE(u)->sysfs) { + _cleanup_free_ char *escaped = NULL; - r = unit_add_dependency_by_name(u, UNIT_WANTS, k, NULL, true); + /* If the unit name is specified as template, then automatically fill in the sysfs path of the + * device as instance name, properly escaped. */ + + r = unit_name_path_escape(DEVICE(u)->sysfs, &escaped); + if (r < 0) + return log_unit_error_errno(u, r, "Failed to escape %s: %m", DEVICE(u)->sysfs); + + r = unit_name_replace_instance(word, escaped, &k); + if (r < 0) + return log_unit_error_errno(u, r, "Failed to build %s instance of template %s: %m", escaped, word); + } else { + /* If this is not a template, then let's mangle it so, that it becomes a valid unit name. */ + + r = unit_name_mangle(word, UNIT_NAME_NOGLOB, &k); + 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, UNIT_DEPENDENCY_UDEV); if (r < 0) - return log_unit_error_errno(u, r, "Failed to add wants dependency: %m"); + return log_unit_error_errno(u, r, "Failed to add Wants= dependency: %m"); } } -static bool device_is_bound_by_mounts(Unit *d, struct udev_device *dev) { +static bool device_is_bound_by_mounts(Device *d, struct udev_device *dev) { const char *bound_by; - int r = false; + int r; assert(d); assert(dev); bound_by = udev_device_get_property_value(dev, "SYSTEMD_MOUNT_DEVICE_BOUND"); - if (bound_by) - r = parse_boolean(bound_by) > 0; + if (bound_by) { + r = parse_boolean(bound_by); + if (r < 0) + log_warning_errno(r, "Failed to parse SYSTEMD_MOUNT_DEVICE_BOUND='%s' udev property of %s, ignoring: %m", bound_by, strna(d->sysfs)); - DEVICE(d)->bind_mounts = r; - return r; + d->bind_mounts = r > 0; + } else + d->bind_mounts = false; + + return d->bind_mounts; } 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; } @@ -337,23 +366,26 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa return log_error_errno(r, "Failed to generate unit name from device path: %m"); u = manager_get_unit(m, e); + if (u) { + /* The device unit can still be present even if the device was unplugged: a mount unit can reference it hence + * preventing the GC to have garbaged it. That's desired since the device unit may have a dependency on the + * mount unit which was added during the loading of the later. */ + if (dev && DEVICE(u)->state == DEVICE_PLUGGED) { - /* The device unit can still be present even if the device was - * unplugged: a mount unit can reference it hence preventing - * the GC to have garbaged it. That's desired since the device - * unit may have a dependency on the mount unit which was - * added during the loading of the later. */ - if (dev && u && DEVICE(u)->state == DEVICE_PLUGGED) { - /* This unit is in plugged state: we're sure it's - * attached to a device. */ - if (!path_equal(DEVICE(u)->sysfs, sysfs)) { - log_unit_debug(u, "Dev %s appeared twice with different sysfs paths %s and %s", - e, DEVICE(u)->sysfs, sysfs); - return -EEXIST; + /* This unit is in plugged state: we're sure it's attached to a device. */ + if (!path_equal(DEVICE(u)->sysfs, sysfs)) { + log_unit_debug(u, "Dev %s appeared twice with different sysfs paths %s and %s", + e, DEVICE(u)->sysfs, sysfs); + return -EEXIST; + } } - } - if (!u) { + delete = false; + + /* Let's remove all dependencies generated due to udev properties. We'll readd whatever is configured + * now below. */ + unit_remove_dependencies(u, UNIT_DEPENDENCY_UDEV); + } else { delete = true; r = unit_new_for_name(m, sizeof(Device), e, &u); @@ -361,8 +393,7 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa goto fail; unit_add_to_load_queue(u); - } else - delete = false; + } /* If this was created via some dependency and has not * actually been seen yet ->sysfs will not be @@ -380,16 +411,13 @@ 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. */ - if (dev && device_is_bound_by_mounts(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. */ + if (dev && device_is_bound_by_mounts(DEVICE(u), dev)) device_upgrade_mount_deps(u); - /* Note that this won't dispatch the load queue, the caller - * has to do that if needed and appropriate */ + /* Note that this won't dispatch the load queue, the caller has to do that if needed and appropriate */ unit_add_to_dbus_queue(u); return 0; diff --git a/src/core/job.c b/src/core/job.c index d441453839..606b6422ab 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -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; diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index 00f09ce60a..948d1bc248 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -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); diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index ede62e7b3d..84f2931b63 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -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; diff --git a/src/core/manager.c b/src/core/manager.c index d501182f22..c89dadc108 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -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) diff --git a/src/core/mount.c b/src/core/mount.c index 9b582072c6..e2c480a51d 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -156,21 +156,6 @@ static bool needs_quota(const MountParameters *p) { "usrquota\0" "grpquota\0" "quota\0" "usrjquota\0" "grpjquota\0"); } -const char *mount_get_fstype(const Mount *m) { - const char *type = NULL; - - assert(m); - - if (m->from_proc_self_mountinfo && m->parameters_proc_self_mountinfo.fstype) - type = m->parameters_proc_self_mountinfo.fstype; - else if (m->from_fragment && m->parameters_fragment.fstype) - type = m->parameters_fragment.fstype; - else - type = ""; - - return type; -} - static void mount_init(Unit *u) { Mount *m = MOUNT(u); @@ -280,9 +265,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; - const char *fstype; +static int mount_add_mount_dependencies(Mount *m) { MountParameters *pm; Unit *other; Iterator i; @@ -292,33 +275,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,32 +310,25 @@ 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; } } - /* 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); - if (r < 0) - return r; - } - 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 +366,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 +392,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 +437,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 +457,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 +466,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 +477,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,14 +485,21 @@ 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; + /* If this is a tmpfs mount then we have to unmount it before we try to deactivate swaps */ + if (streq_ptr(p->fstype, "tmpfs")) { + r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_SWAP_TARGET, NULL, true, mask); + if (r < 0) + return r; + } + return 0; } @@ -577,15 +567,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 +1443,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 +1505,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; } diff --git a/src/core/mount.h b/src/core/mount.h index f37094e39f..f81e4217df 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -110,5 +110,3 @@ MountExecCommand mount_exec_command_from_string(const char *s) _pure_; const char* mount_result_to_string(MountResult i) _const_; MountResult mount_result_from_string(const char *s) _pure_; - -const char *mount_get_fstype(const Mount *m); diff --git a/src/core/path.c b/src/core/path.c index 44831f5803..6b0768505a 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -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; diff --git a/src/core/scope.c b/src/core/scope.c index 7e789ed147..073b216ea2 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -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; diff --git a/src/core/service.c b/src/core/service.c index 78f315ad18..f42f1effb7 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -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; diff --git a/src/core/slice.c b/src/core/slice.c index b15f751c82..e4faa1cdbd 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -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; diff --git a/src/core/socket.c b/src/core/socket.c index c0f4030302..0686de1d1b 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -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; diff --git a/src/core/swap.c b/src/core/swap.c index f475755572..42d91ba3f7 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -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; diff --git a/src/core/target.c b/src/core/target.c index 2a58dd394d..7083813459 100644 --- a/src/core/target.c +++ b/src/core/target.c @@ -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) { diff --git a/src/core/timer.c b/src/core/timer.c index 9ea5f322d8..5f50576999 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -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) diff --git a/src/core/transaction.c b/src/core/transaction.c index 46c2fa452e..52014c634f 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -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); diff --git a/src/core/unit.c b/src/core/unit.c index f6b281b285..7d95f9db0b 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -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) { @@ -4681,3 +4831,72 @@ int unit_fork_helper_process(Unit *u, pid_t *ret) { *ret = pid; return 1; } + +static void unit_update_dependency_mask(Unit *u, UnitDependency d, Unit *other, UnitDependencyInfo di) { + assert(u); + assert(d >= 0); + assert(d < _UNIT_DEPENDENCY_MAX); + assert(other); + + 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, "%s lost dependency %s=%s", u->id, unit_dependency_to_string(d), other->id); + } else + /* Mask was reduced, let's update the entry */ + assert_se(hashmap_update(u->dependencies[d], other, di.data) == 0); +} + +void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) { + UnitDependency d; + + assert(u); + + /* Removes all dependencies u has on other units marked for ownership by 'mask'. */ + + if (mask == 0) + return; + + for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { + bool done; + + do { + UnitDependencyInfo di; + Unit *other; + Iterator i; + + done = true; + + HASHMAP_FOREACH_KEY(di.data, other, u->dependencies[d], i) { + UnitDependency q; + + if ((di.origin_mask & ~mask) == di.origin_mask) + continue; + di.origin_mask &= ~mask; + unit_update_dependency_mask(u, d, 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 (q = 0; q < _UNIT_DEPENDENCY_MAX; q++) { + UnitDependencyInfo dj; + + dj.data = hashmap_get(other->dependencies[q], u); + if ((dj.destination_mask & ~mask) == dj.destination_mask) + continue; + dj.destination_mask &= ~mask; + + unit_update_dependency_mask(other, q, u, dj); + } + + unit_add_to_gc_queue(other); + + done = false; + break; + } + + } while (!done); + } +} diff --git a/src/core/unit.h b/src/core/unit.h index 8c5c92ecd9..5e5e791bc4 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -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); @@ -689,6 +740,8 @@ void unit_set_exec_params(Unit *s, ExecParameters *p); int unit_fork_helper_process(Unit *u, pid_t *ret); +void unit_remove_dependencies(Unit *u, UnitDependencyMask mask); + /* Macros which append UNIT= or USER_UNIT= to the message */ #define log_unit_full(unit, level, error, ...) \ diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index f9a0a14d9e..2e1ce334bf 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -688,7 +688,7 @@ static int add_mounts(void) { } int main(int argc, char *argv[]) { - int r = 0, k; + int r, k; if (argc > 1 && argc != 4) { log_error("This program takes three or no arguments."); @@ -720,6 +720,8 @@ int main(int argc, char *argv[]) { if (arg_root_enabled) r = add_root_mount(); + else + r = 0; if (!in_initrd()) { k = add_mounts(); diff --git a/src/test/test-engine.c b/src/test/test-engine.c index 55249fdce2..a2e68bf5d9 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -115,6 +115,33 @@ int main(int argc, char *argv[]) { assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, NULL, &j) == 0); 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(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)); + + 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)); + + 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)); + manager_free(m); return 0; diff --git a/test/TEST-17-UDEV-WANTS/Makefile b/test/TEST-17-UDEV-WANTS/Makefile new file mode 100644 index 0000000000..b895de8bcb --- /dev/null +++ b/test/TEST-17-UDEV-WANTS/Makefile @@ -0,0 +1,4 @@ +include ../Makefile.guess + +all setup clean run: + @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@ diff --git a/test/TEST-17-UDEV-WANTS/test.sh b/test/TEST-17-UDEV-WANTS/test.sh new file mode 100755 index 0000000000..7beef7f398 --- /dev/null +++ b/test/TEST-17-UDEV-WANTS/test.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -e +TEST_DESCRIPTION="UDEV SYSTEMD_WANTS property" +TEST_NO_NSPAWN=1 + +. $TEST_BASE_DIR/test-functions +QEMU_TIMEOUT=180 + +test_setup() { + create_empty_image + mkdir -p $TESTDIR/root + mount ${LOOPDEV}p1 $TESTDIR/root + + ( + LOG_LEVEL=5 + eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) + + setup_basic_environment + + # mask some services that we do not want to run in these tests + ln -s /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service + ln -s /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service + ln -s /dev/null $initdir/etc/systemd/system/systemd-networkd.service + ln -s /dev/null $initdir/etc/systemd/system/systemd-networkd.socket + ln -s /dev/null $initdir/etc/systemd/system/systemd-resolved.service + + # setup the testsuite service + cat >$initdir/etc/systemd/system/testsuite.service < /run/udev/rules.d/50-testsuite.rules < /run/udev/rules.d/50-testsuite.rules < /testok + +exit 0 diff --git a/units/tmp.mount b/units/tmp.mount index a057fa1cf9..3a333d22ec 100644 --- a/units/tmp.mount +++ b/units/tmp.mount @@ -13,6 +13,7 @@ ConditionPathIsSymbolicLink=!/tmp DefaultDependencies=no Conflicts=umount.target Before=local-fs.target umount.target +After=swap.target [Mount] What=tmpfs