# clang-format off
checkpoint_time="$(date +%s%N)"

M0=${M0:=/mnt/glusterfs/0};   # 0th mount point for FUSE
M1=${M1:=/mnt/glusterfs/1};   # 1st mount point for FUSE
M2=${M2:=/mnt/glusterfs/2};   # 2nd mount point for FUSE
M3=${M3:=/mnt/glusterfs/3};   # 3rd mount point for FUSE
N0=${N0:=/mnt/nfs/0};         # 0th mount point for NFS
N1=${N1:=/mnt/nfs/1};         # 1st mount point for NFS
V0=${V0:=patchy};             # volume name to use in tests
V1=${V1:=patchy1};            # volume name to use in tests
GMV0=${GMV0:=primary};	      # primary volume name to use in geo-rep tests
GSV0=${GSV0:=secondary};      # secondary volume name to use in geo-rep tests
GSV1=${GSV1:=secondary1}      # secondary volume name to use in geo-rep tests
B0=${B0:=/d/backends};        # top level of brick directories
DEVDIR=${DEVDIR:=/d/dev} # directory for loop device management
WORKDIRS="$B0 $M0 $M1 $M2 $M3 $N0 $N1 $DEVDIR"

ROOT_GFID="00000000-0000-0000-0000-000000000001"
DOT_SHARD_GFID="be318638-e8a0-4c6d-977d-7a937aa84806"

META_VOL=${META_VOL:=gluster_shared_storage}; # shared gluster storage volume used by snapshot scheduler, nfs ganesha and geo-rep.
META_MNT=${META_MNT:=/var/run/gluster/shared_storage}; # Mount point of shared gluster volume.

CC=cc
OSTYPE=$(uname -s)
GFREG_ID="$(hostname -s)"

env_dir=$(dirname $0)
while true; do
        ENV_RC=${env_dir}/env.rc
        if [ -f ${ENV_RC} ]; then
                break
        fi
        new_dir=$(dirname $env_dir)
        if [ x"$new_dir" = x"$old_dir" ]; then
                ENV_RC="/not/found"
                break
        fi
        old_dir=$env_dir
        env_dir=$new_dir
done

if [ ! -f $ENV_RC ]; then
   echo "Aborting." | tee /dev/stderr
   echo | tee /dev/stderr
   echo "env.rc not found" | tee /dev/stderr
   echo | tee /dev/stderr
   echo "Please correct the problem and try again." | tee /dev/stderr
   echo | tee /dev/stderr
   exit 1
fi
. $ENV_RC

H0=${H0:=`ip -o -4 addr | grep -v "\<lo\>" | awk '{print $4}' | cut -d/ -f1 | head -n 1`}; # hostname
MOUNT_TYPE_FUSE="fuse.glusterfs"
GREP_MOUNT_OPT_RO="grep (ro"
GREP_MOUNT_OPT_RW="grep (rw"
UMOUNT_F="umount -f"

PATH=$PATH:${PWD}/tests/utils

case $OSTYPE in
Linux)
  H0=${H0:=`hostname --fqdn`}; # hostname
  ;;
NetBSD)
  MOUNT_TYPE_FUSE="puffs|perfuse|fuse.glusterfs"
  GREP_MOUNT_OPT_RO="grep (read-only"
  GREP_MOUNT_OPT_RW="grep -v (read-only"
  UMOUNT_F="umount -f -R"
  ;;
*)
  ;;
esac

DEBUG=${DEBUG:=0}             # turn on debugging?

MIGRATION_START_TIMEOUT=5
PROCESS_DOWN_TIMEOUT=5
PROCESS_UP_TIMEOUT=45
NFS_EXPORT_TIMEOUT=20
CHILD_UP_TIMEOUT=20
PROBE_TIMEOUT=60
PEER_SYNC_TIMEOUT=20
REBALANCE_TIMEOUT=600
REOPEN_TIMEOUT=20
HEAL_TIMEOUT=80
IO_HEAL_TIMEOUT=120
MARKER_UPDATE_TIMEOUT=20
JANITOR_TIMEOUT=60
UMOUNT_TIMEOUT=5
CONFIG_UPDATE_TIMEOUT=5
AUTH_REFRESH_INTERVAL=10
GRAPH_SWITCH_TIMEOUT=10
UNLINK_TIMEOUT=5
MDC_TIMEOUT=5
IO_WAIT_TIMEOUT=5
DISK_FAIL_TIMEOUT=80

LOGDIR=$(gluster --print-logdir)

statedumpdir=`gluster --print-statedumpdir`; # Default directory for statedump

