diff --git a/src/core/device.c b/src/core/device.c index c18a40a494..d6710262a9 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -547,18 +547,24 @@ static int device_add_udev_wants(Unit *u, sd_device *dev) { if (d->state != DEVICE_DEAD) /* So here's a special hack, to compensate for the fact that the udev database's reload cycles are not * synchronized with our own reload cycles: when we detect that the SYSTEMD_WANTS property of a device - * changes while the device unit is already up, let's manually trigger any new units listed in it not - * seen before. This typically happens during the boot-time switch root transition, as udev devices - * will generally already be up in the initrd, but SYSTEMD_WANTS properties get then added through udev - * rules only available on the host system, and thus only when the initial udev coldplug trigger runs. + * changes while the device unit is already up, let's skip to trigger units that were already listed + * and are active, and start units otherwise. This typically happens during the boot-time switch root + * transition, as udev devices will generally already be up in the initrd, but SYSTEMD_WANTS properties + * get then added through udev rules only available on the host system, and thus only when the initial + * udev coldplug trigger runs. * * We do this only if the device has been up already when we parse this, as otherwise the usual * dependency logic that is run from the dead → plugged transition will trigger these deps. */ STRV_FOREACH(i, added) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - if (strv_contains(d->wants_property, *i)) /* Was this unit already listed before? */ - continue; + if (strv_contains(d->wants_property, *i)) { + Unit *v; + + v = manager_get_unit(u->manager, *i); + if (v && UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(v))) + continue; /* The unit was already listed and is running. */ + } r = manager_add_job_by_name(u->manager, JOB_START, *i, JOB_FAIL, NULL, &error, NULL); if (r < 0) diff --git a/src/core/unit.c b/src/core/unit.c index 07c83c90a4..0798c29c9d 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -5167,6 +5167,9 @@ void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) { unit_add_to_gc_queue(other); + /* The unit 'other' may not be wanted by the unit 'u'. */ + unit_submit_to_stop_when_unneeded_queue(other); + done = false; break; } diff --git a/test/units/assert.sh b/test/units/assert.sh index 66357ab688..2f4d93ab8c 100644 --- a/test/units/assert.sh +++ b/test/units/assert.sh @@ -4,10 +4,10 @@ # utility functions for shell tests assert_true() {( - local rc - set +ex + local rc + "$@" rc=$? if [[ $rc -ne 0 ]]; then @@ -47,10 +47,10 @@ assert_not_in() {( )} assert_rc() {( - local rc exp="${1?}" - set +ex + local rc exp="${1?}" + shift "$@" rc=$? diff --git a/test/units/testsuite-17.07.sh b/test/units/testsuite-17.07.sh new file mode 100755 index 0000000000..549107af10 --- /dev/null +++ b/test/units/testsuite-17.07.sh @@ -0,0 +1,205 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +# shellcheck source=test/units/assert.sh +. "$(dirname "$0")"/assert.sh + +wait_service_active() {( + set +ex + for (( i = 0; i < 20; i++ )); do + if (( i != 0 )); then sleep 0.5; fi + if systemctl --quiet is-active "${1?}"; then + return 0 + fi + done + return 1 +)} + +wait_service_inactive() {( + set +ex + for (( i = 0; i < 20; i++ )); do + if (( i != 0 )); then sleep 0.5; fi + systemctl --quiet is-active "${1?}" + if [[ "$?" == "3" ]]; then + return 0 + fi + done + return 1 +)} + +mkdir -p /run/systemd/system +cat >/run/systemd/system/both.service </run/systemd/system/on-add.service </run/systemd/system/on-change.service </run/udev/rules.d/50-testsuite.rules </run/systemd/system/both.service </run/systemd/system/on-add.service </run/systemd/system/on-change.service <