diff --git a/party b/party index 1e89598..ac4a445 100755 --- a/party +++ b/party @@ -796,10 +796,17 @@ EOF # mkdir race tear down the winner's directory and tmux server via # set -e + EXIT trap. Now the trap fires only on resources this # process actually created. Disarmed once roster_write commits. + # + # The cleanup hangs off EXIT alone; the signal traps only exit. A + # signal trap body that returns lets the shell resume where it was + # interrupted, which here means configuring a dir we just removed. trap ' "$PARTY_TMUX" -S "$sock" kill-server 2>/dev/null || true rm -rf "$party_dir" - ' EXIT INT TERM HUP + ' EXIT + trap 'exit 129' HUP + trap 'exit 130' INT + trap 'exit 143' TERM chgrp "$TMUX_PARTY_GROUP" "$party_dir" chmod 0750 "$party_dir" diff --git a/tests/95-stub-roundtrip.bats b/tests/95-stub-roundtrip.bats index 7c6ecec..693158f 100644 --- a/tests/95-stub-roundtrip.bats +++ b/tests/95-stub-roundtrip.bats @@ -39,6 +39,9 @@ case "$*" in *"new-session -d "*) # cmd_host expects the socket file to exist for apply_party_perms_file. [ -n "$sock" ] && : > "$sock" + # Optional injection point for the signal test: TERM the calling + # party process while it waits on us, mid-setup. + [ -n "${SIGNAL_ON_NEWSESSION:-}" ] && kill -TERM "$PPID" ;; *"display-message -p "*) # Caller is fishing for #{pid}; emit the sleeper's PID. @@ -557,6 +560,21 @@ STUB [ ! -e "$party_dir" ] } +@test "host interrupted by a signal rolls back and stops" { + # A signal trap body that does not exit lets the shell resume where + # it left off: cmd_host would clean up, then carry on configuring a + # party dir it just removed. The signal must end the run. + party_dir="$PARTY_SOCKET_DIR/party-$USER:sigterm.d" + + SIGNAL_ON_NEWSESSION=1 run "$PARTY_BIN" host sigterm + [ "$status" -eq 143 ] + + [ ! -e "$party_dir" ] + # Nothing but the rollback's kill-server follows new-session. + [ "$(sed -n '/new-session -d /,$p' "$TMUX_LOG" | sed 1d)" \ + = "-S $party_dir/sock kill-server" ] +} + @test "host refuses leftover per-party dir without a roster" { # Simulate a prior incarnation that left a per-party dir behind with # no roster (the rollback trap couldn't fire — SIGKILL or panic).