CLI="gluster --mode=script --wignore";
CLI_NO_FORCE="gluster --mode=script";

# CLI_IGNORE_PARTITION makes sure that the warning related to bricks being on
# root partition is ignored while running the command in a "no force" mode
CLI_IGNORE_PARTITION="gluster --mode=script --wignore-partition"

function wait_delay() {
        local delay="$1"
        local interval="$2"
        shift 2
        local deadline="$(($(date +%s%N) + ${delay}000000000))"

        $*
        while [[ $? -ne 0 ]]; do
                if [[ $(date +%s%N) -ge ${deadline} ]]; then
                        return 1
                fi
                sleep ${interval}
                $*
        done

        return 0
}

_GFS () {
	glusterfs "$@"
	local mount_ret=$?
	if [ $mount_ret != 0 ]; then
		return $mount_ret
	fi
	local mount_point=${!#}
	local i=0
	while true; do
		touch $mount_point/xy_zzy 2> /dev/null && break
		i=$((i+1))
		[ $i -lt 100 ] || break
		sleep 0.1
	done
	rm -f $mount_point/xy_zzy
	return $mount_ret
}
GFS="_GFS --attribute-timeout=0 --entry-timeout=0";

mkdir -p $WORKDIRS

case $OSTYPE in
FreeBSD | Darwin)
wc () {
   if test "x$1" = "x-l"; then
      awk '{ lines++ } END {print lines}'
   fi
   if test "x$1" = "x-w"; then
      awk '{ words += NF } END {print words}' }
   fi
   if test "x$1" = "x-c"; then
      awk '{ chars += length($0) + 1 } END {print chars}'
   fi
   if test "x$1" = "x-m"; then
      awk '{ chars += length($0) + 1 } END {print chars}'
   fi
}
;;
NetBSD)
wc() {
    /usr/bin/wc $@ | sed 's/^ *\([0-9]*\).*$/\1/g'
}
;;
esac

testcnt=`egrep '^[[:space:]]*(EXPECT|EXPECT_NOT|TEST|TEST_WITHIN|EXPECT_WITHIN|EXPECT_KEYWORD)[[:space:]]' $0 | wc -l`
expect_tests=`egrep '^[[:space:]]*TESTS_EXPECTED_IN_LOOP[[:space:]]*' $0`

x_ifs=$IFS
IFS=$'\n'
for line in $expect_tests; do
        expect_tests=`echo $line | cut -f 2 -d =`
        testcnt=`expr $testcnt + $expect_tests`
done
IFS=$x_ifs

echo "1..`echo $testcnt`"

t=1

function dbg()
{
        [ "x$DEBUG" = "x0" ] || echo "$*" >&2;
}

function G_LOG()
{
     local g_log_logdir;
     g_log_logdir=`$CLI --print-logdir`
     test -d $g_log_logdir
     if [ $? != 0 ]; then
        return
     fi
     local g_log_string;
     g_log_string="++++++++++ G_LOG:$0: TEST: $@ ++++++++++"
     g_log_string="`date -u +["%F %T.%6N"]`:$g_log_string"
     local g_log_filename
     for  g_log_filename in `find $g_log_logdir/ -type f -name \*.log`;
        do
                echo "$g_log_string" >> "$g_log_filename"
        done
}

function test_header()
{
        dbg "=========================";
        dbg "TEST $t (line $TESTLINE): $*";
        saved_cmd="$*"
        start_time="$(date +%s%N)"
}


function test_footer()
{
        RET=$?
        local lineno=$1
        local err=$2
        local end_time
        local elapsed1
        local elapsed2

        end_time="$(date +%s%N)"
        elapsed1="$(((start_time - checkpoint_time) / 1000000))"
        elapsed2="$(((end_time - start_time) / 1000000))"
        checkpoint_time="$end_time"
        if [ $RET -eq 0 ]; then
                printf "ok %3d [%7d/%7d] <%4d> '%s'\n" "$t" "$elapsed1" "$elapsed2" "$lineno" "$saved_cmd";
        else
                printf "not ok %3d [%7d/%7d] <%4d> '%s' -> '%s'\n" "$t" "$elapsed1" "$elapsed2" "$lineno" "$saved_cmd" "$err"
                if [ "$EXIT_EARLY" = "1" ]; then
			cleanup
                        exit $RET
                fi
        fi

        dbg "RESULT $t: $RET";

        t=`expr $t + 1`;
}

