1########################################################################
2#                                                                      #
3#               This software is part of the ast package               #
4#          Copyright (c) 1982-2012 AT&T Intellectual Property          #
5#                      and is licensed under the                       #
6#                 Eclipse Public License, Version 1.0                  #
7#                    by AT&T Intellectual Property                     #
8#                                                                      #
9#                A copy of the License is available at                 #
10#          http://www.eclipse.org/org/documents/epl-v10.html           #
11#         (with md5 checksum b35adb5213ca9657e911e9befb180842)         #
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
36unset n s t
37typeset -A SIG
38for s in $(kill -l)
39do	if	! n=$(kill -l $s 2>/dev/null)
40	then	err_exit "'kill -l $s' failed"
41	elif	! t=$(kill -l $n 2>/dev/null)
42	then	err_exit "'kill -l $n' failed"
43	elif	[[ $s == ?(SIG)$t ]]
44	then	SIG[${s#SIG}]=1
45	elif	! m=$(kill -l $t 2>/dev/null)
46	then	err_exit "'kill -l $t' failed"
47	elif	[[ $m != $n ]]
48	then	err_exit "'kill -l $s' => $n, 'kill -l $n' => $t, kill -l $t => $m -- expected $n"
49	fi
50done
51
52(
53	set --pipefail
54	{
55		$SHELL 2> out2 <<- \EOF
56			g=false
57			trap 'print -u2 PIPED; $g && exit 0;g=true' PIPE
58			while :
59			do 	print hello
60			done
61		EOF
62	} | head > /dev/null
63	(( $? == 0)) ||   err_exit "SIGPIPE with wrong error code $?"
64	[[ $(<out2) == $'PIPED\nPIPED' ]] || err_exit 'SIGPIPE output on standard error is not correct'
65) &
66cop=$!
67{ sleep 4; kill $cop; } 2>/dev/null &
68spy=$!
69if	wait $cop 2>/dev/null
70then	kill $spy 2>/dev/null
71else	err_exit "pipe with --pipefail PIPE trap hangs"
72fi
73wait
74rm -f out2
75
76[[ $( trap 'print -n got_child' SIGCHLD
77	sleep 2 &
78	for	((i=0; i < 4; i++))
79	do 	sleep .75
80		print -n $i
81	done) == 01got_child23 ]] || err_exit 'SIGCHLD not working'
82
83# begin standalone SIGINT test generation
84
85cat > tst <<'!'
86# shell trap tests
87#
88#    tst  control script that calls tst-1, must be run by ksh
89#  tst-1  calls tst-2
90#  tst-2  calls tst-3
91#  tst-3  defaults or handles and discards/propagates SIGINT
92#
93# initial -v option lists script entry and SIGINT delivery
94#
95# three test options
96#
97#     d call next script directly, otherwise via $SHELL -c
98#     t trap, echo, and kill self on SIGINT, otherwise x or SIGINT default if no x
99#     x trap, echo on SIGINT, and tst-3 exit 0, tst-2 exit, otherwise SIGINT default
100#     z trap, echo on SIGINT, and tst-3 exit 0, tst-2 exit 0, otherwise SIGINT default
101#
102# Usage: tst [-v] [-options] shell-to-test ...
103
104# "trap + sig" is an unadvertized extension for this test
105# if run from nmake SIGINT is set to SIG_IGN
106# this call sets it back to SIG_DFL
107# semantics w.r.t. function scope must be worked out before
108# making it public
109trap + INT
110
111set -o monitor
112
113function gen
114{
115	typeset o t x d
116	for x in - x z
117	do	case $x in
118		[$1])	for t in - t
119			do	case $t in
120				[$1])	for d in - d
121					do	case $d in
122						[$1])	o="$o $x$t$d"
123						esac
124					done
125				esac
126			done
127		esac
128	done
129	echo '' $o
130}
131
132case $1 in
133-v)	v=v; shift ;;
134-*v*)	v=v ;;
135*)	v= ;;
136esac
137case $1 in
138*' '*)	o=$1; shift ;;
139-*)	o=$(gen $1); shift ;;
140*)	o=$(gen -txd) ;;
141esac
142case $# in
1430)	set ksh bash ksh88 pdksh ash zsh ;;
144esac
145for f in $o
146do	case $# in
147	1)	;;
148	*)	echo ;;
149	esac
150	for sh
151	do	if	$sh -c 'exit 0' > /dev/null 2>&1
152		then	case $# in
153			1)	printf '%3s ' "$f" ;;
154			*)	printf '%16s %3s ' "$sh" "$f" ;;
155			esac
156			$sh tst-1 $v$f $sh > tst.out &
157			wait
158			echo $(cat tst.out)
159		fi
160	done
161done
162case $# in
1631)	;;
164*)	echo ;;
165esac
166!
167cat > tst-1 <<'!'
168exec 2>/dev/null
169case $1 in
170*v*)	echo 1-main ;;
171esac
172{
173	sleep 2
174	case $1 in
175	*v*)	echo "SIGINT" ;;
176	esac
177	kill -s INT 0
178} &
179case $1 in
180*t*)	trap '
181		echo 1-intr
182		trap - INT
183		# omitting the self kill exposes shells that deliver
184		# the SIGINT trap but exit 0 for -xt
185		# kill -s INT $$
186	' INT
187	;;
188esac
189case $1 in
190*d*)	tst-2 $1 $2; status=$? ;;
191*)	$2 -c "tst-2 $1 $2"; status=$? ;;
192esac
193printf '1-%04d\n' $status
194sleep 2
195!
196cat > tst-2 <<'!'
197case $1 in
198*z*)	trap '
199		echo 2-intr
200		exit 0
201	' INT
202	;;
203*x*)	trap '
204		echo 2-intr
205		exit
206	' INT
207	;;
208*t*)	trap '
209		echo 2-intr
210		trap - INT
211		kill -s INT $$
212	' INT
213	;;
214esac
215case $1 in
216*v*)	echo 2-main ;;
217esac
218case $1 in
219*d*)	tst-3 $1 $2; status=$? ;;
220*)	$2 -c "tst-3 $1 $2"; status=$? ;;
221esac
222printf '2-%04d\n' $status
223!
224cat > tst-3 <<'!'
225case $1 in
226*[xz]*)	trap '
227		sleep 2
228		echo 3-intr
229		exit 0
230	' INT
231	;;
232*)	trap '
233		sleep 2
234		echo 3-intr
235		trap - INT
236		kill -s INT $$
237	' INT
238	;;
239esac
240case $1 in
241*v*)	echo 3-main ;;
242esac
243sleep 5
244printf '3-%04d\n' $?
245!
246chmod +x tst tst-?
247
248# end standalone test generation
249
250export PATH=$PATH:
251typeset -A expected
252expected[---]="3-intr"
253expected[--d]="3-intr"
254expected[-t-]="3-intr 2-intr 1-intr 1-0258"
255expected[-td]="3-intr 2-intr 1-intr 1-0258"
256expected[x--]="3-intr 2-intr 1-0000"
257expected[x-d]="3-intr 2-intr 1-0000"
258expected[xt-]="3-intr 2-intr 1-intr 1-0000"
259expected[xtd]="3-intr 2-intr 1-intr 1-0000"
260expected[z--]="3-intr 2-intr 1-0000"
261expected[z-d]="3-intr 2-intr 1-0000"
262expected[zt-]="3-intr 2-intr 1-intr 1-0000"
263expected[ztd]="3-intr 2-intr 1-intr 1-0000"
264
265tst $SHELL > tst.got
266
267while	read ops out
268do	[[ $out == ${expected[$ops]} ]] || err_exit "interrupt $ops test failed -- expected '${expected[$ops]}', got '$out'"
269done < tst.got
270
271if	[[ ${SIG[USR1]} ]]
272then	float s=$SECONDS
273	[[ $(LC_ALL=C $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'
274	(( (SECONDS-s) < 4 )) && err_exit 'parent does not wait for child to complete before handling signal'
275	((s = SECONDS))
276	[[ $(LC_ALL=C $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'
277	(( SECONDS-s < 4 )) && err_exit 'parent completes early'
278fi
279
280yes=$(whence -p yes)
281if	[[ $yes ]]
282then	for exp in TERM VTALRM PIPE
283	do	if	[[ ${SIG[$exp]} ]]
284		then	{
285				$SHELL <<- EOF
286				foo() { return 0; }
287				trap foo EXIT
288				{ sleep 2; kill -$exp \$\$; sleep 3; kill -0 \$\$ && kill -KILL \$\$; } &
289				$yes |
290				while read yes
291				do	(/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'"
297		fi
298	done
299fi
300
301SECONDS=0
302$SHELL 2> /dev/null -c 'sleep 2 && kill $$ & trap "print done; exit 3" EXIT; (sleep 5); print finished' > $tmp/sig
303e=$?
304[[ $e == 3 ]] || err_exit "exit status failed -- expected 3, got $e"
305x=$(<$tmp/sig)
306[[ $x == done ]] || err_exit "output failed -- expected 'done', got '$x'"
307(( SECONDS > 3.5 )) && err_exit "took $SECONDS seconds, expected around 2"
308
309SECONDS=0
310$SHELL 2> /dev/null -c 'sleep 2 && kill $$ & trap "print done; exit 3" EXIT; sleep 5; print finished' > $tmp/sig
311e=$?
312[[ $e == 3 ]] || err_exit "exit status failed -- expected 3, got $e"
313x=$(<$tmp/sig)
314[[ $x == done ]] || err_exit "output failed -- expected 'done', got '$x'"
315(( SECONDS > 3.5 )) && err_exit "took $SECONDS seconds, expected around 2"
316
317SECONDS=0
318{ $SHELL 2> /dev/null -c 'sleep 2 && kill $$ & trap "print done; exit 3" EXIT; (sleep 5); print finished' > $tmp/sig ;} 2> /dev/null
319e=$?
320[[ $e == 3 ]] || err_exit "exit status failed -- expected 3, got $e"
321x=$(<$tmp/sig)
322[[ $x == done ]] || err_exit "output failed -- expected 'done', got '$x'"
323(( SECONDS > 3.5 )) && err_exit "took $SECONDS seconds, expected around 2"
324
325SECONDS=0
326{ $SHELL 2> /dev/null -c 'sleep 2 && kill $$ & trap "print done; exit 3" EXIT; sleep 5; print finished' > $tmp/sig ;} 2> /dev/null
327e=$?
328[[ $e == 3 ]] || err_exit "exit status failed -- expected 3, got $e"
329x=$(<$tmp/sig)
330[[ $x == done ]] || err_exit "output failed -- expected 'done', got '$x'"
331(( SECONDS > 3.5 )) && err_exit "took $SECONDS seconds, expected around 2"
332
333SECONDS=0
334x=$($SHELL 2> /dev/null -c 'sleep 2 && kill $$ & trap "print done; exit 3" EXIT; (sleep 5); print finished')
335e=$?
336[[ $e == 3 ]] || err_exit "exit status failed -- expected 3, got $e"
337[[ $x == done ]] || err_exit "output failed -- expected 'done', got '$x'"
338(( SECONDS > 3.5 )) && err_exit "took $SECONDS seconds, expected around 2"
339
340SECONDS=0
341x=$($SHELL 2> /dev/null -c 'sleep 2 && kill $$ & trap "print done; exit 3" EXIT; sleep 5; print finished')
342e=$?
343[[ $e == 3 ]] || err_exit "exit status failed -- expected 3, got $e"
344[[ $x == done ]] || err_exit "output failed -- expected 'done', got '$x'"
345(( SECONDS > 3.5 )) && err_exit "took $SECONDS seconds, expected around 2"
346
347trap '' SIGBUS
348[[ $($SHELL -c 'trap date SIGBUS; trap -p SIGBUS') ]] && err_exit 'SIGBUS should not have a trap'
349trap -- - SIGBUS
350
351{
352    x=$(
353    $SHELL   <<- \++EOF
354	timeout()
355	{
356		trap 'trap - TERM; return' TERM
357		( sleep $1; kill -TERM $$ ) >/dev/null 2>&1 &
358		sleep 3
359	}
360	timeout 1
361	print ok
362++EOF
363    )
364} 2> /dev/null
365[[ $x == ok ]] || err_exit 'return without arguments in trap not preserving exit status'
366
367x=$(
368    $SHELL  <<- \++EOF
369	set -o pipefail
370        foobar()
371        {
372		for ((i=0; i < 10000; i++))
373		do	print abcdefghijklmnopqrstuvwxyz
374		done | head > /dev/null
375        }
376        foobar
377        print ok
378	++EOF
379)
380[[ $x == ok ]] || err_exit 'SIGPIPE exit status causes PIPE signal to be propogaged'
381
382x=$(
383    $SHELL <<- \EOF
384	trap "print GNAW" URG
385	print 1
386	( sleep 1 ; kill -URG $$ ; sleep 1 ; print S1 ; )
387	print 2
388EOF
389)
390[[ $x == $'1\nS1\nGNAW\n2' ]] || err_exit 'signal ignored in subshell not propagated to parent'
391
392if	[[ ${SIG[RTMIN]} ]]
393then	{
394	$SHELL <<- \EOF
395		trap : RTMIN
396		for ((i=0 ; i < 3 ; i++))
397		do	sleep 1
398			kill -RTMIN $$ 2> /dev/null
399		done &
400		wait
401	EOF
402	} 2> /dev/null
403	[[ $? == 0 ]] && err_exit 'wait interrupted by caught signal should have non-zero exit status'
404	{
405	$SHELL <<- \EOF
406		for ((i=0 ; i < 3 ; i++))
407		do	sleep 1
408			kill -RTMIN $$ 2> /dev/null
409		done &
410		wait
411	EOF
412	} 2> /dev/null
413	[[ $(kill -l $?) == RTMIN ]] || err_exit 'wait interrupted by signal not caught should exit with the value of that signal+256'
414fi
415
416function b
417{
418	sleep 3
419	endb=1
420}
421
422function a
423{
424	trap 'print int'  TERM
425	b
426	enda=1
427}
428
429{ /bin/sleep 1;kill -s TERM $$;}&
430unset enda endb
431a
432[[ $endb ]] &&  err_exit 'TERM signal did not kill function b'
433[[ $enda == 1 ]] || err_exit 'TERM signal killed function a'
434
435exit $((Errors<125?Errors:125))
436