1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or http://www.opensolaris.org/os/licensing.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21
22#
23# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24#
25
26#
27# This test checks whether the EXIT trap is called correctly in subshells
28#
29# This was reported as CR #6907460 ("EXIT trap handlers are sometimes executed twice"):
30# ------------ snip ------------
31# During SST testing of snv_128(RE) we found out that ksh93 executes EXIT
32# trap handlers twice under some circumstances.
33#
34# Here is a test script:
35# ---
36# #!/bin/ksh93 -x
37#
38# function A
39# {
40#         set -x
41#         trap "print TRAP A >>log" EXIT
42#         print >&2
43# }
44#
45# function B
46# {
47#         set -x
48#         trap "print TRAP B >>log" EXIT
49#         A
50# }
51#
52# rm -f log
53# x=$(B)
54# ---
55#
56# It produces the following output on snv_128:
57# ---
58# + rm -f log
59# + B
60# + trap 'print TRAP B >>log' EXIT
61# + A
62# + trap 'print TRAP A >>log' EXIT
63# + print
64# + + print TRAP A
65# 1>& 2
66# + 1>> log
67# + print TRAP B
68#
69# + 1>> log
70# + print TRAP A
71# + 1>> log
72# + print TRAP B
73# + 1>> log
74# + x=''
75# ---
76#
77# The log file then contains:
78# TRAP A
79# TRAP B
80# TRAP A
81# TRAP B
82#
83# However, the expected log would be:
84# TRAP A
85# TRAP B
86#
87# When the "x=$(B)" line is changed to "B", the log is correct:
88# TRAP A
89# TRAP B
90# ------------ snip ------------
91#
92
93# test setup
94function err_exit
95{
96	print -u2 -n "\t"
97	print -u2 -r ${Command}[$1]: "${@:2}"
98	(( Errors < 127 && Errors++ ))
99}
100alias err_exit='err_exit $LINENO'
101
102set -o nounset
103Command=${0##*/}
104integer Errors=0
105
106typeset ocwd
107typeset tmpdir
108typeset out
109
110# create temporary test directory
111ocwd="$PWD"
112tmpdir="$(mktemp -t -d "test_sun_solaris_cr_6907460_EXIT_trap_handlers_are_sometimes_executed_twice.XXXXXXXX")" || err_exit "Cannot create temporary directory"
113
114cd "${tmpdir}" || { err_exit "cd ${tmpdir} failed." ; exit $((Errors)) ; }
115
116
117# run tests
118
119# test 1: Run test with some variations
120compound vari
121typeset testname
122
123for vari.shell_options in \
124	"" \
125	"-o xtrace" \
126	"-o errexit" \
127	"-o errexit -o xtrace" ; do
128	for vari.xtrace1 in \
129		"" \
130		"set -x" ; do
131		for vari.xtrace2 in \
132			"" \
133			"set -x" ; do
134			for vari.func_A_end in \
135				"" \
136				"print >&2" \
137				"return 0" \
138				"print >&2 ; return 0" ; do
139				for vari.subshell in \
140					$'x=$(B)' \
141					$'x=$( ( B ) )' \
142					$'x=${ B ; }' \
143					$'x=${ ( B ) ; }' \
144					$'( x=$(B) )' \
145					$'( x=$( ( B ) ) )' \
146					$'( x=${ B ; } )' \
147					$'( x=${ ( B ) ; } )' ; do
148					testname="$( printf "test |%#B|\n" vari )"
149
150cat >"testscript.sh" <<EOF
151		function A
152		{
153			${vari.xtrace1}
154			trap "print TRAP A >>log" EXIT
155			${vari.func_A_end}
156		}
157
158		function B
159		{
160			${vari.xtrace2}
161			trap "print TRAP B >>log" EXIT
162			A
163		}
164
165		rm -f log
166		${vari.subshell}
167EOF
168					${SHELL} ${vari.shell_options} "testscript.sh" >/dev/null 2>&1 || err_exit "${testname}: Unexpected error code $?"
169					rm "testscript.sh"
170
171					if [[ -f "log" ]] ; then
172						out="$( < log )"
173						rm "log"
174					else
175						err_exit "${testname}: File 'log' not found."
176					fi
177					[[ "${out}" == $'TRAP A\nTRAP B' ]] || err_exit "${testname}: Expected \$'TRAP A\nTRAP B', got $(printf "%q\n" "${out}")"
178				done
179			done
180		done
181	done
182done
183
184
185
186# test 2: This is the unmodified test from the bugster bug report
187(
188cat <<EOF
189	function A
190	{
191       		set -x
192        	trap "print TRAP A >>log" EXIT
193        	print >&2
194	}
195
196	function B
197	{
198		set -x
199		trap "print TRAP B >>log" EXIT
200		A
201	}
202
203	rm -f log
204	x=\$(B)
205EOF
206) | ${SHELL} >/dev/null 2>&1 || err_exit "Unexpected error code $?"
207
208if [[ -f "log" ]] ; then
209	out="$( < log )"
210	rm "log"
211else
212	err_exit "File 'log' not found."
213fi
214[[ "${out}" == $'TRAP A\nTRAP B' ]] || err_exit "Expected \$'TRAP A\nTRAP B', got $(printf "%q\n" "${out}")"
215
216
217cd "${ocwd}"
218rmdir "${tmpdir}" || err_exit "Cannot remove temporary directory ${tmpdir}".
219
220# tests done
221exit $((Errors))
222