File: timeout-group.sh

package info (click to toggle)
coreutils 9.10-1
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 70,560 kB
  • sloc: ansic: 253,546; sh: 30,931; perl: 8,141; yacc: 1,846; makefile: 198; python: 47; sed: 16
file content (138 lines) | stat: -rwxr-xr-x 4,234 bytes parent folder | download
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