1#!/bin/sh 2# SPDX-License-Identifier: GPL-2.0-or-later 3# Copyright (c) Linux Test Project, 2014-2019 4# Author: Cyril Hrubis <chrubis@suse.cz> 5# 6# LTP test library for shell. 7 8[ -n "$TST_LIB_LOADED" ] && return 0 9 10export TST_PASS=0 11export TST_FAIL=0 12export TST_BROK=0 13export TST_WARN=0 14export TST_CONF=0 15export TST_COUNT=1 16export TST_ITERATIONS=1 17export TST_TMPDIR_RHOST=0 18export TST_LIB_LOADED=1 19 20. tst_ansi_color.sh 21. tst_security.sh 22 23# default trap function 24trap "tst_brk TBROK 'test interrupted'" INT 25 26_tst_do_exit() 27{ 28 local ret=0 29 TST_DO_EXIT=1 30 31 if [ -n "$TST_CLEANUP" -a -z "$TST_NO_CLEANUP" ]; then 32 $TST_CLEANUP 33 fi 34 35 if [ "$TST_NEEDS_DEVICE" = 1 -a "$TST_DEVICE_FLAG" = 1 ]; then 36 if ! tst_device release "$TST_DEVICE"; then 37 tst_res TWARN "Failed to release device '$TST_DEVICE'" 38 fi 39 fi 40 41 if [ "$TST_NEEDS_TMPDIR" = 1 -a -n "$TST_TMPDIR" ]; then 42 cd "$LTPROOT" 43 rm -r "$TST_TMPDIR" 44 [ "$TST_TMPDIR_RHOST" = 1 ] && tst_cleanup_rhost 45 fi 46 47 if [ -n "$_tst_setup_timer_pid" ]; then 48 kill $_tst_setup_timer_pid 2>/dev/null 49 wait $_tst_setup_timer_pid 2>/dev/null 50 fi 51 52 if [ $TST_FAIL -gt 0 ]; then 53 ret=$((ret|1)) 54 fi 55 56 if [ $TST_BROK -gt 0 ]; then 57 ret=$((ret|2)) 58 fi 59 60 if [ $TST_WARN -gt 0 ]; then 61 ret=$((ret|4)) 62 fi 63 64 if [ $TST_CONF -gt 0 -a $TST_PASS -eq 0 ]; then 65 ret=$((ret|32)) 66 fi 67 68 if [ $TST_BROK -gt 0 -o $TST_FAIL -gt 0 -o $TST_WARN -gt 0 ]; then 69 _tst_check_security_modules 70 fi 71 72 echo 73 echo "Summary:" 74 echo "passed $TST_PASS" 75 echo "failed $TST_FAIL" 76 echo "skipped $TST_CONF" 77 echo "warnings $TST_WARN" 78 79 exit $ret 80} 81 82_tst_inc_res() 83{ 84 case "$1" in 85 TPASS) TST_PASS=$((TST_PASS+1));; 86 TFAIL) TST_FAIL=$((TST_FAIL+1));; 87 TBROK) TST_BROK=$((TST_BROK+1));; 88 TWARN) TST_WARN=$((TST_WARN+1));; 89 TCONF) TST_CONF=$((TST_CONF+1));; 90 TINFO) ;; 91 *) tst_brk TBROK "Invalid res type '$1'";; 92 esac 93} 94 95tst_res() 96{ 97 local res=$1 98 shift 99 100 tst_color_enabled 101 local color=$? 102 103 _tst_inc_res "$res" 104 105 printf "$TST_ID $TST_COUNT " 106 tst_print_colored $res "$res: " 107 echo "$@" 108} 109 110tst_brk() 111{ 112 local res=$1 113 shift 114 115 if [ "$TST_DO_EXIT" = 1 ]; then 116 tst_res TWARN "$@" 117 return 118 fi 119 120 tst_res "$res" "$@" 121 _tst_do_exit 122} 123 124ROD_SILENT() 125{ 126 local tst_out="$(tst_rod $@ 2>&1)" 127 if [ $? -ne 0 ]; then 128 echo "$tst_out" 129 tst_brk TBROK "$@ failed" 130 fi 131} 132 133ROD() 134{ 135 tst_rod "$@" 136 if [ $? -ne 0 ]; then 137 tst_brk TBROK "$@ failed" 138 fi 139} 140 141_tst_expect_pass() 142{ 143 local fnc="$1" 144 shift 145 146 tst_rod "$@" 147 if [ $? -eq 0 ]; then 148 tst_res TPASS "$@ passed as expected" 149 return 0 150 else 151 $fnc TFAIL "$@ failed unexpectedly" 152 return 1 153 fi 154} 155 156_tst_expect_fail() 157{ 158 local fnc="$1" 159 shift 160 161 # redirect stderr since we expect the command to fail 162 tst_rod "$@" 2> /dev/null 163 if [ $? -ne 0 ]; then 164 tst_res TPASS "$@ failed as expected" 165 return 0 166 else 167 $fnc TFAIL "$@ passed unexpectedly" 168 return 1 169 fi 170} 171 172EXPECT_PASS() 173{ 174 _tst_expect_pass tst_res "$@" 175} 176 177EXPECT_PASS_BRK() 178{ 179 _tst_expect_pass tst_brk "$@" 180} 181 182EXPECT_FAIL() 183{ 184 _tst_expect_fail tst_res "$@" 185} 186 187EXPECT_FAIL_BRK() 188{ 189 _tst_expect_fail tst_brk "$@" 190} 191 192TST_RETRY_FN_EXP_BACKOFF() 193{ 194 local tst_fun="$1" 195 local tst_exp=$2 196 local tst_sec=$(($3 * 1000000)) 197 local tst_delay=1 198 199 _tst_multiply_timeout tst_sec 200 201 if [ $# -ne 3 ]; then 202 tst_brk TBROK "TST_RETRY_FN_EXP_BACKOFF expects 3 parameters" 203 fi 204 205 if ! tst_is_int "$tst_sec"; then 206 tst_brk TBROK "TST_RETRY_FN_EXP_BACKOFF: tst_sec must be integer ('$tst_sec')" 207 fi 208 209 while true; do 210 eval "$tst_fun" 211 if [ "$?" = "$tst_exp" ]; then 212 break 213 fi 214 215 if [ $tst_delay -lt $tst_sec ]; then 216 tst_sleep ${tst_delay}us 217 tst_delay=$((tst_delay*2)) 218 else 219 tst_brk TBROK "\"$tst_fun\" timed out" 220 fi 221 done 222 223 return $tst_exp 224} 225 226TST_RETRY_FUNC() 227{ 228 if [ $# -ne 2 ]; then 229 tst_brk TBROK "TST_RETRY_FUNC expects 2 parameters" 230 fi 231 232 TST_RETRY_FN_EXP_BACKOFF "$1" "$2" 1 233 return $2 234} 235 236TST_RTNL_CHK() 237{ 238 local msg1="RTNETLINK answers: Function not implemented" 239 local msg2="RTNETLINK answers: Operation not supported" 240 local msg3="RTNETLINK answers: Protocol not supported" 241 local output="$($@ 2>&1 || echo 'LTP_ERR')" 242 local msg 243 244 echo "$output" | grep -q "LTP_ERR" || return 0 245 246 for msg in "$msg1" "$msg2" "$msg3"; do 247 echo "$output" | grep -q "$msg" && tst_brk TCONF "'$@': $msg" 248 done 249 250 tst_brk TBROK "$@ failed: $output" 251} 252 253tst_mount() 254{ 255 local mnt_opt mnt_err 256 257 if [ -n "$TST_FS_TYPE" ]; then 258 mnt_opt="-t $TST_FS_TYPE" 259 mnt_err=" $TST_FS_TYPE type" 260 fi 261 262 ROD_SILENT mkdir -p $TST_MNTPOINT 263 mount $mnt_opt $TST_DEVICE $TST_MNTPOINT $TST_MNT_PARAMS 264 local ret=$? 265 266 if [ $ret -eq 32 ]; then 267 tst_brk TCONF "Cannot mount${mnt_err}, missing driver?" 268 fi 269 270 if [ $ret -ne 0 ]; then 271 tst_brk TBROK "Failed to mount device${mnt_err}: mount exit = $ret" 272 fi 273} 274 275tst_umount() 276{ 277 local device="${1:-$TST_DEVICE}" 278 local i=0 279 280 [ -z "$device" ] && return 281 282 if ! grep -q "$device" /proc/mounts; then 283 tst_res TINFO "The $device is not mounted, skipping umount" 284 return 285 fi 286 287 while [ "$i" -lt 50 ]; do 288 if umount "$device" > /dev/null; then 289 return 290 fi 291 292 i=$((i+1)) 293 294 tst_res TINFO "umount($device) failed, try $i ..." 295 tst_res TINFO "Likely gvfsd-trash is probing newly mounted "\ 296 "fs, kill it to speed up tests." 297 298 tst_sleep 100ms 299 done 300 301 tst_res TWARN "Failed to umount($device) after 50 retries" 302} 303 304tst_mkfs() 305{ 306 local fs_type=${1:-$TST_FS_TYPE} 307 local device=${2:-$TST_DEVICE} 308 [ $# -ge 1 ] && shift 309 [ $# -ge 1 ] && shift 310 local fs_opts="$@" 311 312 if [ -z "$fs_type" ]; then 313 tst_brk TBROK "No fs_type specified" 314 fi 315 316 if [ -z "$device" ]; then 317 tst_brk TBROK "No device specified" 318 fi 319 320 tst_require_cmds mkfs.$fs_type 321 322 tst_res TINFO "Formatting $device with $fs_type extra opts='$fs_opts'" 323 ROD_SILENT mkfs.$fs_type $fs_opts $device 324} 325 326tst_cmd_available() 327{ 328 if type command > /dev/null 2>&1; then 329 command -v $1 > /dev/null 2>&1 || return 1 330 else 331 which $1 > /dev/null 2>&1 332 if [ $? -eq 0 ]; then 333 return 0 334 elif [ $? -eq 127 ]; then 335 tst_brk TCONF "missing which command" 336 else 337 return 1 338 fi 339 fi 340} 341 342tst_require_cmds() 343{ 344 local cmd 345 for cmd in $*; do 346 tst_cmd_available $cmd || tst_brk TCONF "'$cmd' not found" 347 done 348} 349 350tst_check_cmds() 351{ 352 local cmd 353 for cmd; do 354 if ! tst_cmd_available $cmd; then 355 tst_res TCONF "'$cmd' not found" 356 return 1 357 fi 358 done 359 return 0 360} 361 362tst_require_drivers() 363{ 364 [ $# -eq 0 ] && return 0 365 366 local drv 367 368 drv="$(tst_check_drivers $@ 2>&1)" 369 370 [ $? -ne 0 ] && tst_brk TCONF "$drv driver not available" 371 return 0 372} 373 374tst_is_int() 375{ 376 [ "$1" -eq "$1" ] 2>/dev/null 377 return $? 378} 379 380tst_is_num() 381{ 382 echo "$1" | grep -Eq '^[-+]?[0-9]+\.?[0-9]*$' 383} 384 385tst_usage() 386{ 387 if [ -n "$TST_USAGE" ]; then 388 $TST_USAGE 389 else 390 echo "usage: $0" 391 echo "OPTIONS" 392 fi 393 394 echo "-h Prints this help" 395 echo "-i n Execute test n times" 396} 397 398_tst_resstr() 399{ 400 echo "$TST_PASS$TST_FAIL$TST_CONF" 401} 402 403_tst_rescmp() 404{ 405 local res=$(_tst_resstr) 406 407 if [ "$1" = "$res" ]; then 408 tst_brk TBROK "Test didn't report any results" 409 fi 410} 411 412_tst_multiply_timeout() 413{ 414 [ $# -ne 1 ] && tst_brk TBROK "_tst_multiply_timeout expect 1 parameter" 415 eval "local timeout=\$$1" 416 417 LTP_TIMEOUT_MUL=${LTP_TIMEOUT_MUL:-1} 418 419 local err="LTP_TIMEOUT_MUL must be number >= 1!" 420 421 tst_is_num "$LTP_TIMEOUT_MUL" || tst_brk TBROK "$err ($LTP_TIMEOUT_MUL)" 422 423 if ! tst_is_int "$LTP_TIMEOUT_MUL"; then 424 LTP_TIMEOUT_MUL=$(echo "$LTP_TIMEOUT_MUL" | cut -d. -f1) 425 LTP_TIMEOUT_MUL=$((LTP_TIMEOUT_MUL+1)) 426 tst_res TINFO "ceiling LTP_TIMEOUT_MUL to $LTP_TIMEOUT_MUL" 427 fi 428 429 [ "$LTP_TIMEOUT_MUL" -ge 1 ] || tst_brk TBROK "$err ($LTP_TIMEOUT_MUL)" 430 [ "$timeout" -ge 1 ] || tst_brk TBROK "timeout need to be >= 1 ($timeout)" 431 432 eval "$1='$((timeout * LTP_TIMEOUT_MUL))'" 433 return 0 434} 435 436_tst_setup_timer() 437{ 438 TST_TIMEOUT=${TST_TIMEOUT:-300} 439 440 if [ "$TST_TIMEOUT" = -1 ]; then 441 tst_res TINFO "Timeout per run is disabled" 442 return 443 fi 444 445 if ! tst_is_int "$TST_TIMEOUT" || [ "$TST_TIMEOUT" -lt 1 ]; then 446 tst_brk TBROK "TST_TIMEOUT must be int >= 1! ($TST_TIMEOUT)" 447 fi 448 449 local sec=$TST_TIMEOUT 450 _tst_multiply_timeout sec 451 local h=$((sec / 3600)) 452 local m=$((sec / 60 % 60)) 453 local s=$((sec % 60)) 454 local pid=$$ 455 456 tst_res TINFO "timeout per run is ${h}h ${m}m ${s}s" 457 458 sleep $sec && tst_res TBROK "test killed, timeout! If you are running on slow machine, try exporting LTP_TIMEOUT_MUL > 1" && kill -9 -$pid & 459 460 _tst_setup_timer_pid=$! 461} 462 463tst_require_root() 464{ 465 if [ "$(id -ru)" != 0 ]; then 466 tst_brk TCONF "Must be super/root for this test!" 467 fi 468} 469 470tst_require_module() 471{ 472 local _tst_module=$1 473 474 for tst_module in "$_tst_module" \ 475 "$LTPROOT/testcases/bin/$_tst_module" \ 476 "$TST_STARTWD/$_tst_module"; do 477 478 if [ -f "$tst_module" ]; then 479 TST_MODPATH="$tst_module" 480 break 481 fi 482 done 483 484 if [ -z "$TST_MODPATH" ]; then 485 tst_brk TCONF "Failed to find module '$_tst_module'" 486 fi 487 488 tst_res TINFO "Found module at '$TST_MODPATH'" 489} 490 491tst_run() 492{ 493 local _tst_i 494 local _tst_data 495 local _tst_max 496 local _tst_name 497 498 if [ -n "$TST_TEST_PATH" ]; then 499 for _tst_i in $(grep '^[^#]*\bTST_' "$TST_TEST_PATH" | sed 's/.*TST_//; s/[="} \t\/:`].*//'); do 500 case "$_tst_i" in 501 DISABLE_APPARMOR|DISABLE_SELINUX);; 502 SETUP|CLEANUP|TESTFUNC|ID|CNT|MIN_KVER);; 503 OPTS|USAGE|PARSE_ARGS|POS_ARGS);; 504 NEEDS_ROOT|NEEDS_TMPDIR|TMPDIR|NEEDS_DEVICE|DEVICE);; 505 NEEDS_CMDS|NEEDS_MODULE|MODPATH|DATAROOT);; 506 NEEDS_DRIVERS|FS_TYPE|MNTPOINT|MNT_PARAMS);; 507 IPV6|IPVER|TEST_DATA|TEST_DATA_IFS);; 508 RETRY_FUNC|RETRY_FN_EXP_BACKOFF|TIMEOUT);; 509 NET_DATAROOT|NET_MAX_PKT);; 510 *) tst_res TWARN "Reserved variable TST_$_tst_i used!";; 511 esac 512 done 513 514 for _tst_i in $(grep '^[^#]*\b_tst_' "$TST_TEST_PATH" | sed 's/.*_tst_//; s/[="} \t\/:`].*//'); do 515 tst_res TWARN "Private variable or function _tst_$_tst_i used!" 516 done 517 fi 518 519 OPTIND=1 520 521 while getopts ":hi:$TST_OPTS" _tst_name $TST_ARGS; do 522 case $_tst_name in 523 'h') tst_usage; exit 0;; 524 'i') TST_ITERATIONS=$OPTARG;; 525 '?') tst_usage; exit 2;; 526 *) $TST_PARSE_ARGS "$_tst_name" "$OPTARG";; 527 esac 528 done 529 530 if ! tst_is_int "$TST_ITERATIONS"; then 531 tst_brk TBROK "Expected number (-i) not '$TST_ITERATIONS'" 532 fi 533 534 if [ "$TST_ITERATIONS" -le 0 ]; then 535 tst_brk TBROK "Number of iterations (-i) must be > 0" 536 fi 537 538 [ "$TST_NEEDS_ROOT" = 1 ] && tst_require_root 539 540 [ "$TST_DISABLE_APPARMOR" = 1 ] && tst_disable_apparmor 541 [ "$TST_DISABLE_SELINUX" = 1 ] && tst_disable_selinux 542 543 tst_require_cmds $TST_NEEDS_CMDS 544 tst_require_drivers $TST_NEEDS_DRIVERS 545 546 if [ -n "$TST_MIN_KVER" ]; then 547 tst_kvcmp -lt "$TST_MIN_KVER" && \ 548 tst_brk TCONF "test requires kernel $TST_MIN_KVER+" 549 fi 550 551 _tst_setup_timer 552 553 [ "$TST_NEEDS_DEVICE" = 1 ] && TST_TMPDIR=1 554 555 if [ "$TST_NEEDS_TMPDIR" = 1 ]; then 556 if [ -z "$TMPDIR" ]; then 557 export TMPDIR="/tmp" 558 fi 559 560 TST_TMPDIR=$(mktemp -d "$TMPDIR/LTP_$TST_ID.XXXXXXXXXX") 561 562 chmod 777 "$TST_TMPDIR" 563 564 TST_STARTWD=$(pwd) 565 566 cd "$TST_TMPDIR" 567 fi 568 569 TST_MNTPOINT="${TST_MNTPOINT:-mntpoint}" 570 if [ "$TST_NEEDS_DEVICE" = 1 ]; then 571 572 TST_DEVICE=$(tst_device acquire) 573 574 if [ ! -b "$TST_DEVICE" -o $? -ne 0 ]; then 575 unset TST_DEVICE 576 tst_brk TBROK "Failed to acquire device" 577 fi 578 579 TST_DEVICE_FLAG=1 580 fi 581 582 [ -n "$TST_NEEDS_MODULE" ] && tst_require_module "$TST_NEEDS_MODULE" 583 584 if [ -n "$TST_SETUP" ]; then 585 $TST_SETUP 586 fi 587 588 #TODO check that test reports some results for each test function call 589 while [ $TST_ITERATIONS -gt 0 ]; do 590 if [ -n "$TST_TEST_DATA" ]; then 591 tst_require_cmds cut tr wc 592 _tst_max=$(( $(echo $TST_TEST_DATA | tr -cd "$TST_TEST_DATA_IFS" | wc -c) +1)) 593 for _tst_i in $(seq $_tst_max); do 594 _tst_data="$(echo "$TST_TEST_DATA" | cut -d"$TST_TEST_DATA_IFS" -f$_tst_i)" 595 _tst_run_tests "$_tst_data" 596 done 597 else 598 _tst_run_tests 599 fi 600 TST_ITERATIONS=$((TST_ITERATIONS-1)) 601 done 602 603 _tst_do_exit 604} 605 606_tst_run_tests() 607{ 608 local _tst_data="$1" 609 local _tst_i 610 611 for _tst_i in $(seq ${TST_CNT:-1}); do 612 if type ${TST_TESTFUNC}1 > /dev/null 2>&1; then 613 _tst_run_test "$TST_TESTFUNC$_tst_i" $_tst_i "$_tst_data" 614 else 615 _tst_run_test "$TST_TESTFUNC" $_tst_i "$_tst_data" 616 fi 617 done 618} 619 620_tst_run_test() 621{ 622 local _tst_res=$(_tst_resstr) 623 local _tst_fnc="$1" 624 shift 625 626 $_tst_fnc "$@" 627 _tst_rescmp "$_tst_res" 628 TST_COUNT=$((TST_COUNT+1)) 629} 630 631if [ -z "$TST_ID" ]; then 632 _tst_filename=$(basename $0) || \ 633 tst_brk TCONF "Failed to set TST_ID from \$0 ('$0'), fix it with setting TST_ID before sourcing tst_test.sh" 634 TST_ID=${_tst_filename%%.*} 635fi 636export TST_ID="$TST_ID" 637 638if [ -z "$LTPROOT" ]; then 639 export LTPROOT="$PWD" 640 export TST_DATAROOT="$LTPROOT/datafiles" 641else 642 export TST_DATAROOT="$LTPROOT/testcases/data/$TST_ID" 643fi 644 645if [ -z "$TST_NO_DEFAULT_RUN" ]; then 646 if TST_TEST_PATH=$(which $0) 2>/dev/null; then 647 if ! grep -q tst_run "$TST_TEST_PATH"; then 648 tst_brk TBROK "Test $0 must call tst_run!" 649 fi 650 fi 651 652 if [ -z "$TST_TESTFUNC" ]; then 653 tst_brk TBROK "TST_TESTFUNC is not defined" 654 fi 655 656 TST_TEST_DATA_IFS="${TST_TEST_DATA_IFS:- }" 657 658 if [ -n "$TST_CNT" ]; then 659 if ! tst_is_int "$TST_CNT"; then 660 tst_brk TBROK "TST_CNT must be integer" 661 fi 662 663 if [ "$TST_CNT" -le 0 ]; then 664 tst_brk TBROK "TST_CNT must be > 0" 665 fi 666 fi 667 668 if [ -n "$TST_POS_ARGS" ]; then 669 if ! tst_is_int "$TST_POS_ARGS"; then 670 tst_brk TBROK "TST_POS_ARGS must be integer" 671 fi 672 673 if [ "$TST_POS_ARGS" -le 0 ]; then 674 tst_brk TBROK "TST_POS_ARGS must be > 0" 675 fi 676 fi 677 678 TST_ARGS="$@" 679 680 while getopts ":hi:$TST_OPTS" tst_name; do 681 case $tst_name in 682 'h') TST_PRINT_HELP=1;; 683 *);; 684 esac 685 done 686 687 shift $((OPTIND - 1)) 688 689 if [ -n "$TST_POS_ARGS" ]; then 690 if [ -z "$TST_PRINT_HELP" -a $# -ne "$TST_POS_ARGS" ]; then 691 tst_brk TBROK "Invalid number of positional parameters:"\ 692 "have ($@) $#, expected ${TST_POS_ARGS}" 693 fi 694 else 695 if [ -z "$TST_PRINT_HELP" -a $# -ne 0 ]; then 696 tst_brk TBROK "Unexpected positional arguments '$@'" 697 fi 698 fi 699fi 700