function test_expect_footer()
{
        local lineno=$1
        local e=$2
        local a=$3
        local err=""

        if ! [[ "$a" =~ $e ]]; then
                err="Got \"$a\" instead of \"$e\""
        fi
        [[ "$a" =~ $e ]];

        test_footer "$lineno" "$err";
}

function _EXPECT()
{
        TESTLINE=$1;
        shift;
        local a=""

        G_LOG $TESTLINE "$@";
        test_header "$@";

        e="$1";
        shift;
        a=$("$@" | tail -1)

        if [ "x$e" = "x" ] ; then
                test_expect_footer "$TESTLINE" "x$e" "x$a";
        else
                test_expect_footer "$TESTLINE" "$e" "$a";
        fi
}

function test_expect_not_footer()
{
        local lineno=$1
        local e=$2
        local a=$3
        local err=""

        if [[ "$a" =~ $e ]]; then
                err="Got \"$a\" when not expecting it"
        fi

        ! [[ "$a" =~ $e ]];
        test_footer "$lineno" "$err";
}

function _EXPECT_NOT()
{
        TESTLINE=$1;
        shift;
        local a=""

        G_LOG $TESTLINE "$@";
        test_header "$@";

        e="$1";
        shift;
        a=$("$@" | tail -1)

        if [ "x$e" = "x" ] ; then
               test_expect_not_footer "$TESTLINE" "x$e" "x$a";
        else
               test_expect_not_footer "$TESTLINE" "$e" "$a";
        fi
}

function _EXPECT_KEYWORD()
{
        TESTLINE=$1;
        shift;
        G_LOG $TESTLINE "$@";
        test_header "$@";

        e="$1";
        shift;
        "$@" | tail -1 | grep -q "$e"

        test_footer "$TESTLINE";
}

function _TEST()
{
        TESTLINE=$1;
        shift;
        local redirect=""

        G_LOG $TESTLINE "$@";
        test_header "$@";

        if [ "$1" = "!" ]; then
                redirect="2>&1"
        fi

        eval "$@" >/dev/null $redirect

        test_footer "$TESTLINE";
}


function _TEST_WITHIN()
{
        local res output

        TESTLINE=$1
        shift;

        local timeout=$1
        shift;

        G_LOG $TESTLINE "$@";
        test_header "$@"

        local endtime="$(( ${timeout}000000000 + $(date +%s%N) ))"

        # We *want* this to be globally visible.
        EW_RETRIES=0

        output=""
        res="1"
        while [[ "$(date +%s%N)" < "$endtime" ]]; do
                output="$("${@}" 2>/dev/null)"
                res="${?}"
                if [[ "${res}" == "0" ]]; then
                        break
                fi
                sleep 0.25;
                EW_RETRIES=$((EW_RETRIES+1))
        done

        ! [[ "${res}" != "0" ]]

        test_footer "$TESTLINE"

        TEST_OUTPUT="${output}"
}

#This function should be used carefully.
#The expected regex, given to this function, should be
#used within ^ and $ to match exactly with the output of
#command.
function _EXPECT_WITHIN()
{
        TESTLINE=$1
        shift;

        local timeout=$1
        shift;

        G_LOG $TESTLINE "$@";
        test_header "$@"

        e=$1;
        a="";
        shift;

        local endtime="$(( ${timeout}000000000 + $(date +%s%N) ))"

        # We *want* this to be globally visible.
        EW_RETRIES=0

        while [[ "$(date +%s%N)" < "$endtime" ]]; do
                a=$("$@" | tail -1 ; exit ${PIPESTATUS[0]})
                ## Check command success
                if [ $? -ne 0 ]; then
                        break;
                fi
                ## Check match success
                if [[ "$a" =~ $e ]]; then
                        break;
                fi
                sleep 0.25;
                EW_RETRIES=$((EW_RETRIES+1))
        done

        if [ "x$e" = "x" ] ; then
               test_expect_footer "$TESTLINE" "x$e" "x$a";
        else
               test_expect_footer "$TESTLINE" "$e" "$a";
        fi
}


function SKIP_TESTS()
{
        dbg "Skipping tests $t-$testcnt";
        while [ $t -le $testcnt ]; do
                true ; test_footer;
        done
}


function _TEST_IN_LOOP()
{
        testcnt=`expr $testcnt + 1`;
        _TEST $@
}

function _EXPECT_WITHIN_TEST_IN_LOOP()
{
        testcnt=`expr $testcnt + 1`;
        _EXPECT_WITHIN $@
}

which killall > /dev/null || {
  killall() {
    pkill $@
  }
}

which pidof > /dev/null || {
  pidof() {
    $PYTHON pidof.py $@
  }
}

