134f9b3eeSRoland Mainz# 234f9b3eeSRoland Mainz# CDDL HEADER START 334f9b3eeSRoland Mainz# 434f9b3eeSRoland Mainz# The contents of this file are subject to the terms of the 534f9b3eeSRoland Mainz# Common Development and Distribution License (the "License"). 634f9b3eeSRoland Mainz# You may not use this file except in compliance with the License. 734f9b3eeSRoland Mainz# 834f9b3eeSRoland Mainz# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 934f9b3eeSRoland Mainz# or http://www.opensolaris.org/os/licensing. 1034f9b3eeSRoland Mainz# See the License for the specific language governing permissions 1134f9b3eeSRoland Mainz# and limitations under the License. 1234f9b3eeSRoland Mainz# 1334f9b3eeSRoland Mainz# When distributing Covered Code, include this CDDL HEADER in each 1434f9b3eeSRoland Mainz# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1534f9b3eeSRoland Mainz# If applicable, add the following below this CDDL HEADER, with the 1634f9b3eeSRoland Mainz# fields enclosed by brackets "[]" replaced with your own identifying 1734f9b3eeSRoland Mainz# information: Portions Copyright [yyyy] [name of copyright owner] 1834f9b3eeSRoland Mainz# 1934f9b3eeSRoland Mainz# CDDL HEADER END 2034f9b3eeSRoland Mainz# 2134f9b3eeSRoland Mainz 2234f9b3eeSRoland Mainz# 233e14f97fSRoger A. Faulkner# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 2434f9b3eeSRoland Mainz# 2534f9b3eeSRoland Mainz 2634f9b3eeSRoland Mainz# 2734f9b3eeSRoland Mainz# Written by Roland Mainz <roland.mainz@nrubsig.org> 2834f9b3eeSRoland Mainz# 2934f9b3eeSRoland Mainz 3034f9b3eeSRoland Mainz# test setup 3134f9b3eeSRoland Mainzfunction err_exit 3234f9b3eeSRoland Mainz{ 3334f9b3eeSRoland Mainz print -u2 -n "\t" 3434f9b3eeSRoland Mainz print -u2 -r ${Command}[$1]: "${@:2}" 353e14f97fSRoger A. Faulkner (( Errors < 127 && Errors++ )) 3634f9b3eeSRoland Mainz} 3734f9b3eeSRoland Mainzalias err_exit='err_exit $LINENO' 3834f9b3eeSRoland Mainz 3934f9b3eeSRoland Mainzset -o nounset 4034f9b3eeSRoland MainzCommand=${0##*/} 4134f9b3eeSRoland Mainzinteger Errors=0 4234f9b3eeSRoland Mainz 4334f9b3eeSRoland Mainz 4434f9b3eeSRoland Mainzfunction isvalidpid 4534f9b3eeSRoland Mainz{ 4634f9b3eeSRoland Mainz kill -0 ${1} 2>/dev/null && return 0 4734f9b3eeSRoland Mainz return 1 4834f9b3eeSRoland Mainz} 4934f9b3eeSRoland Mainzinteger testfilesize i maxwait 5034f9b3eeSRoland Mainztypeset tmpfile 5134f9b3eeSRoland Mainzinteger testid 5234f9b3eeSRoland Mainz 5334f9b3eeSRoland Mainz 5434f9b3eeSRoland Mainz######################################################################## 5534f9b3eeSRoland Mainz#### test set 001: 5634f9b3eeSRoland Mainz# run loop and check various temp filesizes 5734f9b3eeSRoland Mainz# (Please keep this test syncted with sun_solaris_cr_6800929_large_command_substitution_hang.sh) 5834f9b3eeSRoland Mainz 5934f9b3eeSRoland Mainz# test 1: run loop and check various temp filesizes 603e14f97fSRoger A. Faulknertmpfile="$(mktemp -t "ksh93_tests_command_substitution.${PPID}.$$.XXXXXX")" || err_exit "Cannot create temporary file." 6134f9b3eeSRoland Mainz 6234f9b3eeSRoland Mainzcompound test1=( 6334f9b3eeSRoland Mainz compound -a testcases=( 6434f9b3eeSRoland Mainz # test 1a: Run test child for $(...) 6534f9b3eeSRoland Mainz # (note the pipe chain has to end in a builtin command, an external command may not trigger the bug) 6634f9b3eeSRoland Mainz ( name="test1a" cmd="builtin cat ; print -- \"\$(cat \"${tmpfile}\" | cat)\" ; true" ) 6734f9b3eeSRoland Mainz # test 1b: Same as test1a but uses ${... ; } instead if $(...) 6834f9b3eeSRoland Mainz ( name="test1b" cmd="builtin cat ; print -- \"\${ cat \"${tmpfile}\" | cat ; }\" ; true" ) 6934f9b3eeSRoland Mainz # test 1c: Same as test1a but does not use a pipe 7034f9b3eeSRoland Mainz ( name="test1c" cmd="builtin cat ; print -- \"\$(cat \"${tmpfile}\" ; true)\" ; true" ) 7134f9b3eeSRoland Mainz # test 1d: Same as test1a but does not use a pipe 7234f9b3eeSRoland Mainz ( name="test1d" cmd="builtin cat ; print -- \"\${ cat \"${tmpfile}\" ; true ; }\" ; true" ) 7334f9b3eeSRoland Mainz 7434f9b3eeSRoland Mainz # test 1e: Same as test1a but uses an external "cat" command 7534f9b3eeSRoland Mainz ( name="test1e" cmd="builtin -d cat /bin/cat ; print -- \"\$(cat \"${tmpfile}\" | cat)\" ; true" ) 7634f9b3eeSRoland Mainz # test 1f: Same as test1a but uses an external "cat" command 7734f9b3eeSRoland Mainz ( name="test1f" cmd="builtin -d cat /bin/cat ; print -- \"\${ cat \"${tmpfile}\" | cat ; }\" ; true" ) 7834f9b3eeSRoland Mainz # test 1g: Same as test1a but uses an external "cat" command 7934f9b3eeSRoland Mainz ( name="test1g" cmd="builtin -d cat /bin/cat ; print -- \"\$(cat \"${tmpfile}\" ; true)\" ; true" ) 8034f9b3eeSRoland Mainz # test 1h: Same as test1a but uses an external "cat" command 8134f9b3eeSRoland Mainz ( name="test1h" cmd="builtin -d cat /bin/cat ; print -- \"\${ cat \"${tmpfile}\" ; true ; }\" ; true" ) 8234f9b3eeSRoland Mainz ) 8334f9b3eeSRoland Mainz) 8434f9b3eeSRoland Mainz 8534f9b3eeSRoland Mainzfor (( testfilesize=1*1024 ; testfilesize <= 1024*1024 ; testfilesize*=2 )) ; do 8634f9b3eeSRoland Mainz # Create temp file 8734f9b3eeSRoland Mainz { 8834f9b3eeSRoland Mainz for (( i=0 ; i < testfilesize ; i+=64 )) ; do 8934f9b3eeSRoland Mainz print "0123456789abcdef01234567890ABCDEF0123456789abcdef01234567890ABCDE" 9034f9b3eeSRoland Mainz done 9134f9b3eeSRoland Mainz } >"${tmpfile}" 9234f9b3eeSRoland Mainz 9334f9b3eeSRoland Mainz # wait up to log2(i) seconds for the child to terminate 9434f9b3eeSRoland Mainz # (this is 10 seconds for 1KB and 19 seconds for 512KB) 9534f9b3eeSRoland Mainz (( maxwait=log2(testfilesize) )) 96*b30d1939SAndy Fiddaman 9734f9b3eeSRoland Mainz for testid in "${!test1.testcases[@]}" ; do 9834f9b3eeSRoland Mainz nameref currtst=test1.testcases[testid] 9934f9b3eeSRoland Mainz ${SHELL} -o errexit -c "${currtst.cmd}" >"${tmpfile}.out" & 10034f9b3eeSRoland Mainz (( childpid=$! )) 10134f9b3eeSRoland Mainz 10234f9b3eeSRoland Mainz for (( i=0 ; i < maxwait ; i++ )) ; do 10334f9b3eeSRoland Mainz isvalidpid ${childpid} || break 10434f9b3eeSRoland Mainz sleep 0.25 10534f9b3eeSRoland Mainz done 10634f9b3eeSRoland Mainz 10734f9b3eeSRoland Mainz if isvalidpid ${childpid} ; then 10834f9b3eeSRoland Mainz err_exit "${currtst.name}: child (pid=${childpid}) still busy, filesize=${testfilesize}." 10934f9b3eeSRoland Mainz kill -KILL ${childpid} 2>/dev/null 11034f9b3eeSRoland Mainz fi 11134f9b3eeSRoland Mainz wait || err_exit "${currtst.name}: Child returned non-zero exit code." # wait for child (and/or avoid zombies/slime) 11234f9b3eeSRoland Mainz 11334f9b3eeSRoland Mainz # compare input/output 11434f9b3eeSRoland Mainz cmp -s "${tmpfile}" "${tmpfile}.out" || err_exit "${currtst.name}: ${tmpfile} and ${tmpfile}.out differ, filesize=${testfilesize}." 11534f9b3eeSRoland Mainz rm "${tmpfile}.out" 11634f9b3eeSRoland Mainz done 11734f9b3eeSRoland Mainz 11834f9b3eeSRoland Mainz # Cleanup 11934f9b3eeSRoland Mainz rm "${tmpfile}" 12034f9b3eeSRoland Mainzdone 12134f9b3eeSRoland Mainz 12234f9b3eeSRoland Mainz 12334f9b3eeSRoland Mainz######################################################################## 12434f9b3eeSRoland Mainz#### test set 002: 12534f9b3eeSRoland Mainz# If a command substitution calls a function and that function contains 12634f9b3eeSRoland Mainz# a command substitution which contains a piped command, the original 12734f9b3eeSRoland Mainz# command substitution calling the function will return 127 instead of 0. 12834f9b3eeSRoland Mainz# This is causing problems in several VSC tests. 12934f9b3eeSRoland Mainz# If we remove the piped command from the simple 13034f9b3eeSRoland Mainz# case in the attached script, it returns 0. 13134f9b3eeSRoland Mainz 13234f9b3eeSRoland Mainztypeset str 13334f9b3eeSRoland Mainztypeset testbody 13434f9b3eeSRoland Mainztypeset testout 13534f9b3eeSRoland Mainz 13634f9b3eeSRoland Mainztestbody=$( 13734f9b3eeSRoland Mainz# <CS> means command substitution start, <CE> means command substitution end 13834f9b3eeSRoland Mainzcat <<EOF 13934f9b3eeSRoland Mainzmyfunc () 140*b30d1939SAndy Fiddaman{ 14134f9b3eeSRoland Mainz pipedcmd=<CS> printf "hi" | tr "h" "H" <CE> 14234f9b3eeSRoland Mainz echo \$pipedcmd 14334f9b3eeSRoland Mainz 14434f9b3eeSRoland Mainz return 0 14534f9b3eeSRoland Mainz} 14634f9b3eeSRoland Mainz 14734f9b3eeSRoland Mainzfoo=<CS>myfunc<CE> 14834f9b3eeSRoland Mainzretval=\$? 14934f9b3eeSRoland Mainz 15034f9b3eeSRoland Mainzif [ "\$foo"X != "HiX" ]; then 15134f9b3eeSRoland Mainz echo "myfunc returned '\${foo}'; expected 'Hi'" 15234f9b3eeSRoland Mainzfi 15334f9b3eeSRoland Mainz 15434f9b3eeSRoland Mainzif [ \$retval -ne 0 ]; then 15534f9b3eeSRoland Mainz echo "command substitution calling myfunc returned \"\${retval}\"; expected 0" 15634f9b3eeSRoland Mainzelse 15734f9b3eeSRoland Mainz echo "command substitution calling myfunc successfully returned 0" 15834f9b3eeSRoland Mainzfi 15934f9b3eeSRoland MainzEOF 16034f9b3eeSRoland Mainz) 16134f9b3eeSRoland Mainz 16234f9b3eeSRoland Mainz 16334f9b3eeSRoland Mainz# Test 002/a: Plain test 16434f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/$(/g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" } 16534f9b3eeSRoland Mainz[[ "${testout}" == "command substitution calling myfunc successfully returned 0" ]] || err_exit "Expected 'command substitution calling myfunc successfully returned 0', got ${testout}" 16634f9b3eeSRoland Mainz 16734f9b3eeSRoland Mainz# Test 002/b: Same as test002/a but replaces "$(" with "${" 16834f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/${ /g;s/<CE>/ ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" } 16934f9b3eeSRoland Mainz[[ "${testout}" == "command substitution calling myfunc successfully returned 0" ]] || err_exit "Expected 'command substitution calling myfunc successfully returned 0', got ${testout}" 17034f9b3eeSRoland Mainz 17134f9b3eeSRoland Mainz# Test 002/c: Same as test002/a but forces |fork()| for a subshell via "ulimit -c 0" 17234f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/$( ulimit -c 0 ; /g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" } 17334f9b3eeSRoland Mainz[[ "${testout}" == "command substitution calling myfunc successfully returned 0" ]] || err_exit "Expected 'command substitution calling myfunc successfully returned 0', got ${testout}" 17434f9b3eeSRoland Mainz 17534f9b3eeSRoland Mainz# Test 002/d: Same as test002/a but uses extra subshell 17634f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/$( ( /g;s/<CE>/) )/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" } 17734f9b3eeSRoland Mainz[[ "${testout}" == "command substitution calling myfunc successfully returned 0" ]] || err_exit "Expected 'command substitution calling myfunc successfully returned 0', got ${testout}" 17834f9b3eeSRoland Mainz 179*b30d1939SAndy Fiddaman# Test 002/e: Same as test002/b but uses extra subshell after "${ " 18034f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/${ ( /g;s/<CE>/) ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" } 18134f9b3eeSRoland Mainz[[ "${testout}" == "command substitution calling myfunc successfully returned 0" ]] || err_exit "Expected 'command substitution calling myfunc successfully returned 0', got ${testout}" 18234f9b3eeSRoland Mainz 18334f9b3eeSRoland Mainz 18434f9b3eeSRoland Mainz 18534f9b3eeSRoland Mainz 18634f9b3eeSRoland Mainz######################################################################## 18734f9b3eeSRoland Mainz#### test set 003: 18834f9b3eeSRoland Mainz# An expression within backticks which should return false, instead 18934f9b3eeSRoland Mainz# returns true (0). 19034f9b3eeSRoland Mainz 19134f9b3eeSRoland Mainztypeset str 19234f9b3eeSRoland Mainztypeset testbody 19334f9b3eeSRoland Mainztypeset testout 19434f9b3eeSRoland Mainz 19534f9b3eeSRoland Mainztestbody=$( 19634f9b3eeSRoland Mainz# <CS> means command substitution start, <CE> means command substitution end 19734f9b3eeSRoland Mainzcat <<EOF 19834f9b3eeSRoland Mainzif <CS>expr "NOMATCH" : ".*Z" > /dev/null<CE> ; then 19934f9b3eeSRoland Mainz echo "xerror" 20034f9b3eeSRoland Mainzelse 20134f9b3eeSRoland Mainz echo "xok" 20234f9b3eeSRoland Mainzfi 20334f9b3eeSRoland MainzEOF 20434f9b3eeSRoland Mainz) 20534f9b3eeSRoland Mainz 20634f9b3eeSRoland Mainz 20734f9b3eeSRoland Mainz# Test 003/a: Plain test 20834f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/$(/g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" } 20934f9b3eeSRoland Mainz[[ "${testout}" == "xok" ]] || err_exit "Expected 'xok', got ${testout}" 21034f9b3eeSRoland Mainz 21134f9b3eeSRoland Mainz# Test 003/b: Same as test003/a but replaces "$(" with "${" 21234f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/${ /g;s/<CE>/ ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" } 21334f9b3eeSRoland Mainz[[ "${testout}" == "xok" ]] || err_exit "Expected 'xok', got ${testout}" 21434f9b3eeSRoland Mainz 21534f9b3eeSRoland Mainz# Test 003/c: Same as test003/a but forces |fork()| for a subshell via "ulimit -c 0" 21634f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/$( ulimit -c 0 ; /g;s/<CE>/)/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" } 21734f9b3eeSRoland Mainz[[ "${testout}" == "xok" ]] || err_exit "Expected 'xok', got ${testout}" 21834f9b3eeSRoland Mainz 21934f9b3eeSRoland Mainz# Test 003/d: Same as test003/a but uses extra subshell 22034f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/$( ( /g;s/<CE>/) )/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" } 22134f9b3eeSRoland Mainz[[ "${testout}" == "xok" ]] || err_exit "Expected 'xok', got ${testout}" 22234f9b3eeSRoland Mainz 223*b30d1939SAndy Fiddaman# Test 003/e: Same as test003/b but uses extra subshell after "${ " 22434f9b3eeSRoland Mainztestout=${ printf "%B\n" testbody | sed 's/<CS>/${ ( /g;s/<CE>/) ; }/g' | ${SHELL} 2>&1 || err_exit "command returned exit code $?" } 22534f9b3eeSRoland Mainz[[ "${testout}" == "xok" ]] || err_exit "Expected 'xok', got ${testout}" 22634f9b3eeSRoland Mainz 22734f9b3eeSRoland Mainz 22834f9b3eeSRoland Mainz######################################################################## 22934f9b3eeSRoland Mainz#### test set 004: 23034f9b3eeSRoland Mainz# test pipe within ${... ; } command subtitution ending in a 23134f9b3eeSRoland Mainz# non-builtin command (therefore we use "/bin/cat" instead of "cat" below 23234f9b3eeSRoland Mainz# to force the use of the external "cat" command). ast-ksh.2009-01-20 23334f9b3eeSRoland Mainz# had a bug which caused this test to fail. 23434f9b3eeSRoland Mainztestout=$( ${SHELL} -c 'pipedcmd=${ printf "hi" | /bin/cat ; } ; print $pipedcmd' ) 23534f9b3eeSRoland Mainz[[ "${testout}" == "hi" ]] || err_exit "test004: Expected 'hi', got '${testout}'" 23634f9b3eeSRoland Mainz 23734f9b3eeSRoland Mainz 23834f9b3eeSRoland Mainz######################################################################## 23934f9b3eeSRoland Mainz#### test set 005: 24034f9b3eeSRoland Mainz# Test whether the shell may hang in a 24134f9b3eeSRoland Mainz# 'exec 5>/dev/null; print $(eval ls -d . 2>&1 1>&5)' 24234f9b3eeSRoland Mainz# Originally discovered with ast-ksh.2009-05-05 which hung in 243*b30d1939SAndy Fiddaman# the "configure" script of postgresql-8.3.7.tar.gz (e.g. 24434f9b3eeSRoland Mainz# configure --enable-thread-safety --without-readline) 24534f9b3eeSRoland Mainzcompound test5=( 24634f9b3eeSRoland Mainz compound -a testcases=( 24734f9b3eeSRoland Mainz # gsf's reduced testcase 24834f9b3eeSRoland Mainz ( name="test5_a" cmd='exec 5>/dev/null; print $(eval ls -d . 2>&1 1>&5)done' ) 24934f9b3eeSRoland Mainz # gisburn's reduced testcase 25034f9b3eeSRoland Mainz ( name="test5_b" cmd='exec 5>/dev/null; print $(eval "/bin/printf hello\n" 2>&1 1>&5)done' ) 25134f9b3eeSRoland Mainz 25234f9b3eeSRoland Mainz ## The following tests do not trigger the problem but are included here for completeness 25334f9b3eeSRoland Mainz ## and to make sure we don't get other incarnations of the same problem later... 25434f9b3eeSRoland Mainz 25534f9b3eeSRoland Mainz # same as test5_a but uses ${ ... ; } instead of $(...) 25634f9b3eeSRoland Mainz ( name="test5_c" cmd='exec 5>/dev/null; print "${ eval ls -d . 2>&1 1>&5 ;}done"' ) 25734f9b3eeSRoland Mainz # same as test5_b but uses ${ ... ; } instead of $(...) 25834f9b3eeSRoland Mainz ( name="test5_d" cmd='exec 5>/dev/null; print "${ eval "/bin/printf hello\n" 2>&1 1>&5 ;}done"' ) 25934f9b3eeSRoland Mainz # same as test5_a but uses "ulimit -c 0" to force the shell to use a seperare process for $(...) 26034f9b3eeSRoland Mainz ( name="test5_e" cmd='exec 5>/dev/null; print $(ulimit -c 0 ; eval ls -d . 2>&1 1>&5)done' ) 26134f9b3eeSRoland Mainz # same as test5_b but uses "ulimit -c 0" to force the shell to use a seperare process for $(...) 26234f9b3eeSRoland Mainz ( name="test5_f" cmd='exec 5>/dev/null; print $(ulimit -c 0 ; eval "/bin/printf hello\n" 2>&1 1>&5)done' ) 26334f9b3eeSRoland Mainz ) 26434f9b3eeSRoland Mainz) 26534f9b3eeSRoland Mainz 26634f9b3eeSRoland Mainzmaxwait=5 26734f9b3eeSRoland Mainzfor testid in "${!test5.testcases[@]}" ; do 26834f9b3eeSRoland Mainz nameref currtst=test5.testcases[testid] 26934f9b3eeSRoland Mainz ${SHELL} -o errexit -c "${currtst.cmd}" >"${tmpfile}.out" & 27034f9b3eeSRoland Mainz (( childpid=$! )) 27134f9b3eeSRoland Mainz 27234f9b3eeSRoland Mainz for (( i=0 ; i < maxwait ; i++ )) ; do 27334f9b3eeSRoland Mainz isvalidpid ${childpid} || break 27434f9b3eeSRoland Mainz sleep 0.25 27534f9b3eeSRoland Mainz done 27634f9b3eeSRoland Mainz 27734f9b3eeSRoland Mainz if isvalidpid ${childpid} ; then 27834f9b3eeSRoland Mainz err_exit "${currtst.name}: child (pid=${childpid}) still busy." 27934f9b3eeSRoland Mainz kill -KILL ${childpid} 2>/dev/null 28034f9b3eeSRoland Mainz fi 28134f9b3eeSRoland Mainz wait || err_exit "${currtst.name}: Child returned non-zero exit code." # wait for child (and/or avoid zombies/slime) 28234f9b3eeSRoland Mainz 28334f9b3eeSRoland Mainz testout="$( < "${tmpfile}.out")" 28434f9b3eeSRoland Mainz rm "${tmpfile}.out" || err_exit "File '${tmpfile}.out' could not be removed." 28534f9b3eeSRoland Mainz [[ "${testout}" == "done" ]] || err_exit "test '${currtst.name}' failed, expected 'done', got '${testout}'" 28634f9b3eeSRoland Mainzdone 28734f9b3eeSRoland Mainz 28834f9b3eeSRoland Mainz 28934f9b3eeSRoland Mainz# tests done 29034f9b3eeSRoland Mainzexit $((Errors)) 291