1########################################################################
2#                                                                      #
3#               This software is part of the ast package               #
4#          Copyright (c) 1982-2010 AT&T Intellectual Property          #
5#                      and is licensed under the                       #
6#                  Common Public License, Version 1.0                  #
7#                    by AT&T Intellectual Property                     #
8#                                                                      #
9#                A copy of the License is available at                 #
10#            http://www.opensource.org/licenses/cpl1.0.txt             #
11#         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         #
12#                                                                      #
13#              Information and Software Systems Research               #
14#                            AT&T Research                             #
15#                           Florham Park NJ                            #
16#                                                                      #
17#                  David Korn <dgk@research.att.com>                   #
18#                                                                      #
19########################################################################
20function err_exit
21{
22	print -u2 -n "\t"
23	print -u2 -r ${Command}[$1]: "${@:2}"
24	(( Errors++ ))
25}
26alias err_exit='err_exit $LINENO'
27
28Command=${0##*/}
29integer Errors=0
30
31tmp=$(mktemp -dt) || { err_exit mktemp -dt failed; exit 1; }
32trap "cd /; rm -rf $tmp" EXIT
33
34cd $tmp || err_exit "cd $tmp failed"
35
36(
37	set --pipefail
38	{
39		$SHELL 2> out2 <<- \EOF
40			g=false
41			trap 'print -u2 PIPED; $g && exit 0;g=true' PIPE
42			while :
43			do 	print hello
44			done
45		EOF
46	} | head > /dev/null
47	(( $? == 0)) ||   err_exit "SIGPIPE with wrong error code $?"
48	[[ $(<out2) == $'PIPED\nPIPED' ]] || err_exit 'SIGPIPE output on standard error is not correct'
49) &
50cop=$!
51{ sleep 4; kill $cop; } 2>/dev/null &
52spy=$!
53if	wait $cop 2>/dev/null
54then	kill $spy 2>/dev/null
55else	err_exit "pipe with --pipefail PIPE trap hangs"
56fi
57wait
58rm -f out2
59
60[[ $( trap 'print -n got_child' SIGCHLD
61	sleep 2 &
62	for	((i=0; i < 4; i++))
63	do 	sleep .75
64		print -n $i
65	done) == 01got_child23 ]] || err_exit 'SIGCHLD not working'
66
67# begin standalone SIGINT test generation
68
69cat > tst <<'!'
70# shell trap tests
71#
72#    tst  control script that calls tst-1, must be run by ksh
73#  tst-1  calls tst-2
74#  tst-2  calls tst-3
75#  tst-3  defaults or handles and discards/propagates SIGINT
76#
77# initial -v option lists script entry and SIGINT delivery
78#
79# three test options
80#
81#     d call next script directly, otherwise via $SHELL -c
82#     t trap, echo, and kill self on SIGINT, otherwise x or SIGINT default if no x
83#     x trap, echo on SIGINT, and tst-3 exit 0, tst-2 exit, otherwise SIGINT default
84#     z trap, echo on SIGINT, and tst-3 exit 0, tst-2 exit 0, otherwise SIGINT default
85#
86# Usage: tst [-v] [-options] shell-to-test ...
87
88# "trap + sig" is an unadvertized extension for this test
89# if run from nmake SIGINT is set to SIG_IGN
90# this call sets it back to SIG_DFL
91# semantics w.r.t. function scope must be worked out before
92# making it public
93trap + INT
94
95set -o monitor
96
97function gen
98{
99	typeset o t x d
100	for x in - x z
101	do	case $x in
102		[$1])	for t in - t
103			do	case $t in
104				[$1])	for d in - d
105					do	case $d in
106						[$1])	o="$o $x$t$d"
107						esac
108					done
109				esac
110			done
111		esac
112	done
113	echo '' $o
114}
115
116case $1 in
117-v)	v=v; shift ;;
118-*v*)	v=v ;;
119*)	v= ;;
120esac
121case $1 in
122*' '*)	o=$1; shift ;;
123-*)	o=$(gen $1); shift ;;
124*)	o=$(gen -txd) ;;
125esac
126case $# in
1270)	set ksh bash ksh88 pdksh ash zsh ;;
128esac
129for f in $o
130do	case $# in
131	1)	;;
132	*)	echo ;;
133	esac
134	for sh
135	do	if	$sh -c 'exit 0' > /dev/null 2>&1
136		then	case $# in
137			1)	printf '%3s ' "$f" ;;
138			*)	printf '%16s %3s ' "$sh" "$f" ;;
139			esac
140			$sh tst-1 $v$f $sh > tst.out &
141			wait
142			echo $(cat tst.out)
143		fi
144	done
145done
146case $# in
1471)	;;
148*)	echo ;;
149esac
150!
151cat > tst-1 <<'!'
152exec 2>/dev/null
153case $1 in
154*v*)	echo 1-main ;;
155esac
156{
157	sleep 2
158	case $1 in
159	*v*)	echo "SIGINT" ;;
160	esac
161	kill -s INT 0
162} &
163case $1 in
164*t*)	trap '
165		echo 1-intr
166		trap - INT
167		# omitting the self kill exposes shells that deliver
168		# the SIGINT trap but exit 0 for -xt
169		# kill -s INT $$
170	' INT
171	;;
172esac
173case $1 in
174*d*)	tst-2 $1 $2; status=$? ;;
175*)	$2 -c "tst-2 $1 $2"; status=$? ;;
176esac
177printf '1-%04d\n' $status
178sleep 2
179!
180cat > tst-2 <<'!'
181case $1 in
182*z*)	trap '
183		echo 2-intr
184		exit 0
185	' INT
186	;;
187*x*)	trap '
188		echo 2-intr
189		exit
190	' INT
191	;;
192*t*)	trap '
193		echo 2-intr
194		trap - INT
195		kill -s INT $$
196	' INT
197	;;
198esac
199case $1 in
200*v*)	echo 2-main ;;
201esac
202case $1 in
203*d*)	tst-3 $1 $2; status=$? ;;
204*)	$2 -c "tst-3 $1 $2"; status=$? ;;
205esac
206printf '2-%04d\n' $status
207!
208cat > tst-3 <<'!'
209case $1 in
210*[xz]*)	trap '
211		sleep 2
212		echo 3-intr
213		exit 0
214	' INT
215	;;
216*)	trap '
217		sleep 2
218		echo 3-intr
219		trap - INT
220		kill -s INT $$
221	' INT
222	;;
223esac
224case $1 in
225*v*)	echo 3-main ;;
226esac
227sleep 5
228printf '3-%04d\n' $?
229!
230chmod +x tst tst-?
231
232# end standalone test generation
233
234export PATH=$PATH:
235typeset -A expected
236expected[---]="3-intr"
237expected[--d]="3-intr"
238expected[-t-]="3-intr 2-intr 1-intr 1-0258"
239expected[-td]="3-intr 2-intr 1-intr 1-0258"
240expected[x--]="3-intr 2-intr"
241expected[x-d]="3-intr 2-intr"
242expected[xt-]="3-intr 2-intr 1-intr 1-0258"
243expected[xtd]="3-intr 2-intr 1-intr 1-0258"
244expected[z--]="3-intr 2-intr 1-0000"
245expected[z-d]="3-intr 2-intr 1-0000"
246expected[zt-]="3-intr 2-intr 1-intr 1-0000"
247expected[ztd]="3-intr 2-intr 1-intr 1-0000"
248
249tst $SHELL > tst.got
250
251while	read ops out
252do	[[ $out == ${expected[$ops]} ]] || err_exit "interrupt $ops test failed -- expected '${expected[$ops]}', got '$out'"
253done < tst.got
254
255float s=$SECONDS
256[[ $($SHELL -c 'trap "print SIGUSR1 ; exit 0" USR1; (trap "" USR1 ; exec kill -USR1 $$ & sleep 5); print done') == SIGUSR1 ]] || err_exit 'subshell ignoring signal does not send signal to parent'
257(( (SECONDS-s) < 4 )) && err_exit 'parent does not wait for child to complete before handling signal'
258((s = SECONDS))
259[[ $($SHELL -c 'trap "print SIGUSR1 ; exit 0" USR1; (trap "exit" USR1 ; exec kill -USR1 $$ & sleep 5); print done') == SIGUSR1 ]] || err_exit 'subshell catching signal does not send signal to parent'
260(( SECONDS-s < 4 )) && err_exit 'parent completes early'
261
262unset n s t
263for s in $(kill -l)
264do	if	! n=$(kill -l $s 2>/dev/null)
265	then	err_exit "'kill -l $s' failed"
266		continue
267	fi
268	if	! t=$(kill -l $n 2>/dev/null)
269	then	err_exit "'kill -l $n' failed"
270		continue
271	fi
272	if	[[ $s == ?(SIG)$t ]]
273	then	continue
274	fi
275	if	! m=$(kill -l $t 2>/dev/null)
276	then	err_exit "'kill -l $t' failed"
277		continue
278	fi
279	if	[[ $m == $n ]]
280	then	continue
281	fi
282	err_exit "'kill -l $s' => $n, 'kill -l $n' => $t, kill -l $t => $m -- expected $n"
283done
284yes=$(whence -p yes)
285[[ $yes ]] && for exp in TERM VTALRM PIPE
286do { $SHELL <<- EOF
287		foo() { return 0; }
288		trap foo EXIT
289		{ sleep 2; kill -$exp \$\$; sleep 3; kill -0 \$\$ && kill -KILL \$\$; } &
290		$yes | while read yes; do
291		        (/bin/date; sleep .1)
292		done > /dev/null
293	EOF
294    } 2>> /dev/null
295    got=$(kill -l $?)
296    [[ $exp == $got ]] || err_exit "kill -$exp \$\$ failed, required termination by signal '$got'"
297done
298
299SECONDS=0
300$SHELL 2> /dev/null -c 'sleep 2 && kill $$ & trap "print done;exit 3" EXIT; (sleep 5);print finished' > $tmp/sig
301(( $?==3)) || err_exit "wrong exit status expecting 3 got $?"
302x=$(<$tmp/sig)
303[[ $x == done ]] || err_exit "wrong result - execting done got $x"
304(( SECONDS > 3.5 )) && err_exit "took $SECONDS seconds, expecting around 2"
305
306SECONDS=0
307{ $SHELL 2> /dev/null -c 'sleep 2 && kill $$ & trap "print done;exit" EXIT; (sleep 5);print finished' > $tmp/sig ;} 2> /dev/null
308[[ $(kill -l $?) == TERM ]] || err_exit "wrong exit status expecting TERM got $(kill -l $?)"
309x=$(<$tmp/sig)
310[[ $x == done ]] || err_exit "wrong result - execting done got $x"
311(( SECONDS > 3.5 )) && err_exit "took $SECONDS seconds, expecting around 2"
312
313SECONDS=0
314x=$($SHELL 2> /dev/null -c 'sleep 2 && kill $$ & trap "print done;exit 3" EXIT; (sleep 5);print finished')
315(( $?==3)) || err_exit "wrong exit status expecting 3 got $?"
316[[ $x == done ]] || err_exit "wrong result - execting done got $x"
317(( SECONDS < 4 )) && err_exit "took $SECONDS seconds, expecting around 5"
318
319trap '' SIGBUS
320[[ $($SHELL -c 'trap date SIGBUS;trap -p SIGBUS') ]] && err_exit 'SIGBUS should not have a trap'
321trap -- - SIGBUS
322
323exit $((Errors))
324