]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - tests/functions.sh
libblkid: check status for the current CDROM slot
[thirdparty/util-linux.git] / tests / functions.sh
index 58db7475f63c934edb7e51678abe2a3ffb661434..0af8b96b77f9a7527baa95013d5224bbabd1b054 100644 (file)
@@ -75,9 +75,25 @@ function ts_report {
 }
 
 function ts_check_test_command {
-       if [ ! -x "$1" ]; then
-               ts_skip "${1##*/} not found"
-       fi
+       case "$1" in
+       */*)
+               # paths
+               if [ ! -x "$1" ]; then
+                       ts_skip "${1##*/} not found"
+               fi
+               ;;
+       *)
+               # just command names (e.g. --use-system-commands)
+               local cmd=$1
+               type "$cmd" >/dev/null 2>&1
+               if [ $? -ne 0 ]; then
+                       if [ "$TS_NOSKIP_COMMANDS" = "yes" ]; then
+                               ts_failed "missing in PATH: $cmd"
+                       fi
+                       ts_skip "missing in PATH: $cmd"
+               fi
+               ;;
+       esac
 }
 
 function ts_check_prog {
@@ -158,6 +174,17 @@ function ts_log {
        [ "$TS_VERBOSE" == "yes" ] && echo "$1"
 }
 
+function ts_logerr {
+       echo "$1" >> $TS_ERRLOG
+       [ "$TS_VERBOSE" == "yes" ] && echo "$1"
+}
+
+function ts_log_both {
+       echo "$1" >> $TS_OUTPUT
+       echo "$1" >> $TS_ERRLOG
+       [ "$TS_VERBOSE" == "yes" ] && echo "$1"
+}
+
 function ts_has_option {
        NAME="$1"
        ALL="$2"
@@ -201,24 +228,28 @@ function ts_init_core_env {
        TS_SUBNAME=""
        TS_NS="$TS_COMPONENT/$TS_TESTNAME"
        TS_OUTPUT="$TS_OUTDIR/$TS_TESTNAME"
+       TS_ERRLOG="$TS_OUTDIR/$TS_TESTNAME.err"
        TS_VGDUMP="$TS_OUTDIR/$TS_TESTNAME.vgdump"
        TS_DIFF="$TS_DIFFDIR/$TS_TESTNAME"
        TS_EXPECTED="$TS_TOPDIR/expected/$TS_NS"
+       TS_EXPECTED_ERR="$TS_TOPDIR/expected/$TS_NS.err"
        TS_MOUNTPOINT="$TS_OUTDIR/${TS_TESTNAME}-mnt"
 }
 
 function ts_init_core_subtest_env {
        TS_NS="$TS_COMPONENT/$TS_TESTNAME-$TS_SUBNAME"
        TS_OUTPUT="$TS_OUTDIR/$TS_TESTNAME-$TS_SUBNAME"
+       TS_ERRLOG="$TS_OUTDIR/$TS_TESTNAME-$TS_SUBNAME.err"
        TS_VGDUMP="$TS_OUTDIR/$TS_TESTNAME-$TS_SUBNAME.vgdump"
        TS_DIFF="$TS_DIFFDIR/$TS_TESTNAME-$TS_SUBNAME"
        TS_EXPECTED="$TS_TOPDIR/expected/$TS_NS"
+       TS_EXPECTED_ERR="$TS_TOPDIR/expected/$TS_NS.err"
        TS_MOUNTPOINT="$TS_OUTDIR/${TS_TESTNAME}-${TS_SUBNAME}-mnt"
 
-       rm -f $TS_OUTPUT $TS_VGDUMP
+       rm -f $TS_OUTPUT $TS_ERRLOG $TS_VGDUMP
        [ -d "$TS_OUTDIR" ]  || mkdir -p "$TS_OUTDIR"
 
-       touch $TS_OUTPUT
+       touch $TS_OUTPUT $TS_ERRLOG
        [ -n "$TS_VALGRIND_CMD" ] && touch $TS_VGDUMP
 }
 
@@ -230,8 +261,9 @@ function ts_init_env {
        LANGUAGE="POSIX"
        LC_ALL="POSIX"
        CHARSET="UTF-8"
+       ASAN_OPTIONS="detect_leaks=0"
 
-       export LANG LANGUAGE LC_ALL CHARSET
+       export LANG LANGUAGE LC_ALL CHARSET ASAN_OPTIONS
 
        mydir=$(ts_canonicalize "$mydir")
 
@@ -253,8 +285,20 @@ function ts_init_env {
        top_srcdir=$(ts_abspath $top_srcdir)
        top_builddir=$(ts_abspath $top_builddir)
 
-       # some ul commands search other ul commands in $PATH
-       export PATH="$top_builddir:$PATH"
+       # We use helpser always from build tree
+       ts_helpersdir="${top_builddir}/"
+
+       TS_USE_SYSTEM_COMMANDS=$(ts_has_option "use-system-commands" "$*")
+       if [ "$TS_USE_SYSTEM_COMMANDS" == "yes" ]; then
+               # Don't define anything, just follow current PATH
+               ts_commandsdir=""
+       else
+               # The default is to use commands from build tree
+               ts_commandsdir="${top_builddir}/"
+
+               # some ul commands search other ul commands in $PATH
+               export PATH="$ts_commandsdir:$PATH"
+       fi
 
        TS_SCRIPT="$mydir/$(basename $0)"
        TS_SUBDIR=$(dirname $TS_SCRIPT)
@@ -270,8 +314,15 @@ function ts_init_env {
        TS_OUTDIR="$top_builddir/tests/output/$TS_COMPONENT"
        TS_DIFFDIR="$top_builddir/tests/diff/$TS_COMPONENT"
 
+       TS_NOLOCKS=$(ts_has_option "nolocks" "$*")
+       TS_LOCKDIR="$top_builddir/tests/output"
+
+       # Don't lock if flock(1) is missing
+       type "flock" >/dev/null 2>&1 || TS_NOLOCKS="yes"
+
        ts_init_core_env
 
+       TS_NOSKIP_COMMANDS=$(ts_has_option "noskip-commands" "$*")
        TS_VERBOSE=$(ts_has_option "verbose" "$*")
        TS_SHOWDIFF=$(ts_has_option "show-diff" "$*")
        TS_PARALLEL=$(ts_has_option "parallel" "$*")
@@ -280,10 +331,14 @@ function ts_init_env {
        TS_PARSABLE=$(ts_has_option "parsable" "$*")
        [ "$TS_PARSABLE" = "yes" ] || TS_PARSABLE="$TS_PARALLEL"
 
-       tmp=$( ts_has_option "memcheck" "$*")
+       tmp=$( ts_has_option "memcheck-valgrind" "$*")
        if [ "$tmp" == "yes" -a -f /usr/bin/valgrind ]; then
                TS_VALGRIND_CMD="/usr/bin/valgrind"
        fi
+       tmp=$( ts_has_option "memcheck-asan" "$*")
+       if [ "$tmp" == "yes" ]; then
+               TS_ENABLE_ASAN="yes"
+       fi
 
        BLKID_FILE="$TS_OUTDIR/${TS_TESTNAME}.blkidtab"
 
@@ -291,6 +346,7 @@ function ts_init_env {
        declare -a TS_SUID_USER
        declare -a TS_SUID_GROUP
        declare -a TS_LOOP_DEVS
+       declare -a TS_LOCKFILE_FD
 
        if [ -f $TS_TOPDIR/commands.sh ]; then
                . $TS_TOPDIR/commands.sh
@@ -298,15 +354,17 @@ function ts_init_env {
 
        export BLKID_FILE
 
-       rm -f $TS_OUTPUT $TS_VGDUMP
+       rm -f $TS_OUTPUT $TS_ERRLOG $TS_VGDUMP
        [ -d "$TS_OUTDIR" ]  || mkdir -p "$TS_OUTDIR"
 
-       touch $TS_OUTPUT
+       touch $TS_OUTPUT $TS_ERRLOG
        [ -n "$TS_VALGRIND_CMD" ] && touch $TS_VGDUMP
 
        if [ "$TS_VERBOSE" == "yes" ]; then
                echo
                echo "     script: $TS_SCRIPT"
+               echo "   commands: $ts_commandsdir"
+               echo "    helpers: $ts_helpersdir"
                echo "    sub dir: $TS_SUBDIR"
                echo "    top dir: $TS_TOPDIR"
                echo "       self: $TS_SELF"
@@ -316,8 +374,9 @@ function ts_init_env {
                echo "  namespace: $TS_NS"
                echo "    verbose: $TS_VERBOSE"
                echo "     output: $TS_OUTPUT"
+               echo "  error log: $TS_ERRLOG"
                echo "   valgrind: $TS_VGDUMP"
-               echo "   expected: $TS_EXPECTED"
+               echo "   expected: $TS_EXPECTED{.err}"
                echo " mountpoint: $TS_MOUNTPOINT"
                echo
        fi
@@ -377,49 +436,87 @@ function ts_init_py {
 }
 
 function ts_run {
-       if [ -z "$TS_VALGRIND_CMD" ]; then
-               "$@"
-       else
-               $TS_VALGRIND_CMD --tool=memcheck --leak-check=full \
-                                --leak-resolution=high --num-callers=20 \
-                                --log-file="$TS_VGDUMP" "$@"
+       declare -a args
+
+       #
+       # ASAN mode
+       #
+       if [ "$TS_ENABLE_ASAN" == "yes" ]; then
+               args+=(env ASAN_OPTIONS=detect_leaks=1)
        fi
-}
 
-function ts_gen_diff {
-       local res=0
+       #
+       # valgrind mode
+       #
+       if [ -n "$TS_VALGRIND_CMD" ]; then
+               args+=(libtool --mode=execute "$TS_VALGRIND_CMD" --tool=memcheck --leak-check=full)
+               args+=(--leak-resolution=high --num-callers=20 --log-file="$TS_VGDUMP")
+       fi
 
-       [ -f "$TS_OUTPUT" ] || return 1
-       [ -f "$TS_EXPECTED" ] || TS_EXPECTED=/dev/null
+       "${args[@]}" "$@"
+}
 
-       # remove libtool lt- prefixes
-       sed --in-place 's/^lt\-\(.*\: \)/\1/g' $TS_OUTPUT
+function ts_gen_diff_from {
+       local res=0
+       local expected="$1"
+       local output="$2"
+       local difffile="$3"
 
-       [ -d "$TS_DIFFDIR" ] || mkdir -p "$TS_DIFFDIR"
-       diff -u $TS_EXPECTED $TS_OUTPUT > $TS_DIFF
+       diff -u $expected $output > $difffile
 
-       if [ $? -ne 0 ] || [ -s $TS_DIFF ]; then
+       if [ $? -ne 0 ] || [ -s $difffile ]; then
                res=1
-               if [ "$TS_SHOWDIFF" == "yes" ]; then
+               if [ "$TS_SHOWDIFF" == "yes" -a "$TS_KNOWN_FAIL" != "yes" ]; then
                        echo
                        echo "diff-{{{"
-                       cat $TS_DIFF
+                       cat $difffile
                        echo "}}}-diff"
                        echo
                fi
        else
-               rm -f $TS_DIFF;
+               rm -f $difffile;
        fi
 
        return $res
 }
 
-function tt_gen_mem_report {
-       [ -z "$TS_VALGRIND_CMD" ] && echo "$1"
+function ts_gen_diff {
+       local status_out=0
+       local status_err=0
+
+       [ -f "$TS_OUTPUT" ] || return 1
+       [ -f "$TS_EXPECTED" ] || TS_EXPECTED=/dev/null
+
+       # remove libtool lt- prefixes
+       sed --in-place 's/^lt\-\(.*\: \)/\1/g' $TS_OUTPUT
+       sed --in-place 's/^lt\-\(.*\: \)/\1/g' $TS_ERRLOG
+
+       [ -d "$TS_DIFFDIR" ] || mkdir -p "$TS_DIFFDIR"
+
+       ts_gen_diff_from $TS_EXPECTED $TS_OUTPUT $TS_DIFF
+       status_out=$?
 
-       grep -q -E 'ERROR SUMMARY: [1-9]' $TS_VGDUMP &> /dev/null
-       if [ $? -eq 0 ]; then
-               echo "mem-error detected!"
+       # error log is fully optional
+       [ -f "$TS_EXPECTED_ERR" ] || TS_EXPECTED_ERR=/dev/null
+       [ -f "$TS_ERRLOG" ] || TS_ERRLOG=/dev/null
+
+       ts_gen_diff_from $TS_EXPECTED_ERR $TS_ERRLOG $TS_DIFF.err
+       status_err=$?
+
+       if [ $status_out -ne 0 -o $status_err -ne 0 ]; then
+               return 1
+       fi
+       return 0
+}
+
+function tt_gen_mem_report {
+       if [ -n "$TS_VALGRIND_CMD" ]; then
+               grep -q -E 'ERROR SUMMARY: [1-9]' $TS_VGDUMP &> /dev/null
+               if [ $? -eq 0 ]; then
+                       echo "mem-error detected!"
+               fi
+       else
+               echo "$1"
        fi
 }
 
@@ -529,16 +626,34 @@ function ts_device_deinit {
        fi
 }
 
+function ts_blkidtag_by_devname()
+{
+       local tag=$1
+       local dev=$2
+       local out
+       local rval
+
+       out=$($TS_CMD_BLKID -p -s "$tag" -o value "$dev")
+       rval=$?
+       printf "%s\n" "$out"
+
+       test -n "$out" -a "$rval" = "0"
+       return $?
+}
+
 function ts_uuid_by_devname {
-       echo $($TS_CMD_BLKID -p -s UUID -o value $1)
+       ts_blkidtag_by_devname "UUID" "$1"
+       return $?
 }
 
 function ts_label_by_devname {
-       echo $($TS_CMD_BLKID -p -s LABEL -o value $1)
+       ts_blkidtag_by_devname "LABEL" "$1"
+       return $?
 }
 
 function ts_fstype_by_devname {
-       echo $($TS_CMD_BLKID -p -s TYPE -o value $1)
+       ts_blkidtag_by_devname "TYPE" "$1"
+       return $?
 }
 
 function ts_device_has {
@@ -547,24 +662,24 @@ function ts_device_has {
        local DEV="$3"
        local vl=""
 
-       case $TAG in
-               "TYPE") vl=$(ts_fstype_by_devname $DEV);;
-               "LABEL") vl=$(ts_label_by_devname $DEV);;
-               "UUID") vl=$(ts_uuid_by_devname $DEV);;
-               *) return 1;;
-       esac
-
-       if [ "$vl" == "$VAL" ]; then
-               return 0
-       fi
-       return 1
+       vl=$(ts_blkidtag_by_devname "$TAG" "$DEV")
+       test $? = 0 -a "$vl" = "$VAL"
+       return $?
 }
 
-function ts_device_has_uuid {
-       ts_uuid_by_devname "$1" | egrep -q '^[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}$'
+function ts_is_uuid()
+{
+       printf "%s\n" "$1" | egrep -q '^[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}$'
        return $?
 }
 
+function ts_udevadm_settle()
+{
+       local dev=$1 # optional, might be empty
+       shift        # all other args are tags, LABEL, UUID, ...
+       udevadm settle
+}
+
 function ts_mount {
        local out
        local result
@@ -592,10 +707,10 @@ function ts_mount {
 function ts_is_mounted {
        local DEV=$(ts_canonicalize "$1")
 
-       grep -q $DEV /proc/mounts && return 0
+       grep -q "\(^\| \)$DEV " /proc/mounts && return 0
 
        if [ "${DEV#/dev/loop/}" != "$DEV" ]; then
-               grep -q "/dev/loop${DEV#/dev/loop/}" /proc/mounts && return 0
+               grep -q "^/dev/loop${DEV#/dev/loop/} " /proc/mounts && return 0
        fi
        return 1
 }
@@ -606,6 +721,7 @@ function ts_fstab_open {
 
 function ts_fstab_close {
        echo "# -->" >> /etc/fstab
+       sync /etc/fstab 2>/dev/null
 }
 
 function ts_fstab_addline {
@@ -617,13 +733,19 @@ function ts_fstab_addline {
        echo "$SPEC   $MNT   $FS   $OPT   0   0" >> /etc/fstab
 }
 
+function ts_fstab_lock {
+       ts_lock "fstab"
+}
+
 function ts_fstab_add {
+       ts_fstab_lock
        ts_fstab_open
        ts_fstab_addline $*
        ts_fstab_close
 }
 
 function ts_fstab_clean {
+       ts_have_lock "fstab" || return 0
        sed --in-place "
 /# <!-- util-linux/!b
 :a
@@ -633,6 +755,9 @@ function ts_fstab_clean {
 }
 s/# <!-- util-linux.*-->//;
 /^$/d" /etc/fstab
+
+       sync /etc/fstab 2>/dev/null
+       ts_unlock "fstab"
 }
 
 function ts_fdisk_clean {
@@ -640,7 +765,7 @@ function ts_fdisk_clean {
 
        # remove non comparable parts of fdisk output
        if [ -n "${DEVNAME}" ]; then
-               sed -i -e "s@${DEVNAME}@<removed>@;" $TS_OUTPUT
+               sed -i -e "s@${DEVNAME}@<removed>@;" $TS_OUTPUT $TS_ERRLOG
        fi
 
        sed -i \
@@ -651,7 +776,89 @@ function ts_fdisk_clean {
                -e 's/Welcome to fdisk.*/Welcome to fdisk <removed>./' \
                -e 's/typescript file.*/typescript file <removed>./' \
                -e 's@^\(I/O size (minimum/op.* bytes /\) [1-9][0-9]* @\1 <removed> @' \
-               $TS_OUTPUT
+               $TS_OUTPUT $TS_ERRLOG
+}
+
+
+# https://stackoverflow.com/questions/41603787/how-to-find-next-available-file-descriptor-in-bash
+function ts_find_free_fd()
+{
+       local rco
+       local rci
+       for fd in {3..200}; do
+               rco="$(true 2>/dev/null >&${fd}; echo $?)"
+               rci="$(true 2>/dev/null <&${fd}; echo $?)"
+               if [[ "${rco}${rci}" = "11" ]]; then
+                       echo "$fd"
+                       return 0
+               fi
+       done
+       return 1
+}
+
+function ts_get_lock_fd {
+       local resource=$1
+       local fd
+
+       for fd in "${!TS_LOCKFILE_FD[@]}"; do
+               if [ "${TS_LOCKFILE_FD["$fd"]}" = "$resource" ]; then
+                       echo "$fd"
+                       return 0
+               fi
+       done
+       return 1
+}
+
+function ts_have_lock {
+       local resource=$1
+
+       test "$TS_NOLOCKS" = "yes" && return 0
+       ts_get_lock_fd "$resource" >/dev/null && return 0
+       return 1
+}
+
+function ts_lock {
+       local resource="$1"
+       local lockfile="${TS_LOCKDIR}/${resource}.lock"
+       local fd
+
+       if [ "$TS_NOLOCKS" == "yes" ]; then
+               return 0
+       fi
+
+       # Don't lock again
+       fd=$(ts_get_lock_fd "$resource")
+       if [ -n "$fd" ]; then
+               echo "[$$ $TS_TESTNAME] ${resource} already locked!"
+               return 0
+       fi
+
+       fd=$(ts_find_free_fd) || ts_skip "failed to find lock fd"
+
+       eval "exec $fd>$lockfile"
+       flock --exclusive "$fd" || ts_skip "failed to lock $resource"
+
+       TS_LOCKFILE_FD["$fd"]="$resource"
+       ###echo "[$$ $TS_TESTNAME] Locked   $resource"
+}
+
+function ts_unlock {
+       local resource="$1"
+       local lockfile="${TS_LOCKDIR}/${resource}.lock"
+       local fd
+
+       if [ "$TS_NOLOCKS" == "yes" ]; then
+               return 0
+       fi
+
+       fd=$(ts_get_lock_fd "$resource")
+       if [ -n "$fd" ]; then
+               eval "exec $fd<&-"
+               TS_LOCKFILE_FD["$fd"]=""
+               ###echo "[$$ $TS_TESTNAME] Unlocked $resource"
+       else
+               echo "[$$ $TS_TESTNAME] unlocking unlocked $resource!?"
+       fi
 }
 
 function ts_scsi_debug_init {
@@ -659,14 +866,19 @@ function ts_scsi_debug_init {
        local t
        TS_DEVICE="none"
 
+       ts_lock "scsi_debug"
+
        # dry run is not really reliable, real modprobe may still fail
        modprobe --dry-run --quiet scsi_debug &>/dev/null \
                || ts_skip "missing scsi_debug module (dry-run)"
 
        # skip if still in use or removal of modules not supported at all
        # We don't want a slow timeout here so we don't use ts_scsi_debug_rmmod!
-       modprobe -r scsi_debug &>/dev/null \
-               || ts_skip "cannot remove scsi_debug module (rmmod)"
+       modprobe -r scsi_debug &>/dev/null
+       if [ "$?" -eq 1 ]; then
+               ts_unlock "scsi_debug"
+               ts_skip "cannot remove scsi_debug module (rmmod)"
+       fi
 
        modprobe -b scsi_debug "$@" &>/dev/null \
                || ts_skip "cannot load scsi_debug module (modprobe)"
@@ -697,6 +909,9 @@ function ts_scsi_debug_rmmod {
        local t
        local lastmsg
 
+       # We must not run if we don't have the lock
+       ts_have_lock "scsi_debug" || return 0
+
        # Return early most importantly in case we are not root or the module does
        # not exist at all.
        [ $UID -eq 0 ] || return 0
@@ -727,6 +942,7 @@ function ts_scsi_debug_rmmod {
 
        # TODO unset TS_DEVICE, check that nobody uses it later, e.g. ts_fdisk_clean
 
+       ts_unlock "scsi_debug"
        return 0
 }
 
@@ -800,4 +1016,3 @@ function ts_has_ncurses_support {
                echo "no"
        fi
 }
-