#!/bin/bash
#
# Test NBD client unexpected disconnect
#
# Copyright Red Hat, Inc. 2014
#
# 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, see <http://www.gnu.org/licenses/>.
#

# creator
owner=stefanha@redhat.com

seq=`basename $0`
echo "QA output created by $seq"

here=`pwd`
tmp=/tmp/$$
status=1	# failure is the default!

# get standard environment, filters and checks
. ./common.rc
. ./common.filter

_supported_fmt generic
_supported_proto nbd
_supported_os Linux

# Pick a TCP port based on our pid.  This way multiple instances of this test
# can run in parallel without conflicting.
choose_tcp_port() {
	echo $((($$ % 31744) + 1024)) # 1024 <= port < 32768
}

wait_for_tcp_port() {
	while ! (netstat --tcp --listening --numeric | \
		 grep "$1.*0\\.0\\.0\\.0:\\*.*LISTEN") 2>&1 >/dev/null; do
		sleep 0.1
	done
}

filter_nbd() {
	# nbd.c error messages contain function names and line numbers that are prone
	# to change.  Message ordering depends on timing between send and receive
	# callbacks sometimes, making them unreliable.
	#
	# Filter out the TCP port number since this changes between runs.
	sed -e 's#^.*nbd\.c:.*##g' \
	    -e 's#nbd:127\.0\.0\.1:[^:]*:#nbd:127\.0\.0\.1:PORT:#g' \
            -e 's#\(exportname=foo\|PORT\): Failed to .*$#\1#'
}

check_disconnect() {
	event=$1
	when=$2
	negotiation=$3
	echo "=== Check disconnect $when $event ==="
	echo

	port=$(choose_tcp_port)

	cat > "$TEST_DIR/nbd-fault-injector.conf" <<EOF
[inject-error]
event=$event
when=$when
EOF

	if [ "$negotiation" = "--classic-negotiation" ]; then
		extra_args=--classic-negotiation
		nbd_url="nbd:127.0.0.1:$port"
	else
		nbd_url="nbd:127.0.0.1:$port:exportname=foo"
	fi

	$PYTHON nbd-fault-injector.py $extra_args "127.0.0.1:$port" "$TEST_DIR/nbd-fault-injector.conf" 2>&1 >/dev/null &
	wait_for_tcp_port "127\\.0\\.0\\.1:$port"
	$QEMU_IO -c "read 0 512" "$nbd_url" 2>&1 | _filter_qemu_io | filter_nbd

	echo
}

for event in neg1 "export" neg2 request reply data; do
	for when in before after; do
		check_disconnect "$event" "$when"
	done

	# Also inject short replies from the NBD server
	case "$event" in
	neg1)
		for when in 8 16; do
			check_disconnect "$event" "$when"
		done
		;;
	"export")
		for when in 4 12 16; do
			check_disconnect "$event" "$when"
		done
		;;
	neg2)
		for when in 8 10; do
			check_disconnect "$event" "$when"
		done
		;;
	reply)
		for when in 4 8; do
			check_disconnect "$event" "$when"
		done
		;;
	esac
done

# Also check classic negotiation without export information
for when in before 8 16 24 28 after; do
	check_disconnect "neg-classic" "$when" --classic-negotiation
done

# success, all done
echo "*** done"
rm -f $seq.full
status=0
