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# Test whether CR #6800929 ("snv_106 ksh93 update breaks Install(1M)") has been fixed.
28#
29# Quote from CR #6800929:
30# ---- snip ----
31# so i just upgraded this morning from snv_105 to snv_106.  now
32# Install(1M) is hanging whenever i run it.  i'm running it as follows:
33#         Install -o debug -k i86xpv -T domu-219:/tmp
34#
35# and here's where it's hung:
36# ---8<---
37#  Edward Pilatowicz <edward.pilatowicz@sun.com>
38# $ pstack 204600
39# 204600: /bin/ksh /opt/onbld/bin/Install -o debug -k i86xpv -T domu-219:/tmp
40#  fffffd7fff2e3d1a write    (1, 4154c0, 64)
41#  fffffd7ffefdafc8 sfwr () + 2d0
42#  fffffd7ffefc0f6f _sfflsbuf () + 217
43#  fffffd7ffefcb9f7 sfsync () + 17f
44#  fffffd7ffefc5c58 _sfphead () + 188
45#  fffffd7ffefc5ef5 _sfpmove () + 55
46#  fffffd7ffefc2595 _sfmode () + 22d
47#  fffffd7ffefc5fb1 sfpool () + 99
48#  fffffd7fff15eb8e sh_exec () + 2f56
49#  fffffd7fff15f78c sh_exec () + 3b54
50#  fffffd7fff15d9c8 sh_exec () + 1d90
51#  fffffd7fff15788e sh_subshell () + 646
52#  fffffd7fff134562 comsubst () + 8a2
53#  fffffd7fff12f61f copyto () + bcf
54#  fffffd7fff12df79 sh_macexpand () + 1f1
55#  fffffd7fff1129f5 arg_expand () + a5
56#  fffffd7fff112812 sh_argbuild () + 9a
57#  fffffd7fff15dbe2 sh_exec () + 1faa
58#  fffffd7fff15d854 sh_exec () + 1c1c
59#  fffffd7fff0f22ef b_dot_cmd () + 507
60#  fffffd7fff161559 sh_funct () + 199
61#  fffffd7fff15ef35 sh_exec () + 32fd
62#  fffffd7fff136e86 exfile () + 786
63#  fffffd7fff136676 sh_main () + 7fe
64#  0000000000400e72 main () + 52
65#  0000000000400ccc ????
66# ---8<---
67#
68# there is only one place where Install(1M) invokes "uniq":
69#         set -- `grep "^CONF" $modlist | sort | uniq`;
70#
71# as it turns out, i can easily reproduce this problem as follows:
72# ---8<---
73# $ ksh93
74# $ set -- `cat /etc/termcap | sort | uniq`
75# <hang>
76# ---8<---
77# ---- snip ----
78
79
80# test setup
81function err_exit
82{
83	print -u2 -n "\t"
84	print -u2 -r ${Command}[$1]: "${@:2}"
85	(( Errors < 127 && Errors++ ))
86}
87alias err_exit='err_exit $LINENO'
88
89set -o nounset
90Command=${0##*/}
91integer Errors=0
92
93# common functions/variables
94function isvalidpid
95{
96	kill -0 ${1} 2>/dev/null && return 0
97	return 1
98}
99integer testfilesize i maxwait
100typeset tmpfile
101integer testid
102
103
104# test 1: run loop and check various temp filesizes
105tmpfile="$(mktemp -t "sun_solaris_cr_6800929_large_command_substitution_hang.${PPID}.$$.XXXXXX")" || err_exit "Cannot create temporary file."
106
107compound -a testcases=(
108	# test 1a: Run test child for $(...)
109	# (note the pipe chain has to end in a builtin command, an external command may not trigger the bug)
110	( name="test1a" cmd="builtin cat ; print -- \"\$(cat \"${tmpfile}\" | cat)\" ; true" )
111	# test 1b: Same as test1a but uses ${... ; } instead if $(...)
112	( name="test1b" cmd="builtin cat ; print -- \"\${ cat \"${tmpfile}\" | cat ; }\" ; true" )
113	# test 1c: Same as test1a but does not use a pipe
114	( name="test1c" cmd="builtin cat ; print -- \"\$(cat \"${tmpfile}\" ; true)\" ; true" )
115	# test 1d: Same as test1a but does not use a pipe
116	( name="test1d" cmd="builtin cat ; print -- \"\${ cat \"${tmpfile}\" ; true ; }\" ; true" )
117
118	# test 1e: Same as test1a but uses an external "cat" command
119	( name="test1e" cmd="builtin -d cat /bin/cat ; print -- \"\$(cat \"${tmpfile}\" | cat)\" ; true" )
120	# test 1f: Same as test1a but uses an external "cat" command
121	( name="test1f" cmd="builtin -d cat /bin/cat ; print -- \"\${ cat \"${tmpfile}\" | cat ; }\" ; true" )
122	# test 1g: Same as test1a but uses an external "cat" command
123	( name="test1g" cmd="builtin -d cat /bin/cat ; print -- \"\$(cat \"${tmpfile}\" ; true)\" ; true" )
124	# test 1h: Same as test1a but uses an external "cat" command
125	( name="test1h" cmd="builtin -d cat /bin/cat ; print -- \"\${ cat \"${tmpfile}\" ; true ; }\" ; true" )
126)
127
128for (( testfilesize=1*1024 ; testfilesize <= 1024*1024 ; testfilesize*=2 )) ; do
129	# Create temp file
130	{
131		for (( i=0 ; i < testfilesize ; i+=64 )) ; do
132			print "0123456789abcdef01234567890ABCDEF0123456789abcdef01234567890ABCDE"
133		done
134	} >"${tmpfile}"
135
136	# wait up to log2(i) seconds for the child to terminate
137	# (this is 10 seconds for 1KB and 19 seconds for 512KB)
138	(( maxwait=log2(testfilesize) ))
139
140	for testid in "${!testcases[@]}" ; do
141		nameref currtst=testcases[testid]
142		${SHELL} -o errexit -c "${currtst.cmd}" >"${tmpfile}.out" &
143		(( childpid=$! ))
144
145		for (( i=0 ; i < maxwait ; i++ )) ; do
146			isvalidpid ${childpid} || break
147			sleep 0.25
148		done
149
150		if isvalidpid ${childpid} ; then
151			err_exit "${currtst.name}: child (pid=${childpid}) still busy, filesize=${testfilesize}."
152			kill -KILL ${childpid} 2>/dev/null
153		fi
154		wait || err_exit "${currtst.name}: Child returned non-zero exit code." # wait for child (and/or avoid zombies/slime)
155
156		# compare input/output
157		cmp -s "${tmpfile}" "${tmpfile}.out" || err_exit "${currtst.name}: ${tmpfile} and ${tmpfile}.out differ, filesize=${testfilesize}."
158		rm "${tmpfile}.out"
159	done
160
161	# Cleanup
162	rm "${tmpfile}"
163done
164
165
166# test 2a: Edward Pilatowicz <edward.pilatowicz@sun.com>'s Solaris-specific testcase
167${SHELL} -o errexit -c 'builtin uniq ; set -- `cat /etc/termcap | sort | uniq` ; true' >/dev/null &
168(( childpid=$! ))
169sleep 5
170if isvalidpid ${childpid} ; then
171	err_exit "test2a: child (pid=${childpid}) still busy."
172	kill -KILL ${childpid} 2>/dev/null
173fi
174wait || err_exit "test2a: Child returned non-zero exit code." # wait for child (and/or avoid zombies/slime)
175
176
177# test 2b: Same as test 2a but uses ${... ; } instead of $(...)
178${SHELL} -o errexit -c 'builtin uniq ; set -- ${ cat /etc/termcap | sort | uniq ; } ; true' >/dev/null &
179(( childpid=$! ))
180sleep 5
181if isvalidpid ${childpid} ; then
182	err_exit "test2b: child (pid=${childpid}) still busy."
183	kill -KILL ${childpid} 2>/dev/null
184fi
185wait || err_exit "test2b: Child returned non-zero exit code." # wait for child (and/or avoid zombies/slime)
186
187
188# test 2c: Same as test 2a but makes sure that "uniq" is not a builtin
189${SHELL} -o errexit -c 'builtin -d uniq /bin/uniq ; set -- `cat /etc/termcap | sort | uniq` ; true' >/dev/null &
190(( childpid=$! ))
191sleep 5
192if isvalidpid ${childpid} ; then
193	err_exit "test2c: child (pid=${childpid}) still busy."
194	kill -KILL ${childpid} 2>/dev/null
195fi
196wait || err_exit "test2c: Child returned non-zero exit code." # wait for child (and/or avoid zombies/slime)
197
198
199# test 2d: Same as test 2c but uses ${... ; } instead of $(...)
200${SHELL} -o errexit -c 'builtin -d uniq /bin/uniq ; set -- ${ cat /etc/termcap | sort | uniq ; } ; true' >/dev/null &
201(( childpid=$! ))
202sleep 5
203if isvalidpid ${childpid} ; then
204	err_exit "test2d: child (pid=${childpid}) still busy."
205	kill -KILL ${childpid} 2>/dev/null
206fi
207wait || err_exit "test2d: Child returned non-zero exit code." # wait for child (and/or avoid zombies/slime)
208
209
210# tests done
211exit $((Errors))
212