stat -c %s /dev/null > /dev/null 2>&1 || {
  stat() {
    local format=""
    local f=""

    if [ "x$1" = "x-c" ] ; then
      oformat=$2
      shift
      shift
      files=$@
    else
      files=$@
    fi

    for f in $files ; do
        format=$oformat

      # %t/%T should return 0 for non devices.
      case "${format}" in
      *%t*|*%T*)
        `which stat` -f '%HT' $f | grep -q 'Device$' || \
           format=`echo "${format}" | sed 's/%t/0/g; s/%T/0/g;'`
        ;;
      *)
        ;;
      esac

      if [ "x${format}" = "x" ] ; then
        `which stat` $f
      else
         cmd=""
         case $format in
         *%u*) cmd="${cmd} s/%u/`$( which stat ) -f %u $f`/g;" ;&
         *%g*) cmd="${cmd} s/%g/`$( which stat ) -f %g $f`/g;" ;&
         *%a*) cmd="${cmd} s/%a/`$( which stat ) -f %p $f |
                    sed 's/^..//; s/^0//'`/g;" ;&
         *%A*) cmd="${cmd} s/%A/`ls -ld $f|awk '{print $1}'`/g;" ;&
         *%s*) cmd="${cmd} s/%s/`$( which stat ) -f %z $f`/g;" ;&
         *%h*) cmd="${cmd} s/%h/`$( which stat ) -f %l $f`/g;" ;&
         *%F*) cmd="${cmd} s/%F/`$( which stat ) -f %HT $f | sed '
            s/Directory/directory/;
            s/Fifo File/fifo/;
            s/Symbolic Link/symbolic link/;
            s/Regular File/regular file/;
            s/Block Device/block special file/;
            s/Character Device/character special file/;
           ' | sed \"$(
             test -s $f || echo 's/regular file/regular empty file/g'
            )\"`/g;" ;&
         *%n*) cmd="${cmd} s|%n|`$( which stat ) -f %N $f`|g;" ;&
         *%Y*) cmd="${cmd} s/%Y/`$( which stat ) -f %m $f`/g;" ;&
         *%X*) cmd="${cmd} s/%X/`$( which stat ) -f %a $f`/g;" ;&
         *%Z*) cmd="${cmd} s/%Z/`$( which stat ) -f %c $f`/g;" ;&
         *%.Z*) cmd="${cmd} s/%.Z/`$( which stat ) -f %.9Fc $f`/g;" ;&
         *%b*) cmd="${cmd} s/%b/`$( which stat ) -f %b $f`/g;" ;&
         *%B*) cmd="${cmd} s/%B/512/g;" ;&
         *%t*) cmd="${cmd} s/%t/`$( which stat ) -f %XHr $f`/g;" ;&
         *%T*) cmd="${cmd} s/%T/`$( which stat ) -f %XLr $f`/g;" ;&
         esac

         `which stat` -f "`echo $format|sed \"$cmd\"`" $f
      fi
    done
  }
}

