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