#!/bin/sh # # Copyright (c) Linux Test Project, 2014-2017 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Written by Cyril Hrubis # # This is a LTP test library for shell. # export LTP_RET_VAL=0 export TST_COUNT=1 export TST_PASS_COUNT=0 export TST_LIB_LOADED=1 export TST_TMPDIR_RHOST=0 . tst_ansi_color.sh # Exit values map tst_flag2mask() { case "$1" in TPASS) return 0;; TFAIL) return 1;; TBROK) return 2;; TWARN) return 4;; TINFO) return 16;; TCONF) return 32;; *) tst_brkm TBROK "Invalid resm type '$1'";; esac } tst_resm() { local ttype="$1" tst_flag2mask "$ttype" local mask=$? LTP_RET_VAL=$((LTP_RET_VAL|mask)) local ret=$1 shift printf "$TCID $TST_COUNT " tst_print_colored $ret "$ret:" echo " $@" case "$ret" in TPASS|TFAIL|TCONF) TST_COUNT=$((TST_COUNT+1));; esac if [ "$ret" = TPASS ]; then TST_PASS_COUNT=$((TST_PASS_COUNT+1)) fi } tst_brkm() { case "$1" in TFAIL) ;; TBROK) ;; TCONF) ;; *) tst_brkm TBROK "Invalid tst_brkm type '$1'";; esac local ret=$1 shift tst_resm "$ret" "$@" tst_exit } tst_record_childstatus() { if [ $# -ne 1 ]; then tst_brkm TBROK "Requires child pid as parameter" fi local child_pid=$1 local ret=0 wait $child_pid ret=$? if [ $ret -eq 127 ]; then tst_brkm TBROK "Child process pid='$child_pid' does not exist" fi LTP_RET_VAL=$((LTP_RET_VAL|ret)) } tst_require_root() { if [ "$(id -ru)" != 0 ]; then tst_brkm TCONF "Must be super/root for this test!" fi } tst_exit() { if [ -n "${TST_CLEANUP:-}" -a -z "${TST_NO_CLEANUP:-}" ]; then $TST_CLEANUP fi if [ -n "${LTP_IPC_PATH:-}" -a -f "${LTP_IPC_PATH:-}" ]; then rm -f "$LTP_IPC_PATH" fi # Mask out TCONF if no TFAIL/TBROK/TWARN but has TPASS if [ $((LTP_RET_VAL & 7)) -eq 0 -a $TST_PASS_COUNT -gt 0 ]; then LTP_RET_VAL=$((LTP_RET_VAL & ~32)) fi # Mask out TINFO exit $((LTP_RET_VAL & ~16)) } tst_tmpdir() { if [ -z "$TMPDIR" ]; then export TMPDIR="/tmp" fi TST_TMPDIR=$(mktemp -d "$TMPDIR/$TCID.XXXXXXXXXX") chmod 777 "$TST_TMPDIR" TST_STARTWD=$(pwd) cd "$TST_TMPDIR" } tst_rmdir() { if [ -n "$TST_TMPDIR" ]; then cd "$LTPROOT" rm -r "$TST_TMPDIR" [ "$TST_TMPDIR_RHOST" = 1 ] && tst_cleanup_rhost fi } # # Checks if commands passed as arguments exists # tst_require_cmds() { local cmd for cmd in $*; do if ! command -v $cmd > /dev/null 2>&1; then tst_brkm TCONF "'$cmd' not found" fi done } # tst_retry "command" [times] # try run command for specified times, default is 3. # Function returns 0 if succeed in RETRIES times or the last retcode the cmd # returned tst_retry() { local cmd="$1" local RETRIES=${2:-"3"} local i=$RETRIES while [ $i -gt 0 ]; do eval "$cmd" ret=$? if [ $ret -eq 0 ]; then break fi i=$((i-1)) sleep 1 done if [ $ret -ne 0 ]; then tst_resm TINFO "Failed to execute '$cmd' after $RETRIES retries" fi return $ret } # tst_timeout "command arg1 arg2 ..." timeout # Runs command for specified timeout (in seconds). # Function returns retcode of command or 1 if arguments are invalid. tst_timeout() { local command=$1 local timeout=$(echo $2 | grep -o "^[0-9]\+$") # command must be non-empty string with command to run if [ -z "$command" ]; then echo "first argument must be non-empty string" return 1 fi # accept only numbers as timeout if [ -z "$timeout" ]; then echo "only numbers as second argument" return 1 fi setsid sh -c "eval $command" 2>&1 & local pid=$! while [ $timeout -gt 0 ]; do kill -s 0 $pid 2>/dev/null if [ $? -ne 0 ]; then break fi timeout=$((timeout - 1)) sleep 1 done local ret=0 if [ $timeout -le 0 ]; then ret=128 kill -TERM -- -$pid fi wait $pid ret=$((ret | $?)) return $ret } ROD_SILENT() { local tst_out="$($@ 2>&1)" if [ $? -ne 0 ]; then echo "$tst_out" tst_brkm TBROK "$@ failed" fi } ROD_BASE() { local cmd local arg local file local flag for arg; do file="${arg#\>}" if [ "$file" != "$arg" ]; then flag=1 if [ -n "$file" ]; then break fi continue fi if [ -n "$flag" ]; then file="$arg" break fi cmd="$cmd $arg" done if [ -n "$flag" ]; then $cmd > $file else $@ fi } ROD() { ROD_BASE "$@" if [ $? -ne 0 ]; then tst_brkm TBROK "$@ failed" fi } EXPECT_PASS() { ROD_BASE "$@" if [ $? -eq 0 ]; then tst_resm TPASS "$@ passed as expected" else tst_resm TFAIL "$@ failed unexpectedly" fi } EXPECT_FAIL() { # redirect stderr since we expect the command to fail ROD_BASE "$@" 2> /dev/null if [ $? -ne 0 ]; then tst_resm TPASS "$@ failed as expected" else tst_resm TFAIL "$@ passed unexpectedly" fi } tst_mkfs() { local fs_type=$1 local device=$2 shift 2 local fs_opts="$@" if [ -z "$fs_type" ]; then tst_brkm TBROK "No fs_type specified" fi if [ -z "$device" ]; then tst_brkm TBROK "No device specified" fi tst_resm TINFO "Formatting $device with $fs_type extra opts='$fs_opts'" ROD_SILENT mkfs.$fs_type $fs_opts $device } tst_umount() { local device="$1" local i=0 if ! grep -q "$device" /proc/mounts; then tst_resm TINFO "The $device is not mounted, skipping umount" return fi while [ "$i" -lt 50 ]; do if umount "$device" > /dev/null; then return fi i=$((i+1)) tst_resm TINFO "umount($device) failed, try $i ..." tst_resm TINFO "Likely gvfsd-trash is probing newly mounted "\ "fs, kill it to speed up tests." tst_sleep 100ms done tst_resm TWARN "Failed to umount($device) after 50 retries" } # Check a module file existence # Should be called after tst_tmpdir() tst_module_exists() { local mod_name="$1" if [ -f "$mod_name" ]; then TST_MODPATH="$mod_name" return fi local mod_path="$LTPROOT/testcases/bin/$mod_name" if [ -f "$mod_path" ]; then TST_MODPATH="$mod_path" return fi if [ -n "$TST_TMPDIR" ]; then mod_path="$TST_STARTWD/$mod_name" if [ -f "$mod_path" ]; then TST_MODPATH="$mod_path" return fi fi tst_brkm TCONF "Failed to find module '$mod_name'" } TST_CHECKPOINT_WAIT() { ROD tst_checkpoint wait 10000 "$1" } TST_CHECKPOINT_WAKE() { ROD tst_checkpoint wake 10000 "$1" 1 } TST_CHECKPOINT_WAKE2() { ROD tst_checkpoint wake 10000 "$1" "$2" } TST_CHECKPOINT_WAKE_AND_WAIT() { TST_CHECKPOINT_WAKE "$1" TST_CHECKPOINT_WAIT "$1" } # Check that test name is set if [ -z "$TCID" ]; then tst_brkm TBROK "TCID is not defined" fi if [ -z "$TST_TOTAL" ]; then tst_brkm TBROK "TST_TOTAL is not defined" fi export TCID="$TCID" export TST_TOTAL="$TST_TOTAL" # Setup LTPROOT, default to current directory if not set if [ -z "$LTPROOT" ]; then export LTPROOT="$PWD" export LTP_DATAROOT="$LTPROOT/datafiles" else export LTP_DATAROOT="$LTPROOT/testcases/data/$TCID" fi if [ "$TST_NEEDS_CHECKPOINTS" = "1" ]; then LTP_IPC_PATH="/dev/shm/ltp_${TCID}_$$" LTP_IPC_SIZE=$(tst_getconf PAGESIZE) if [ $? -ne 0 ]; then tst_brkm TBROK "tst_getconf PAGESIZE failed" fi ROD_SILENT dd if=/dev/zero of="$LTP_IPC_PATH" bs="$LTP_IPC_SIZE" count=1 ROD_SILENT chmod 600 "$LTP_IPC_PATH" export LTP_IPC_PATH fi