1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
#!/bin/sh
# test program group handling
# Copyright (C) 2011-2026 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ timeout env
require_trap_signame_
require_kill_group_
# construct a program group hierarchy as follows:
# timeout-group - foreground group
# group.sh - separate group
# timeout.cmd - same group as group.sh
#
# We then send a SIGUSR1 to the "separate group"
# to simulate what happens when a terminating signal
# is sent to the foreground group.
setsid true || skip_ "setsid required to control groups"
printf '%s\n' '#!'"$SHELL" > timeout.cmd || framework_failure_
cat >> timeout.cmd <<\EOF
trap 'touch sig.received; exit' USR1
trap
touch timeout.running
count=$1
until test -e sig.received || test $count = 0; do
sleep 1
count=$(expr $count - 1)
done
EOF
chmod a+x timeout.cmd
cat > group.sh <<EOF
#!$SHELL
# trap '' ensures this script ignores the signal,
# so that the 'wait' below is not interrupted.
# Note this then requires env --default... to reset
# the signal disposition so that 'timeout' handles it.
# Alternatively one could use trap ':' USR1
# and then handle the retry in wait like:
# while wait; test \$? -gt 128; do :; done
# Note also INT and QUIT signals are special for backgrounded
# processes like this in shell as they're auto ignored
# and can't be reset with trap to any other disposition.
# Therefore we use the ignored signal method so any
# termination signal can be used.
trap '' USR1
env --default-signal=USR1 \
timeout -v --foreground 25 ./timeout.cmd 20&
wait
echo group.sh wait returned \$ret
EOF
chmod a+x group.sh
check_timeout_cmd_running()
{
local delay="$1"
test -e timeout.running ||
{ sleep $delay; return 1; }
}
check_timeout_cmd_exiting()
{
local delay="$1"
test -e sig.received ||
{ sleep $delay; return 1; }
}
# Terminate any background processes
cleanup_() { kill $pid 2>/dev/null && wait $pid; }
# Start above script in its own group.
# We could use timeout for this, but that assumes an implementation.
setsid ./group.sh & pid=$!
# Wait 6.3s for timeout.cmd to start
retry_delay_ check_timeout_cmd_running .1 6 || fail=1
# Simulate a Ctrl-C to the group to test timely exit
kill -USR1 -- -$pid
wait
test -e sig.received || fail=1
rm -f sig.received timeout.running
# On Linux ensure we kill the monitored command
# even if we're terminated abnormally (e.g., get SIGKILL).
if grep '^#define HAVE_PRCTL 1' "$CONFIG_HEADER" >/dev/null; then
timeout -sUSR1 25 ./timeout.cmd 20 & pid=$!
# Wait 6.3s for timeout.cmd to start
retry_delay_ check_timeout_cmd_running .1 6 || fail=1
kill -KILL -- $pid
wait
# Wait 6.3s for timeout.cmd to exit
retry_delay_ check_timeout_cmd_exiting .1 6 || fail=1
rm -f sig.received timeout.running
fi
# Ensure cascaded timeouts work
# or more generally, ensure we timeout
# commands that create their own group
# This didn't work before 8.13.
start=$(date +%s)
# Note the first timeout must send a signal that
# the second is handling for it to be propagated to the command.
# termination signals are implicitly handled unless ignored.
timeout -sALRM 30 timeout -sUSR1 25 ./timeout.cmd 20 & pid=$!
# Wait 6.3s for timeout.cmd to start
retry_delay_ check_timeout_cmd_running .1 6 || fail=1
kill -ALRM $pid # trigger the alarm of the first timeout command
wait $pid
ret=$?
test $ret -eq 124 ||
skip_ "timeout returned $ret. SIGALRM not handled?"
test -e sig.received || fail=1
end=$(date +%s)
test $(expr $end - $start) -lt 20 ||
skip_ "timeout.cmd didn't receive a signal until after sleep?"
Exit $fail
|