From: Frantisek Sumsal Date: Tue, 21 Apr 2026 10:07:02 +0000 (+0200) Subject: test: avoid using external commands in trap handlers X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4d92c72b819bd6d54dfbbb12e4cd25de5053714c;p=thirdparty%2Fsystemd.git test: avoid using external commands in trap handlers In #39675 the reported fail was as follows: 5580s [ 247.559994] TEST-13-NSPAWN.sh[1858]: Exported 93%. 5580s [ 247.659002] TEST-13-NSPAWN.sh[1858]: Exported 95%. 5580s [ 247.785893] TEST-13-NSPAWN.sh[1858]: Operation completed successfully. 5580s [ 247.923727] TEST-13-NSPAWN.sh[1858]: Exiting. 5580s [ 258.300406] TEST-13-NSPAWN.sh[1074]: + machinectl import-raw /var/tmp/container-export.raw container-raw-reimport 5580s [ 258.323328] TEST-13-NSPAWN.sh[1884]: The 'machinectl import-raw' command has been replaced by 'importctl -m import-raw'. Redirecting invocation. 5580s [ 258.659982] TEST-13-NSPAWN.sh[1884]: Failed to transfer image: Remote peer disconnected 5580s [ 258.734218] TEST-13-NSPAWN.sh[1074]: + at_exit Turns out that the real reason behind this fail is that the machine was under heavy load due to a busy-loop from the stub init. The cause of this is a bug in bash, where running commands that fork (i.e. not built-ins) can cause a permanent busy-loop due to a desync in trap handling if you send the signals to the bash process _just right_: [ 90.855318] TEST-13-NSPAWN.sh[1074]: + machinectl poweroff long-running long-running long-running [ 90.855318] TEST-13-NSPAWN.sh[1074]: + machinectl reboot long-running long-running long-running [ 90.928980] systemd-nspawn[1679]: ++ touch /poweroff [ 90.928980] systemd-nspawn[1679]: +++ touch /reboot [ 90.928980] systemd-nspawn[1679]: + : [ 90.928980] systemd-nspawn[1679]: + : [ 90.928980] systemd-nspawn[1679]: + wait [ 90.928980] systemd-nspawn[1679]: + : [ 90.928980] systemd-nspawn[1679]: + : [ 90.928980] systemd-nspawn[1679]: + wait [ 90.928980] systemd-nspawn[1679]: + : [ 90.928980] systemd-nspawn[1679]: + : [ 90.928980] systemd-nspawn[1679]: + wait ... $ journalctl --file TEST-13-NSPAWN-1.journal -o short-monotonic --no-hostname --grep "^\+ wait$" | wc -l 349734 So the stub-init was hammering the machine in a tight endless loop, which then caused systemd-importd to timeout when talking to D-Bus: [ 258.300096] TEST-13-NSPAWN.sh[1074]: + machinectl import-raw /var/tmp/container-export.raw container-raw-reimport ... [ 258.415319] systemd-importd[1859]: Unable to request name, failing connection: Method call timed out [ 258.483662] systemd-importd[1859]: Bus n/a: changing state RUNNING → CLOSING [ 258.605442] systemd-importd[1859]: Bus n/a: changing state CLOSING → CLOSED [ 258.659958] TEST-13-NSPAWN.sh[1884]: Failed to transfer image: Remote peer disconnected Given this is not our issue, let's work around it by using just built-ins from the trap handlers, which are not susceptible to this bug. Resolves: #39675 --- diff --git a/test/units/TEST-13-NSPAWN.machined.sh b/test/units/TEST-13-NSPAWN.machined.sh index 34307f3c8b1..de51daa24c7 100755 --- a/test/units/TEST-13-NSPAWN.machined.sh +++ b/test/units/TEST-13-NSPAWN.machined.sh @@ -44,21 +44,27 @@ set -x PID=0 -trap 'touch /terminate; kill 0' RTMIN+3 -trap 'touch /poweroff' RTMIN+4 -trap 'touch /reboot' INT -trap 'touch /trap' TRAP +# Use only builtins in trap handlers to avoid forking. External commands +# (like touch) cause bash to enter wait_for() for the child, and a nested +# signal arriving during that wait triggers a bash bug where +# run_interrupt_trap() clears catch_flag while other traps are still +# pending, creating an orphaned pending_traps[] entry that makes 'wait' +# busy-loop indefinitely. +trap ': >/terminate; kill 0' RTMIN+3 +trap ': >/poweroff' RTMIN+4 +trap ': >/reboot' INT +trap ': >/trap' TRAP trap 'exit 0' TERM trap 'kill $PID' EXIT # We need to wait for the sleep process asynchronously in order to allow # bash to process signals sleep infinity & +PID=$! # notify that the process is ready -touch /ready +: >/ready -PID=$! while :; do wait || : done @@ -332,11 +338,11 @@ trap 'kill $PID' EXIT # We need to wait for the sleep process asynchronously in order to allow # bash to process signals sleep infinity & +PID=$! # notify that the process is ready -touch /ready +: >/ready -PID=$! while :; do wait || : done