function signal_pids() {
        local sig="$1"
        shift
        local pids=($*)

        if [[ ${#pids[@]} -gt 0 ]]; then
                kill -${sig} ${pids[@]} 2>/dev/null || true
        fi
}

function check_pids() {
        local pids=($*)
        local tmp=()
        local pid

        for pid in "${pids[@]}"; do
                kill -0 "${pid}" 2>/dev/null && tmp+=(${pid})
        done

        echo "${tmp[@]}"
}

function pids_alive() {
        local pids=($*)

        if [[ "$(check_pids ${pids[@]})" != "" ]]; then
                return 1;
        fi

        return 0
}

function terminate_pids() {
        local pids=($*)

        signal_pids TERM ${pids[@]}
        wait_delay ${PROCESS_DOWN_TIMEOUT} 0.1 pids_alive ${pids[@]}
        if [[ $? -ne 0 ]]; then
                pids=($(check_pids ${pids[@]}))
                signal_pids KILL ${pids[@]}
                wait_delay 1 0.1 pids_alive ${pids[@]}
                if [[ $? -ne 0 ]]; then
                        return 2
                fi

                return 1
        fi

        return 0
}

function process_pids() {
        local proc
        local pids=()

        for proc in $*; do
                pids+=($(pgrep ${proc}))
        done

        echo "${pids[@]}"
}

## Lock files should get automatically removed once "usradd" or "groupadd"
## command finishes. But sometimes we encounter situations (bugs) where
## some of these files may not get properly unlocked after the execution of
## the command. In that case, when we execute useradd next time, it may show
## the error “cannot lock /etc/password” or “unable to lock group file”.
## So, to avoid any such errors, check for any lock files under /etc.
## and remove those.

function remove_lock_files()
{
        if [ ! -f /etc/passwd.lock ];
        then
                rm -rf /etc/passwd.lock;
        fi

        if [ ! -f /etc/group.lock ];
        then
                rm -rf /etc/group.lock;
        fi

        if [ ! -f /etc/shadow.lock ];
        then
                rm -rf /etc/shadow.lock;
        fi

        if [ ! -f /etc/gshadow.lock ];
        then
                rm -rf /etc/gshadow.lock;
        fi
}


function cleanup()
{
        local end_time

        # Prepare flags for umount
        case `uname -s` in
        Linux)
                flag="-l"
                ;;
        NetBSD)
                flag="-f -R"
                ;;
        FreeBSD|Darwin)
                flag="-f"
                ;;
        *)
                flag=""
                ;;
        esac

        # Clean up lock files.
        remove_lock_files

        # Clean up all client mounts
        for m in `mount | grep fuse.glusterfs | awk '{print $3}'`; do
                umount $flag $m
        done

        # Unmount all well known mount points
        umount $flag $M0 2>/dev/null || umount -f $M0 2>/dev/null || true;
        umount $flag $M1 2>/dev/null || umount -f $M1 2>/dev/null || true;
        umount $flag $M2 2>/dev/null || umount -f $M2 2>/dev/null || true;
        umount $flag $N0 2>/dev/null || umount -f $N0 2>/dev/null || true;
        umount $flag $N1 2>/dev/null || umount -f $N1 2>/dev/null || true;


        # unmount all stale mounts from /tmp, This is a temporary work around
        # till the stale mount in /tmp is found.
        umount $flag /tmp/mnt* 2>/dev/null


        # Send SIGTERM to all gluster processes and rpc.statd that are still running
        terminate_pids $(process_pids glusterfs glusterfsd glusterd rpc.statd)

        test x"$OSTYPE" = x"NetBSD" && pkill -9 perfused || true

        # unregister nfs and related services from portmapper/rpcbind
        ## nfs
        rpcinfo -d 100003 3 2>/dev/null || true;
        ## mountd
        rpcinfo -d 100005 1 2>/dev/null || true;
        rpcinfo -d 100005 3 2>/dev/null || true;
        ## nlockmgr
        rpcinfo -d 100021 1 2>/dev/null || true;
        rpcinfo -d 100021 4 2>/dev/null || true;
        ## nfs_acl
        rpcinfo -d 100227 3 2>/dev/null || true;

        # unmount brick filesystems after killing daemons
        MOUNTPOINTS=`findmnt -nRlT "${B0}" -o TARGET,SOURCE | grep "$B0/" | awk '{print $2}'`
        for m in $MOUNTPOINTS;
        do
                umount $flag $m
        done

        # Cleanup lvm
        type cleanup_lvm &>/dev/null && cleanup_lvm || true;

        # Destroy loop devices
        # TODO: This should be a function DESTROY_LOOP
        case `uname -s` in
        Linux)
                for l in $(ls ${DEVDIR}/loop* 2>/dev/null); do
                        losetup -d "${l}"
                        rm -f "${l}"
                done
                ;;
        NetBSD)
                # cleanup loopback device with unmounted backing store
                for vnd in /dev/vnd* ; do
                        vnconfig -l ${vnd} 2>&1 | \
                            grep -q 'Bad file descriptor' && vnconfig -u ${vnd}
                done

                vnd=`vnconfig -l | \
                     awk '!/not in use/{printf("%s%s:%d ", $1, $2, $5);}'`
                for l in ${vnd} ; do
                        dev=${l%%:*}
                        tmp=${l#*:}
                        fs=${tmp%%:*}
                        inode=${tmp#*:}
                        file=`find -x ${fs} -inum ${inode} -print -exit`
                        echo ${file} | grep "$B0/" && \
                            LOOPDEVICES="${LOOPDEVICES} $dev"
                done
                for l in $LOOPDEVICES;
                do
                        vnconfig -u $l
                done
                ;;
        *)
                echo "`uname -s` loopback device supportmissing"
                ;;
        esac

        # remove contents of "GLUSTERD_WORKDIR" except hooks and groups
        # directories.
        if [ -n $GLUSTERD_WORKDIR ]
        then
                find  $GLUSTERD_WORKDIR/* -maxdepth 0 -name 'hooks' -prune \
                -o -name 'groups' -prune -o -exec rm -rf '{}' ';'
        else
                echo "GLUSTERD_WORKDIR is not set"
        fi

        # Complete cleanup time
        rm -rf "$B0/*" "/etc/glusterd/*";
        rm -rf $WORKDIRS
        find $GLUSTERD_PIDFILEDIR -name "*.pid" | xargs rm -rf
        leftover=""
        for d in $WORKDIRS ; do
                if test -d $d ; then
                       leftover="$leftover $d"
                fi
        done
        if [ "x$leftover" != "x" ] ; then
                echo "Aborting."
                echo
                echo "$d could not be deleted, here are the left over items"
                for d in $leftover; do
                        find $d -exec ls -ld {} \;
                done
                echo
                echo "Please correct the problem and try again."
                echo
                return 1;
        fi >&2

        # Physically release unused space
        fstrim /d

        mkdir -p $WORKDIRS
	# This is usually the last thing a test script calls, so our return
	# value becomes their exit value.  While it's not great for the mkdir
	# above to fail, promoting that into a failure of the whole test (and
	# thus of an entire regression-test run) seems a bit excessive.  Make
	# sure we return good status anyway.

        return 0
}

function force_terminate () {
        local ret=$?;
        >&2 echo -e "\nreceived external"\
                        "signal --`kill -l $ret`--, calling 'cleanup' ...\n";
        cleanup;
        exit $ret;
}

trap force_terminate INT TERM HUP

function cleanup_tester ()
{
    local exe=$1
    rm -f $exe
}

function build_tester ()
{
    local cfile=$1
    local fname=$(basename "$cfile")
    local ext="${fname##*.}"
    local execname="${fname%.*}"
    shift
    local cflags=$*
    if [ `echo $cflags | grep -c "lgfapi" ` -gt 0 ]
    then
       cflags="$cflags $(pkg-config glusterfs-api --cflags-only-I --libs-only-L)"
    fi
    $CC -g -o $(dirname $cfile)/$execname $cfile $cflags
}

function process_leak_count ()
{
    local pid=$1;
    return $(ls -lh /proc/$pid/fd | grep "(deleted)"| wc -l)
}

which truncate > /dev/null || {
  truncate() {
    local nocreate=0
    local ioblocks=0
    local fileref=""
    local newsize=""

    args=`getopt xor:s: $*`
    if [ $? -ne 0 ]; then
      echo 'Usage: truncate [-co](-r file | -s size) file ...'
      exit 2
    fi
    set -- $args
    while [ $# -gt 0 ]; do
      case "$1" in
      -c)
        nocreate=1;
        ;;
      -o)
        ioblocks=1;
        echo "Unimplemented -o option"
        exit 2
        ;;
      -r)
        fileref=$2;
        shift;
        ;;
      -s)
        newsize=$2;
        shift;
        ;;
      --)
        shift;
        break;
        ;;
      *)
        echo 'Usage: truncate [-co](-r file | -s size) file ...'
        exit 2;
        ;;
      esac
      shift
    done

    if [ "x$newsize" = "x" -a "x$fileref" = "x" ] ; then
      echo 'Usage: truncate [-co](-r file | -s size) file ...'
      exit 2;
    fi

    if [ "x$newsize" != "x" -a "x$fileref" != "x" ] ; then
      echo 'Usage: truncate [-co](-r file | -s size) file ...'
      exit 2;
    fi

    if [ "x$newsize" != "x" ] ; then
      echo $newsize | grep -q '^[-_<>%/]' && {
        echo "Unimplemented prefix in ${newsize}"
        exit 2;
      }

      echo $newsize | egrep -q '[TPEZY]B?$' && {
        echo "Unit not implemented for ${newsize}"
        exit 2;
      }

      case $newsize in
      *KB)
        newsize=$(( ${newsize/KB/} * 1000 ))
        ;;
      *K)
        newsize=$(( ${newsize/K/} * 1024 ))
        ;;
      *MB)
        newsize=$(( ${newsize/MB/} * 1000 * 1000 ))
        ;;
      *M)
        newsize=$(( ${newsize/M/} * 1024 * 1024 ))
        ;;
      *GB)
        newsize=$(( ${newsize/GB/} * 1000 * 1000 * 1000 ))
        ;;
      *G)
        newsize=$(( ${newsize/G/} * 1024 * 1024 * 1024 ))
        ;;
      esac

    fi

    if [ "x$fileref" != "x" ] ; then
       if [ ! -f $fileref ] ; then
         echo "File does not exists: ${fileref}"
         exit 2;
       fi
       newsize=`ls -l ${fileref}|awk '{print $5}'`
    fi

    if [ $# -eq 0 ]; then
      echo 'Usage: truncate [-co](-r file | -s size) file ...'
      exit 2;
    fi

    for f in $* ; do
      if [ "x$nocreate" = "x1" -a ! -f $f ] ; then
        continue;
      fi

      dd bs=1 seek=$newsize if=/dev/null of=$f msgfmt=quiet
    done
  }
}

which md5sum > /dev/null || {
  md5sum() {
    for f in $* ; do
      md5 $f | awk -F'[() ]' '{printf("%s  %s\n", $6, $3)}'
    done
  }
}

which setfattr > /dev/null || {
  setfattr() {
    $PYTHON setfattr.py $@
  }
}

which getfattr > /dev/null || {
  getfattr() {
    $PYTHON getfattr.py $@
  }
}

which sha1sum > /dev/null || {
  sha1sum() {
  case $OSTYPE in
  Darwin)
    for f in $* ; do
      openssl sha1 $f | awk -F'[() ]' '{printf("%s %s\n", $4, $2)}'
    done
    ;;
  NetBSD | FreeBSD)
    for f in $* ; do
      sha1 $f | awk -F'[() ]' '{printf("%s  %s\n", $6, $3)}'
    done
    ;;
  esac
  }
}

userdel --help 2>/dev/null | grep -q -- '--force' || {
  userdel() {
    if [ "x$1" = "x--force" ]; then
      user=$2
    else
      user=$1
    fi
    eval "$( which userdel ) $user"
  }
}

useradd --help 2>/dev/null | grep -q -- '--no-create-home' || {
  useradd() {
    # Just remove -M (do not create home) which is the default
    # other options are identical
    args=`echo $*|sed 's/-M//'`
    eval "$( which useradd ) $args"
  }
}

userdel --help 2>/dev/null | grep -q -- '--force' || {
  userdel() {
    if [ "x$1" = "x--force" ]; then
      user=$2
    else
      user=$1
    fi
    eval "$( which userdel ) $user"
  }
}

useradd --help 2>/dev/null | grep -q -- '--no-create-home' || {
  useradd() {
    # Just remove -M (do not create home) which is the default
    # other options are identical
    args=`echo $*|sed 's/-M//'`
    eval "$( which useradd ) $args"
  }
}

DBG_TEST () {
        read -p "execute \"$*\"? " x;
        case $x in
        'y')
                _TEST "$@"
                ;;
        'q')
                exit 0
                ;;
        *)
                echo "skipping"
                ;;
        esac
}

alias EXPECT='_EXPECT $LINENO'
alias EXPECT_NOT='_EXPECT_NOT $LINENO'
if [ -n "$GF_INTERACTIVE" ]; then
	alias TEST='DBG_TEST $LINENO'
else
	alias TEST='_TEST $LINENO'
fi
alias TEST_WITHIN='_TEST_WITHIN $LINENO'
alias EXPECT_WITHIN='_EXPECT_WITHIN $LINENO'
alias EXPECT_KEYWORD='_EXPECT_KEYWORD $LINENO'
alias TEST_IN_LOOP='_TEST_IN_LOOP $LINENO'
alias EXPECT_WITHIN_TEST_IN_LOOP='_EXPECT_WITHIN_TEST_IN_LOOP $LINENO'
shopt -s expand_aliases

if [ x"$OSTYPE" = x"Linux" ]; then
        alias dd="dd status=none"
elif [ x"$OSTYPE" = x"NetBSD" ]; then
        alias dd="dd msgfmt=quiet"
fi
# MacOS doesn't seem to support either option.  Doing nothing at all is
# probably the safest option there and on anything we don't recognize, but
# if you want to reduce the noise level and know the correct option for
# your favorite platform please feel free to add it here.

function SETUP_LOOP ()
{
  if [ $# != 1 ] ; then
    echo "SETUP_LOOP usage" >&2
    return 1;
  fi

  backend=$1

  case ${OSTYPE} in
  Linux)
    dev="$(losetup --find --show ${backend})"
    ln -sf "${dev}" "${DEVDIR}/$(basename "${dev}")"
    echo "${dev}"
    ;;
  NetBSD)
    vnd=`vnconfig -l|awk -F: '/not in use/{print $1; exit}'`
    if [ "x${vnd}" = "x" ] ; then
      echo "no more vnd" >&2
      return 1;
    fi
    vnconfig ${vnd} ${backend}
    echo ${vnd}
    ;;
  *)
    echo "Please define SETUP_LOOP for ${OSTYPE} in include.rc" >&2
    return 1;
    ;;
  esac
}

function MKFS_LOOP ()
{
  args=`getopt i: $*`
  if [ $? -ne 0 ] ; then
    echo "MKFS_LOOP usage" >&2
    return 1;
  fi
  set -- ${args}

  isize=""
  while test $# -gt 0; do
    case "$1" in
    -i)         isize=$2; shift ;;
    --)         shift; break ;;
    esac
    shift
  done

  dev=$1

  case ${OSTYPE} in
  Linux)
    test "x${isize}" != "x" && isize="-i size=${isize}"
    mkfs.xfs  -f ${isize} ${dev}
    ;;
  NetBSD)
    test "x${isize}" != "x" && isize="-i ${isize}"

    echo ${dev} | grep -q '^vnd'
    if [ $? -ne 0 ] ; then
      vnd=`vnconfig -l|awk -F: '/not in use/{print $1; exit}'`
      if [ "x${vnd}" = "x" ] ; then
        echo "no more vnd" >&2
        return 1;
      fi
      vnconfig ${vnd} ${dev}
    else
      vnd=${dev}
    fi
    newfs ${isize} /dev/r${vnd}a
    ;;
  *)
    echo "Please define MKFS_LOOP for ${OSTYPE} in include.rc" >&2
    return 1;
    ;;
  esac
}

# usage: log_newer timestamp "string"
# search in glusterfs logs for "string" logged after timestamp seconds
# since the Epoch (usually obtained by date +%s)
log_newer()
{
        ts=$1
        msg=$2
        logdir=`$CLI --print-logdir`

        local x_ifs=$IFS
        IFS="["
        for date in `grep -hr "$msg" $logdir | grep -v "G_LOG" | awk -F '[\]]' '{print $1}'` ; do
                if [ `date -d "$date" +%s` -gt $ts ] ; then
                        IFS=$x_ifs
                        return 0;
                fi
        done 2>/dev/null
        IFS=$x_ifs
        return 1
}

function MOUNT_LOOP ()
{
  if [ $# != 2 ] ; then
    echo "MOUNT_LOOP usage" >&2
    return 1;
  fi

  dev=$1
  target=$2

  case ${OSTYPE} in
  Linux)
    echo ${dev} | grep -q '^/dev/loop'
    if [ $? -eq 0 ] ; then
      mount -t xfs  ${dev} ${target}
    else
      mount -o loop  ${dev} ${target}
    fi
    ;;
  NetBSD)
    echo ${dev} | grep -q '^vnd'
    if [ $? -ne 0 ] ; then
      ino=`/usr/bin/stat -f %i ${dev}`
      dev=`vnconfig -l | awk -v ino=${ino} -F'[: ]*' '($5 == ino) {print $1}'`
    fi

    mount /dev/${dev}a ${target} >&2
    if [ $? -ne 0 ] ; then
      echo "failed to mount  /dev/${dev}a on  ${target}" >&2
      return 1;
    fi

    mkdir -p ${target}/.attribute/system  ${target}/.attribute/user
    mount -u -o extattr ${target} >&2

    ;;
  *)
    echo "Please define MOUNT_LOOP for ${OSTYPE} in include.rc" >&2
    return 1;
    ;;
  esac
}

function UMOUNT_LOOP ()
{
  case ${OSTYPE} in
  Linux)
    force_umount $*
    ;;
  NetBSD)
    for target in $* ; do
      dev=`mount | awk -v target=${target} '($3 == target) {print $1}'`
      force_umount ${target}
      echo ${dev} | grep -q '^/dev/vnd'
      if [ $? -eq 0 ] ; then
        dev=`echo ${dev} | sed 's|^/dev/||; s|a$||'`
        vnconfig -u ${dev}
      else
        ino=`/usr/bin/stat -f %i ${dev}`
        dev=`vnconfig -l | awk -v ino=${ino} -F'[: ]*' '($5 == ino) {print $1}'`
        if [ "x${dev}" != "x" ] ; then
          vnconfig -u ${dev}
        fi
      fi
    done
    ;;
  *)
    echo "Please define UMOUNT_LOOP for ${OSTYPE} in include.rc" >&2
    return 1;
    ;;
  esac
}

function STAT()
{
        stat $1
        echo $?
}

function STAT_INO()
{
        local ino=$(stat -c '%i' $1)
        if [ $? -eq 0 ]; then
                echo $ino
        else
                echo 0
        fi
}

function get_md5_sum()
{
    local file=$1;
    md5_sum=$(md5sum $file | awk '{print $1}');
    echo $md5_sum
}
