Home | History | Annotate | Download | only in testscripts
      1 #!/bin/bash
      2 
      3 #
      4 # Copyright (c) International Business Machines  Corp., 2005
      5 # Authors: Avantika Mathur (mathurav (at] us.ibm.com)
      6 #          Matt Helsley (matthltc (at] us.ibm.com)
      7 #
      8 # This library is free software; you can redistribute it and/or
      9 # modify it under the terms of the GNU Lesser General Public
     10 # License as published by the Free Software Foundation; either
     11 # version 2.1 of the License, or (at your option) any later version.
     12 #
     13 # This library is distributed in the hope that it will be useful,
     14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16 # Lesser General Public License for more details.
     17 #
     18 # You should have received a copy of the GNU Lesser General Public
     19 # License along with this library; if not, write to the Free Software
     20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     21 #
     22 
     23 if tst_kvcmp -lt "2.6.15"; then
     24        tst_resm TCONF "System kernel version is less than 2.6.15"
     25        tst_resm TCONF "Cannot execute test"
     26        exit 0
     27 fi
     28 
     29 test_setup()
     30 {
     31 	#######################################################################
     32 	## Configure
     33 	#######################################################################
     34 	dopts='-dEBb'
     35 
     36 	## Remove logged test state depending on results. 0 means do not remove,
     37 	## 1 means OK to remove.
     38 	# rm saved state from tests that appear to have cleaned up properly?
     39 	rm_ok=1
     40 	# rm saved state from tests that don't appear to have fully cleaned up?
     41 	rm_err=0
     42 
     43 	#######################################################################
     44 	## Initialize some variables
     45 	#######################################################################
     46 	TCID="$0"
     47 	TST_COUNT=0
     48 
     49 	test_dirs=( move bind rbind regression )  #cloneNS
     50 	nfailed=0
     51 	nsucceeded=0
     52 
     53 	# set the LTPROOT directory
     54 	cd `dirname $0`
     55 	LTPROOT="${PWD}"
     56 	echo "${LTPROOT}" | grep testscripts > /dev/null 2>&1
     57 	if [ $? -eq 0 ]; then
     58 		cd ..
     59 		LTPROOT="${PWD}"
     60 	fi
     61 
     62 	FS_BIND_ROOT="${LTPROOT}/testcases/bin/fs_bind"
     63 
     64 	total=0 # total number of tests
     65 	for dir in "${test_dirs[@]}" ; do
     66 		((total += `ls "${FS_BIND_ROOT}/${dir}/test"* | wc -l`))
     67 	done
     68 	TST_TOTAL=${total}
     69 
     70 	# set the PATH to include testcases/bin
     71 	LTPBIN="${LTPROOT}/testcases/bin"
     72 	PATH="${PATH}:/usr/sbin:${LTPBIN}:${FS_BIND_ROOT}/bin"
     73 
     74 	# Results directory
     75 	resdir="${LTPROOT}/results/fs_bind"
     76 	if [ ! -d "${resdir}" ]; then
     77 		mkdir -p "${resdir}" 2> /dev/null
     78 	fi
     79 
     80 	TMPDIR="${TMPDIR:-/tmp}"
     81 	# A temporary directory where we can do stuff and that is
     82 	# safe to remove
     83 	sandbox="${TMPDIR}/sandbox"
     84 
     85 	ERR_MSG=""
     86 
     87 	export LTPBIN PATH FS_BIND_ROOT ERR_MSG TCID TST_COUNT TST_TOTAL
     88 
     89 	if [ ! -d "${resdir}" ]; then
     90 		tst_brkm TBROK true "$0: failed to make results directory"
     91 		exit 1
     92 	fi
     93 }
     94 
     95 test_prereqs()
     96 {
     97 	# Must be root to run the containers testsuite
     98 	if [ $UID != 0 ]; then
     99 		tst_brkm TBROK true "FAILED: Must be root to execute this script"
    100 		exit 1
    101 	fi
    102 
    103 	mkdir "${sandbox}" >& /dev/null
    104 	if [ ! -d "${sandbox}" -o ! -x "${sandbox}" ]; then
    105 		tst_brkm TBROK true "$0: failed to make directory \"${sandbox}\""
    106 		exit -1
    107 	fi
    108 
    109 	mount --bind "${sandbox}" "${sandbox}" >& /dev/null
    110 	if [ $? -ne 0 ]; then
    111 		tst_brkm TBROK true "$0: failed to perform bind mount on directory \"${sandbox}\""
    112 		exit 1
    113 	fi
    114 
    115 	mount --make-private "${sandbox}" >& /dev/null
    116 	if [ $? -ne 0 ]; then
    117 		tst_brkm TBROK true "$0: failed to make private mountpoint on directory \"${sandbox}\""
    118 		exit 1
    119 	fi
    120 
    121 	local mnt_bind=1
    122 	local mnt_move=1
    123 
    124 	pushd "${sandbox}" > /dev/null && {
    125 		mkdir bind_test move_test && {
    126 			mount --bind bind_test bind_test && {
    127 				mnt_bind=0
    128 				mount --move bind_test move_test && {
    129 					mnt_move=0
    130 					umount move_test
    131 				} || {
    132 					# bind mount succeeded but move mount
    133 					# failed
    134 					umount bind_test
    135 				}
    136 			} || {
    137 				# mount failed -- check if it's because we
    138 				# don't have privileges we need
    139 				if [ $? -eq 32 ]; then
    140 					tst_brkm TBROK true "$0 requires the privilege to use the mount command"
    141 					exit 32
    142 				fi
    143 			}
    144 			rmdir bind_test move_test
    145 		}
    146 		popd > /dev/null
    147 	}
    148 
    149 	if [ ${mnt_bind} -eq 1 -o ${mnt_move} -eq 1 ]; then
    150 		tst_brkm TBROK true "$0: requires that mount support the --bind and --move options"
    151 		exit 1
    152 	fi
    153 
    154 	if tst_kvcmp -lt "2.6.15"; then
    155 		tst_resm TWARN "$0: the remaining tests require 2.6.15 or later"
    156 		tst_exit 0
    157 		exit
    158 	else
    159 		tst_resm TINFO "$0: kernel >= 2.6.15 detected -- continuing"
    160 	fi
    161 
    162 	mount --make-shared "${sandbox}" > /dev/null 2>&1 || "${FS_BIND_ROOT}/bin/smount" "${sandbox}" shared
    163 	umount "${sandbox}" || {
    164 		tst_resm TFAIL "$0: failed to umount simplest shared subtree"
    165 		exit 1
    166 	}
    167 	tst_resm TPASS "$0: umounted simplest shared subtree"
    168 
    169 }
    170 
    171 # mounts we are concerned with in a well-defined order (helps diff)
    172 # returns grep return codes
    173 grep_proc_mounts()
    174 {
    175 	local rc=0
    176 
    177 	# Save the pipefail shell option
    178 	shopt -o -q pipefail
    179 	local save=$?
    180 	set -o pipefail
    181 
    182 	# Grep /proc/mounts which is often more up-to-date than mounts
    183 	# We use pipefail because if the grep fails we want to pass that along
    184 	grep -F "${sandbox}" /proc/mounts | sort -b
    185 	rc=$?
    186 
    187 	# Restore the pipefail shell options
    188 	[ $save -eq 0 ] && shopt -o -s pipefail || shopt -o -u pipefail
    189 
    190 	return $rc
    191 }
    192 
    193 # Record the mount state
    194 save_proc_mounts()
    195 {
    196 	touch "$2/proc_mounts.before" >& /dev/null
    197 	if [ $? -ne 0 ]; then
    198 		tst_brkm TBROK true "$1: failed to record proc mounts"
    199 		return 1
    200 	fi
    201 
    202 	grep_proc_mounts 2> /dev/null > "$2/proc_mounts.before"
    203 	return 0
    204 }
    205 
    206 # Compare mount list after the test with the list from before.
    207 # If there are no differences then remove the before list and silently
    208 # return 0. Else print the differences to stderr and return 1.
    209 check_proc_mounts()
    210 {
    211 	local tname="$1"
    212 
    213 	if [ ! -r "$2/proc_mounts.before" ]; then
    214 		tst_brkm TBROK true "${tname}: Could not find pre-test proc mount list"
    215 		return 1
    216 	fi
    217 
    218 	grep_proc_mounts 2> /dev/null > "$2/proc_mounts.after"
    219 	# If the mounts are the same then just return
    220 	diff ${dopts} -q "$2/proc_mounts.before" "$2/proc_mounts.after" >& /dev/null
    221 	if [ $? -eq 0 ]; then
    222 		[ $rm_ok -eq 1 ] && rm -f "$2/proc_mounts."{before,after}
    223 		return 0
    224 	fi
    225 
    226 	tst_resm TWARN "${tname}: did not properly clean up its proc mounts"
    227 	diff ${dopts} -U 0 "$2/proc_mounts.before" "$2/proc_mounts.after" | grep -vE '^\@\@' 1>&2
    228 	[ $rm_err -eq 1 ] && rm -f "$2/proc_mounts."{before,after}
    229 	return 1
    230 }
    231 
    232 # Undo leftover mounts
    233 restore_proc_mounts()
    234 {
    235 	#local tname="$1"
    236 
    237 	# do lazy umounts -- we're assuming that tests will only leave
    238 	# new mounts around and will never remove mounts outside the test
    239 	# directory
    240 	( while grep_proc_mounts ; do
    241 		grep_proc_mounts | awk '{print $2}' | xargs -r -n 1 umount -l
    242 	done ) >& /dev/null
    243 
    244 	# mount list and exit with 0
    245 	[ $rm_err -eq 1 ] && rm -f "$2/proc_mounts."{before,after} 1>&2 # >& /dev/null
    246 	return 0
    247 	# if returning error do this:
    248 	# tst_brkm TBROK true "${tname}: failed to restore mounts"
    249 }
    250 
    251 # mounts we are concerned with in a well-defined order (helps diff)
    252 # returns grep return codes
    253 grep_mounts()
    254 {
    255 	local rc=0
    256 
    257 	# Save the pipefail shell option
    258 	shopt -o -q pipefail
    259 	local save=$?
    260 	set -o pipefail
    261 
    262 	# Grep mount command output (which tends to come from /etc/mtab)
    263 	# We use pipefail because if the grep fails we want to pass that along
    264 	mount | grep -F "${sandbox}" | sort -b
    265 	rc=$?
    266 
    267 	# Restore the pipefail shell options
    268 	[ $save -eq 0 ] && shopt -o -s pipefail || shopt -o -u pipefail
    269 
    270 	return $rc
    271 }
    272 
    273 # Record the mount state
    274 save_mounts()
    275 {
    276 	touch "$2/mtab.before" >& /dev/null
    277 	if [ $? -ne 0 ]; then
    278 		tst_brkm TBROK true "$1: failed to record mtab mounts"
    279 		return 1
    280 	fi
    281 
    282 	grep_mounts 2> /dev/null > "$2/mtab.before"
    283 	return 0
    284 }
    285 
    286 # Compare mount list after the test with the list from before.
    287 # If there are no differences then remove the before list and silently
    288 # return 0. Else print the differences to stderr and return 1.
    289 check_mounts()
    290 {
    291 	local tname="$1"
    292 
    293 	if [ ! -r "$2/mtab.before" ]; then
    294 		tst_brkm TBROK true "${tname}: Could not find pre-test mtab mount list"
    295 		return 1
    296 	fi
    297 
    298 	grep_mounts 2> /dev/null > "$2/mtab.after"
    299 	# If the mounts are the same then just return
    300 	diff ${dopts} -q "$2/mtab.before" "$2/mtab.after" >& /dev/null
    301 	if [ $? -eq 0 ]; then
    302 		[ $rm_ok -eq 1 ] && rm -f "$2/mtab."{before,after}
    303 		return 0
    304 	fi
    305 
    306 	tst_resm TWARN "${tname}: did not properly clean up its mtab mounts"
    307 	diff ${dopts} -U 0 "$2/mtab.before" "$2/mtab.after" | grep -vE '^\@\@' 1>&2
    308 	[ $rm_err -eq 1 ] && rm -f "$2/mtab."{before,after}
    309 	return 1
    310 }
    311 
    312 # Undo leftover mounts
    313 restore_mounts()
    314 {
    315 	#local tname="$1"
    316 
    317 	# do lazy umounts -- we're assuming that tests will only leave
    318 	# new mounts around and will never remove mounts outside the test
    319 	# directory
    320 	( while grep_mounts ; do
    321 		grep_mounts | awk '{print $3}' | xargs -r -n 1 umount -l
    322 	done ) >& /dev/null
    323 
    324 	# mount list and exit with 0
    325 	[ $rm_err -eq 1 ] && rm -f "$2/mtab."{before,after} 1>&2 # >& /dev/null
    326 	return 0
    327 	# if returning error do this:
    328 	# tst_brkm TBROK true "${tname}: failed to restore mounts"
    329 }
    330 
    331 # Record the sandbox state
    332 # We don't save full sandbox state -- just the names of files and dirs present
    333 save_sandbox()
    334 {
    335 	local when="before"
    336 	local tname="$1"
    337 
    338 	if [ -e "$2/files.before" ]; then
    339 		if [ -e "$2/files.after" ]; then
    340 			tst_brkm TBROK true "${tname}: stale catalog of \"${sandbox}\""
    341 			return 1
    342 		fi
    343 		when="after"
    344 	fi
    345 
    346 	( find "${sandbox}" -type d -print | sort > "$2/dirs.$when"
    347 	  find "${sandbox}" -type f -print | sort | \
    348 		grep -vE '^'"$2"'/(dirs|files)\.(before|after)$' > "$2/files.$when" ) >& /dev/null
    349 	return 0
    350 }
    351 
    352 # Save sandbox after test and then compare. If the sandbox state is not
    353 # clean then print the differences to stderr and return 1. Else remove all
    354 # saved sandbox state and silently return 0
    355 check_sandbox()
    356 {
    357 	local tname="$1"
    358 
    359 	if [ ! -r "$2/files.before" -o ! -r "$2/dirs.before" ]; then
    360 		tst_brkm TBROK true "${tname} missing saved catalog of \"${sandbox}\""
    361 		return 1
    362 	fi
    363 
    364 	save_sandbox "${tname} (check)" "$2"
    365 
    366 	( diff ${dopts} -q "$2/dirs.before" "$2/dirs.after" && \
    367 	  diff ${dopts} -q "$2/files.before" "$2/files.after" )  >& /dev/null \
    368 	  && {
    369 		[ $rm_ok -eq 1 ] && rm -f "$2/"{files,dirs}.{before,after}
    370 		return 0
    371 	}
    372 
    373 	tst_resm TWARN "${tname} did not properly clean up \"${sandbox}\""
    374 	diff ${dopts} -U 0 "$2/dirs.before" "$2/dirs.after" 1>&2
    375 	diff ${dopts} -U 0 "$2/files.before" "$2/files.after" 1>&2
    376 	[ $rm_err -eq 1 ] && rm -f "$2/"{files,dirs}.{before,after} 1>&2
    377 	return 1
    378 }
    379 
    380 # Robust sandbox cleanup
    381 clean_sandbox()
    382 {
    383 	local tname="$1"
    384 
    385 	{ rm -rf "${sandbox}" ; mkdir "${sandbox}" ; } >& /dev/null
    386 	if [ ! -d "${sandbox}" -o ! -x "${sandbox}" ]; then
    387 		tst_brkm TBROK true "$tname: failed to make directory \"${sandbox}\""
    388 		return 1
    389 	fi
    390 	return 0
    391 }
    392 
    393 # Check file for non-whitespace chars
    394 is_file_empty()
    395 {
    396 	awk '/^[[:space:]]*$/  { next }
    397 	      { exit 1; }' < "$1"
    398 }
    399 
    400 #
    401 # Run the specified test script.
    402 #
    403 # Return 1 if the test was broken but should not stop the remaining test
    404 #	categories from being run.
    405 # Return 2 if the test was broken and no further tests should be run.
    406 # Return 0 otherwise (if the test was broken but it shouldn't affect other
    407 #	test runs)
    408 # Note that this means the return status is not the success or failure of the
    409 #	test itself.
    410 #
    411 run_test()
    412 {
    413 	local t="$1"
    414 	local tname="$(basename "$(dirname "$t")")/$(basename "$t")"
    415 	local log="$resdir/$tname/log"
    416 	local errlog="$resdir/$tname/err"
    417 	local do_break=0
    418 
    419 	ERR_MSG=""
    420 
    421 	# Pre-test
    422 	mkdir -p "$resdir/$tname"
    423 	if [ ! -d "$resdir/$tname" -o ! -x "$resdir/$tname" ]; then
    424 		tst_brkm TBROK true "$0: can't make or use \"$resdir/$tname\" as a log directory"
    425 		return 1
    426 	fi
    427 
    428 	save_sandbox "$tname" "$resdir/$tname" || do_break=1
    429 	save_mounts "$tname" "$resdir/$tname" || do_break=1
    430 	save_proc_mounts "$tname" "$resdir/$tname" || do_break=1
    431 	mount --bind "${sandbox}" "${sandbox}" >& /dev/null || do_break=1
    432 	mount --make-private "${sandbox}" >& /dev/null || do_break=1
    433 
    434 	if [ $do_break -eq 1 ]; then
    435 		umount -l "${sandbox}" >& /dev/null
    436 		tst_brkm TBROK true "$tname: failed to save pre-test state of \"${sandbox}\""
    437 		return 2
    438 	fi
    439 	pushd "${sandbox}" > /dev/null
    440 
    441 	# Run the test
    442 	(
    443 		TCID="$tname"
    444 		declare -r TST_COUNT
    445 		export LTPBIN PATH FS_BIND_ROOT ERR_MSG TCID TST_COUNT TST_TOTAL
    446 		"$t" #> "$log" 2> "$errlog"
    447 	)
    448 	local rc=$?
    449 	TCID="$0"
    450 
    451 	# Post-test
    452 	popd > /dev/null
    453 	if [ $rc -ne 0 ]; then
    454 		#echo "FAILED"
    455 		((nfailed++))
    456 	else
    457 		#echo "SUCCEEDED"
    458 		((nsucceeded++))
    459 	fi
    460 	umount -l "${sandbox}" >& /dev/null
    461 	check_proc_mounts "$tname" "$resdir/$tname" || \
    462 	restore_proc_mounts "$tname" "$resdir/$tname" || do_break=1
    463 	check_mounts "$tname" "$resdir/$tname" || \
    464 	restore_mounts "$tname" "$resdir/$tname" || do_break=1
    465 	check_sandbox "$tname" "$resdir/$tname"
    466 	clean_sandbox "$tname" || do_break=1
    467 	if [ $do_break -eq 1 ]; then
    468 		tst_brkm TBROK true "$tname: failed to restore pre-test state of \"${sandbox}\""
    469 		return 2
    470 	fi
    471 
    472 	# If we succeeded and the error log is empty remove it
    473 	if [ $rc -eq 0 -a -w "$errlog" ] && is_file_empty "$errlog" ; then
    474 		rm -f "$errlog"
    475 	fi
    476 	return 0
    477 }
    478 
    479 main()
    480 {
    481 	TST_COUNT=1
    482 	for dir in "${test_dirs[@]}" ; do
    483 		tests=( $(find "${FS_BIND_ROOT}/${dir}" -type f -name 'test*') )
    484 		clean_sandbox "$0" || break
    485 		for t in "${tests[@]}" ; do
    486 			run_test "$t"
    487 			local rc=$?
    488 
    489 			if [ $rc -ne 0 ]; then
    490 				break $rc
    491 			fi
    492 
    493 			((TST_COUNT++))
    494 		done
    495 	done
    496 	rm -rf "${sandbox}"
    497 	return 0
    498 
    499 	skipped=$((total - nsucceeded - nfailed))
    500 	if [ $nfailed -eq 0 -a $skipped -eq 0 ]; then
    501 		# Use PASSED for the summary rather than SUCCEEDED to make it
    502 		# easy to determine 100% success from a calling script
    503 		summary="PASSED"
    504 	else
    505 		# Use FAILED to make it easy to find > 0% failure from a
    506 		# calling script
    507 		summary="FAILED"
    508 	fi
    509 	cat - <<-EOF
    510 		*********************************
    511 		RESULTS SUMMARY:
    512 
    513 			passed:  $nsucceeded/$total
    514 			failed:  $nfailed/$total
    515 			skipped: $skipped/$total
    516 			summary: $summary
    517 
    518 		*********************************
    519 	EOF
    520 }
    521 
    522 test_setup || exit 1
    523 test_prereqs || exit 1
    524 declare -r FS_BIND_ROOT
    525 declare -r TST_TOTAL
    526 main  #2> "$resdir/errors" 1> "$resdir/summary"
    527