#!/bin/sh
# Copyright (c) 2000-2019 Synology Inc. All rights reserved.

. /usr/syno/share/synomemtester/syno_memtester_util.sh

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/syno/bin:/usr/syno/sbin
GPIO=/sys/class/gpio
SYNOINFO=/etc.defaults/synoinfo.conf
BN_CONF=/root/burnin_test.conf

BN_LOCK=/tmp/burnin.lock

BN_STATUS=/tmp/burnin.$$
BN_STATUS_UNKNOWN=0
BN_STATUS_INIT=1
BN_STATUS_STAGE1=2
BN_STATUS_STAGE2=3
BN_STATUS_COMPLETE=4
BN_STATUS_FAILED=5
BN_STAGE1_BOOTLOADER_MTEST_RET="Not Tested"

# link self test begins from eth2. all before eth2 will do wget
BN_STAGE2_SELF_TEST_BEGIN=2

TEST_NOTYET=0
TEST_PASSED=1
TEST_FAILED=2

is_us2="no"
is_us3="no"
is_alpine_2_10G="no"
is_vs960hd="no"
support_lcm="no"
bn_stage2_been_passed="no"
bn_stage2_do_selftest="yes"

bn_stage2_loopbak_list=""
bn_stage2_wget_list=""
bn_stage2_selftest_list=""

enable_remote_command=0

# self loop test IPs
IP1="10.10.1.1"
IP2="20.20.1.1"
IP3="30.30.1.1"
IP4="40.40.1.1"

# Variables for copy log
burninlog_mnt="/logmnt"
burnin_server_ipaddr="169.254.1.1"
label="B1"
product_serial=`cat /proc/sys/kernel/syno_serial`
burnin_log_path="/tmp/"
startTime=`date +"%Y%m%d%H%M%S"`
FLAG_LIST_TMP="/tmp/flag_list"
bn_new_log_folder="/volume1/SYNOTESTLOG"

lower_case_char(){
	local n=0
	local m=0
	case "$1" in
		[A-Z])
			n=`printf "%d" "'$1"`
			m=`expr $n + 32`
			printf "\x$(printf %x $m)"
			;;
		*)
			printf "%s" "$1"
			;;
	esac
}

lower_case_string(){
	local word=$1
	local len=${#word}
	for i in `seq 1 ${len}`;
	do
		lower_case_char `echo $word | cut -c$i`
	done
}

get_model_name()
{
	local unique=`get_key_value /etc.defaults/synoinfo.conf unique | cut -d'_' -f3`

	if echo $unique | cut -c1 | grep -qs "[0-9]"; then
		lower_case_string "ds"$unique
	else
		lower_case_string $unique
	fi
}

get_log_dir()
{
	local model=`get_model_name`
	local serial_8th=`cat /proc/sys/kernel/syno_serial | cut -c8`
	local startDate=`cat /proc/sys/kernel/syno_serial | cut -c1-7`
	echo ${burninlog_mnt}/${model}/${label}/${startDate}/${serial_8th}/${product_serial}
}

get_spec_hw()
{
	local _isusbstation=`get_key_value $SYNOINFO usbstation`
	local _isus3=`cat /proc/sys/kernel/syno_hw_version| grep US3`
	local _isds2015xs=`cat /proc/sys/kernel/syno_hw_version | grep DS2015xs`
	local _isds1515=`cat /proc/sys/kernel/syno_hw_version | grep "DS1515-"`
	local _isds1517=`cat /proc/sys/kernel/syno_hw_version | grep "DS1517-"`
	local _isds1817=`cat /proc/sys/kernel/syno_hw_version | grep "DS1817-"`
	local _isvs960hd=`cat /proc/sys/kernel/syno_hw_version | grep "VS960HD-"`
	local _isds3619xs=`cat /proc/sys/kernel/syno_hw_version | grep "DS3619xs"`
	local _issa3200d=`cat /proc/sys/kernel/syno_hw_version | grep "SA3200d"`
	local _isrs1219=`cat /proc/sys/kernel/syno_hw_version | grep "RS1219-"`
	local _isds1621p=`cat /proc/sys/kernel/syno_hw_version | grep "DS1621+"`
	local _isds1620xsp=`cat /proc/sys/kernel/syno_hw_version | grep "DS1620xs+"`
	support_lcm=`get_key_value $SYNOINFO support_acm`

	if [ "x" != "x$_isds2015xs" ] || [ "x" != "x$_isds1817" ]; then
		bn_stage2_loopbak_list="0 1"
		bn_stage2_wget_list="2 3"
		is_alpine_2_10G="yes"
	elif [ "x" != "x$_isds1515" ] || [ "x" != "x$_isds1517" ] || [ "x" != "x$_isrs1219" ] || [ "x" != "x$_isds3619xs" ] || [ "x" != "x$_isds1621p" ]; then
		bn_stage2_wget_list="0 1"
		bn_stage2_loopbak_list="2 3"
	elif [ "x" != "x$_issa3200d" ]; then
		bn_stage2_wget_list="2"
		bn_stage2_loopbak_list="0 1"
	elif [ "x" != "x$_isds1620xsp" ]; then
		bn_stage2_wget_list="0"
		bn_stage2_selftest_list="1 2"
	elif [ 1 = $(system_max_lan) ]; then
		bn_stage2_wget_list="0"
	elif [ 2 = $(system_max_lan) ]; then
		bn_stage2_wget_list="0 1"
	elif [ 4 = $(system_max_lan) ]; then
		bn_stage2_wget_list="0 1"
		bn_stage2_selftest_list="2 3"
	else
		bn_log "eth type is not defined"
		bn_error_exit
	fi

	if [ "x" != "x$_isvs960hd" ]; then
		is_vs960hd="yes"
	fi

	if [ "yes" != "$_isusbstation" ]; then
		return
	fi

	if [ "x" = "x$_isus3" ]; then
		is_us2="yes"
	else
		is_us3="yes"
	fi
}

led_init()
{
	if [ "yes" = "$is_us3" ]; then
		echo 42 > $GPIO/export
		echo out > $GPIO/gpio42/direction
		echo 43 > $GPIO/export
		echo out > $GPIO/gpio43/direction
	elif [ "yes" = "$is_us2" ]; then
		echo 40 > $GPIO/export
		echo out > $GPIO/gpio40/direction
		echo 36 > $GPIO/export
		echo out > $GPIO/gpio36/direction
		echo 37 > $GPIO/export
		echo out > $GPIO/gpio37/direction
	fi
}

led_status_off()
{
	if [ "yes" = "$is_us2" ]; then
		echo 0 > $GPIO/gpio40/value
		echo 0 > $GPIO/gpio36/value
	elif [ "yes" = "$is_vs960hd" ]; then
		mantool -gpio_write 2 1 > /dev/null
		mantool -gpio_write 3 1 > /dev/null
	elif [ "yes" = "$support_lcm" ]; then
		echo 7 > /dev/ttyACM0
	else
		echo 7 > /dev/ttyS1
	fi
}

led_status_green()
{
	if [ "yes" = "$is_us3" ]; then
		echo 0 > $GPIO/gpio42/value
		echo 1 > $GPIO/gpio43/value
	elif [ "yes" = "$is_us2" ]; then
		echo 0 > $GPIO/gpio40/value
		echo 1 > $GPIO/gpio36/value
	elif [ "yes" = "$is_vs960hd" ]; then
		mantool -gpio_write 2 0 > /dev/null
		mantool -gpio_write 3 1 > /dev/null
	elif [ "yes" = "$support_lcm" ]; then
		echo 8 > /dev/ttyACM0
	else
		echo 8 > /dev/ttyS1
	fi
}

led_status_orange()
{
	if [ "yes" = "$is_us3" ]; then
		echo 1 > $GPIO/gpio42/value
		echo 0 > $GPIO/gpio43/value
	elif [ "yes" = "$is_us2" ]; then
		echo 1 > $GPIO/gpio40/value
		echo 0 > $GPIO/gpio36/value
	elif [ "yes" = "$is_vs960hd" ]; then
		mantool -gpio_write 2 1 > /dev/null
		mantool -gpio_write 3 0 > /dev/null
	elif [ "yes" = "$support_lcm" ]; then
		echo : > /dev/ttyACM0
	else
		echo : > /dev/ttyS1
	fi
}

led_stage_procesing()
{
	local _led_status=0
	local _stop_function=$1

	while ! $_stop_function; do
		if [ 0 -eq "$_led_status" ]; then
			led_status_green
		else
			led_status_orange
		fi

		_led_status=$((($_led_status + 1) % 2))
		sleep 1
	done
}

led_power_on()
{
	if [ "yes" = "$is_us2" ]; then
		echo 0 > $GPIO/gpio37/value
	elif [ "yes" = "$is_vs960hd" ]; then
		mantool -gpio_write 0 0 > /dev/null
		mantool -gpio_write 1 1 > /dev/null
	else
		echo 4 > /dev/ttyS1
	fi
}

led_power_off()
{
	if [ "yes" = "$is_us2" ]; then
		echo 1 > $GPIO/gpio37/value
	elif [ "yes" = "$is_vs960hd" ]; then
		mantool -gpio_write 0 1 > /dev/null
		mantool -gpio_write 1 1 > /dev/null
	else
		echo 6 > /dev/ttyS1
	fi
}

system_max_lan()
{
	local _num=-1

	if [ -f /proc/sys/kernel/syno_internal_netif_num ]; then
		_num=$(cat /proc/sys/kernel/syno_internal_netif_num)
	else
		_num=$(get_key_value $SYNOINFO maxlanport)
	fi

	[ "$_num" -ge 1 ] || _num=1
	echo $_num
}

test_flag_set()
{
	mantool -test_flag_set $1 $2
	mantool -test_flag_list >> ${FLAG_LIST_TMP}
	bn_timestamp >> ${FLAG_LIST_TMP}
}

test_flag_get()
{
	mantool -test_flag_get $1
}

free_tmp()
{
	local tmp_free=$(df -m /tmp | tail -1 | xargs echo | cut -d' ' -f4)
	echo $(((${tmp_free:-50} - 2) * 95 / 100))
}

bn_msg()
{
	echo `date +'%b %e %T'` burnin: "$@"
}

bn_log()
{
	local _msg=$(bn_msg "$@")

	echo $_msg
	echo $_msg >> /var/log/messages
}

bn_status_set()
{
	echo "$1" >> $BN_STATUS
}

bn_status_get()
{
	if [ -f $BN_STATUS ]; then
		tail -1 $BN_STATUS 2>> /var/log/messages
	else
		echo "$BN_STATUS not found, status unknown" >> /var/log/messages
		echo $BN_STATUS_UNKNOWN
	fi
}

bn_status_show()
{
	local _pid=$(cat $BN_LOCK 2>> /var/log/messages)

	[ -f "/tmp/burnin.$_pid" ] || { echo "no burn-in is running..."; return; }

	case "$(tail -1 /tmp/burnin.$_pid 2>> /var/log/messages)" in
		$BN_STATUS_UNKNOWN)
			echo "unknown"
			;;
		$BN_STATUS_INIT)
			echo "initialize"
			;;
		$BN_STATUS_STAGE1)
			echo "memory test. progress: $(cat /tmp/memtester.progress 2> /dev/null)"
			;;
		$BN_STATUS_STAGE2)
			echo "dma test"
			;;
		$BN_STATUS_COMPLETE)
			echo "PCBA burn-in completed"
			;;
		$BN_STATUS_FAILED)
			echo "PCBA burn-in failed"
			;;
	esac
}

bn_lock()
{
	if [ ! -f $BN_LOCK ]; then
		echo $$ > $BN_LOCK
	else
		bn_log "PCBA burn-in test is already running..."
		exit 1
	fi
}

bn_unlock()
{
	rm -f $BN_LOCK
}

bn_completed()
{
	bn_log "Burn-in test main thread completed"
	bn_status_set $BN_STATUS_COMPLETE

	syno_memtester_post

	exit 0
}

bn_error_exit()
{
	bn_unlock
	if [ "Fail" = "$(test_flag_get PTB_NETWORK_CRC)" ]; then
		bn_log "Restart rc.network"
		/etc/rc.network restart > /dev/null 2>&1
	fi
	bn_log "Burn-in exited with error or force terminate"
	bn_status_set $BN_STATUS_FAILED

	syno_memtester_post

	bn_copy_log_to_server
	exit 1
}

bn_init()
{
	set -o nounset
	trap bn_error_exit HUP INT TERM

	get_spec_hw
	led_init
	bn_status_set $BN_STATUS_INIT
}

bn_seconds()
{
	date +%s | cut -d' ' -f3
}

bn_wget_max_lan()
{
	local _maxlan=0

	for _lan in ${bn_stage2_wget_list}; do
		_maxlan=$(($_maxlan + 1))
	done

	echo $_maxlan
}

bn_link_detect()
{
	if [ -f /usr/bin/ethtool ]; then
		[ "yes" = "$(echo `ethtool $1 2>&1 | grep 'Link detected' | cut -d':' -f2`)" ]
	else
		true # usbstation2
	fi
}

bn_stage1_is_bootloader_support()
{
	local ret=`cat /proc/cmdline | grep "MTEST_BOOTLOADER=1"`
	[ ! -z "$ret" ]
}

bn_stage1_is_bootloader_mtest_tested()
{
	local ret=`/usr/syno/bin/mantool -get_mtest_ret | grep "Not tested"`
	[ -z "$ret" ]
}

bn_stage1_get_bootloader_mtest_ret()
{
	local ret=`/usr/syno/bin/mantool -get_mtest_ret | grep "Error"`
	if [ ! -z "$ret" ]; then
		BN_STAGE1_BOOTLOADER_MTEST_RET="ERROR"
	else
		BN_STAGE1_BOOTLOADER_MTEST_RET="PASS"
	fi
}

bn_stage1()
{

	[ "xyes" == "x$bn_stage1_skip" ] && return

	syno_memtester_pre
	bn_log "Start memtester"
	bn_status_set $BN_STATUS_STAGE1
	bn_stage1_led_status &

	if bn_stage1_is_bootloader_support; then
		bn_log "BN_STAGE1_BOOTLOADER_MTEST_RET = ${BN_STAGE1_BOOTLOADER_MTEST_RET}"
		if [ "Not Tested" = "$BN_STAGE1_BOOTLOADER_MTEST_RET" ]; then
			bn_log "Trigger bootloader to do memory test"
			/usr/syno/bin/mantool -set_mtest_on
			/sbin/reboot
		elif [ "ERROR" = "$BN_STAGE1_BOOTLOADER_MTEST_RET" ]; then
			bn_log "Failed to do memtester"
			led_status_orange
			test_flag_set PTB_MEMORY_TEST $TEST_FAILED
			bn_error_exit
		fi
	else
		if ! memtester -max 1; then
			bn_log "Failed to do memtester"
			test_flag_set PTB_MEMORY_TEST $TEST_FAILED
			bn_error_exit
		fi
	fi

	syno_memtester_post
	bn_log "Complete memtester"
	test_flag_set PTB_MEMORY_TEST $TEST_PASSED
	sleep 2
}

bn_stage1_led_status()
{
	local _led_active=""
	local _led_complete=""
	if [ "yes" = "$is_us3" ]; then
		_led_active="memtest_blinking"
	else
		_led_active="usbcopy_blinking"
	fi

	synohwctrl -set_led ${_led_active}
	sleep 1
	led_stage_procesing bn_stage1_should_stop
	if [ "yes" = "$is_us3" ]; then
		synohwctrl -set_led "memtest_off"
	fi
}

bn_stage1_should_stop()
{
	local _get=`ps | grep memtester | grep -v grep`
	[ "x" = "x$_get" ]
}

bn_stage2()
{
	local _lan=0
	local _begin_time=$(bn_seconds)

	bn_log "Check stage2 of this unit whether had been passed or not"
	bn_stage2_been_passed_check

	bn_log "Start RAM/NIC test"
	bn_status_set $BN_STATUS_STAGE2

	#loopback test
	if [ "x" != "x${bn_stage2_loopbak_list}" ]; then
		local loop_back_lan0=`echo $bn_stage2_loopbak_list | cut -d" " -f 1`
		local loop_back_lan1=`echo $bn_stage2_loopbak_list | cut -d" " -f 2`
		bn_stage2_self_loop_test $loop_back_lan0 $loop_back_lan1 &
	fi
	#wget
	for _lan in ${bn_stage2_wget_list}; do
		bn_stage2_wget $_lan &
	done

	#selftest
	bn_stage2_selftest &

	bn_stage2_links_detect &
	bn_stage2_led_status &

	bn_stage2_periodic_check &

	while [ $(($(bn_seconds) - $_begin_time)) -lt $(($bn_stage2_duration * 60)) ]; do
		# only error would stop main loop before time is up
		[ "$BN_STATUS_STAGE2" -eq "$(bn_status_get)" ] || bn_error_exit
		sleep 10
	done

	bn_log "Stage2 has been lasted for $bn_stage2_duration minutes and passed successfully."
	test_flag_set PTB_FILE_CHECKSUM $TEST_PASSED
	test_flag_set PTB_NETWORK_CRC $TEST_PASSED
	test_flag_set PTB_FILE_COPY $TEST_PASSED
	test_flag_set pt_dma_cnt $((($(bn_seconds) - $_begin_time) / 60))

	[ "Pass" != "$(test_flag_get PTB_FILE_CHECKSUM)" ] && bn_log "Fail to set PTB_FILE_CHECKSUM!" && bn_error_exit
	[ "Pass" != "$(test_flag_get PTB_NETWORK_CRC)" ] && bn_log "Fail to set PTB_NETWORK_CRC!" && bn_error_exit
	[ "Pass" != "$(test_flag_get PTB_FILE_COPY)" ] && bn_log "Fail to set PTB_FILE_COPY!" && bn_error_exit
}

bn_stage2_duration_adjust()
{
	local _duration=$(get_key_value $BN_CONF bn_stage2_duration)
	local _maxdisks=$(get_key_value $SYNOINFO maxdisks)
	local _freemem=$(free | grep Mem| xargs echo| cut -d' ' -f2)
	_freemem=`expr ${_freemem} / 1024`

	if [ "$_freemem" -le 1024 ] && [ "$_maxdisks" -le 2 ]; then
		bn_stage2_duration=60
	else
		bn_stage2_duration=90
	fi

	if [ "x" != "x$_duration" ]; then
		bn_stage2_duration=$_duration
	fi
}

bn_stage2_been_passed_check()
{
	if [ "Pass" = "$(test_flag_get PTB_FILE_CHECKSUM)" ] && [ "Pass" = "$(test_flag_get PTB_NETWORK_CRC)" ] && [ "Pass" = "$(test_flag_get PTB_FILE_COPY)" ]; then
		bn_stage2_been_passed="yes"
	else
		bn_log "This unit is not passed yet."
	fi
}

bn_stage2_fail_or_unknown()
{
	[ "$BN_STATUS_FAILED" -eq "$(bn_status_get)" -o "$BN_STATUS_UNKNOWN" -eq "$(bn_status_get)" ]
}

bn_stage2_fail_or_unknown_or_complete()
{
	bn_stage2_fail_or_unknown || [ "$BN_STATUS_COMPLETE" -eq "$(bn_status_get)" ]
}

bn_stage2_should_stop()
{
	if [ "xyes" == "x$bn_continue_after_pass" ]; then
		bn_stage2_fail_or_unknown
	else
		bn_stage2_fail_or_unknown_or_complete
	fi
}

bn_stage2_air_setup()
{
	local _bridge="lbr0"
	local _unique=$(get_key_value $SYNOINFO unique)
	local _airmodel=`echo ${_unique} | grep air | grep -v 213`
	local _airbridge=`brctl show | grep ${_bridge}`

	if [ "x" != "x${_airmodel}" -a "x" != "x${_airbridge}" ]; then
		`ifconfig ${_bridge} down`
		`brctl delbr ${_bridge}`
	fi
}

bn_stage2_self_loop_network_setup()
{
	# For some specific models, we physically connect eth$1 and eth$2 together, to form a self loop.
	# Here sets up the required network settings
	local _lan1=$1
	local _lan2=$2

	lsmod | grep iptable_nat > /dev/null
	if [ $? -ne 0 ]; then
		for m in nf_conntrack nf_defrag_ipv4 nf_conntrack_ipv4 nf_nat x_tables ip_tables nf_nat_ipv4 xt_nat iptable_nat; do
			insmod /lib/modules/$m.ko
		done
		sleep 1
	fi

	# avoid flushing kernel log "nf_conntrack: table full, dropping packet."
	echo 60 > /proc/sys/net/netfilter/nf_conntrack_generic_timeout  # was 600
	echo 131072 > /proc/sys/net/netfilter/nf_conntrack_max  # was 65536
	echo 20 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established  # was 432000

	if [ "xyes" = "x${is_alpine_2_10G}" ]; then
		# disable 10GbE RX equalization
		echo 0 > /sys/class/net/eth${_lan1}/device/rx_equal_enable
		echo 0 > /sys/class/net/eth${_lan2}/device/rx_equal_enable
		ifconfig | grep eth${_lan1} > /dev/null
		if [ $? -ne 0 ]; then
			ifconfig eth${_lan1} up
			ifconfig eth${_lan2} up
			sleep 25  # wait for udhcpc to take effect. We want to overwrite dhcp's result
		fi
	fi
	ifconfig eth${_lan1} ${IP1} netmask 255.255.0.0
	bn_log "Set IP address ${IP1} to eth${_lan1}"
	ifconfig eth${_lan2} ${IP2} netmask 255.255.0.0
	bn_log "Set IP address ${IP2} to eth${_lan2}"
	/sbin/iptables -t nat -A POSTROUTING -s ${IP1} -d ${IP4} -j SNAT --to-source ${IP3}
	/sbin/iptables -t nat -A PREROUTING -d ${IP3} -j DNAT --to-destination ${IP1}
	/sbin/iptables -t nat -A POSTROUTING -s ${IP2} -d ${IP3} -j SNAT --to-source ${IP4}
	/sbin/iptables -t nat -A PREROUTING -d ${IP4} -j DNAT --to-destination ${IP2}

	ip route add ${IP4} dev eth${_lan1} > /dev/null 2>&1
	arp -i eth${_lan1} -s ${IP4} `ifconfig eth${_lan2} | grep HWaddr | cut -c 39-55`
	ip route add ${IP3} dev eth${_lan2} > /dev/null 2>&1
	arp -i eth${_lan2} -s ${IP3} `ifconfig eth${_lan1} | grep HWaddr | cut -c 39-55`
}

bn_stage2_led_status()
{

	if [ "yes" == "$is_us3" ]; then
		synohwctrl -set_led memtest_on
	elif [ "yes" = "$is_us2" ] || [ "yes" == "$is_vs960hd" ]; then
		led_power_off
	else
		#usbcopy_off means green on, not off
		synohwctrl -set_led usbcopy_off
	fi

	led_stage_procesing bn_stage2_fail_or_unknown_or_complete

	if [ "$BN_STATUS_COMPLETE" -eq "$(bn_status_get)" ]; then
		bn_completed_led_status
	fi
}

# status LED green blinking controlled by CPU instead of uP, to prevent CPU hang cannot be distinguished from LED
bn_completed_led_status()
{
	local _led_status=0
	while ! bn_stage2_fail_or_unknown; do
		if [ 0 -eq "$_led_status" ]; then
			led_status_green
		else
			led_status_off
		fi
		_led_status=$((($_led_status + 1) % 2))
		sleep 1
	done
}

bn_stage2_wget()
{
	local _nif=eth$1
	local _server=""
	local _localdir=""
	local _freetmp=$(free_tmp)
	local _maxidx=$(( $_freetmp / $(bn_wget_max_lan) ))
	local _platform=$(get_key_value $SYNOINFO unique | cut -d"_" -f2)
	local _idx=0
	local _crcerr=0
	local _httpok="no"
	local _retrywget=5
	local _localfile=$_localdir/testing.$_idx

	local _i=0
	for _lan in ${bn_stage2_wget_list}; do
		if [ $1 = $_lan ]; then
			_server=169.$((254 - $_i)).1.1
			break
		fi
		_i=$(($_i + 1))
	done

	_localdir=/tmp/$_server

	mkdir -p $_localdir

	while ! bn_stage2_should_stop; do
		_idx=0
		_crcerr=0

		[ "xyes" == "x$bn_debug" ] && bn_log "Remove files in $_localdir"
		rm -f $_localdir/*

		while [ $_idx -lt $_maxidx ]; do

			_httpok="no"
			_retrywget=5
			_localfile=$_localdir/testing.$_idx

			[ "xyes" == "x$bn_debug" ] && bn_log "Fetching $_localfile from $_server"
			while [ "yes" != "$_httpok" -a $_retrywget -gt 0 ]; do
				if wget http://${_server}/testing -O$_localfile > /dev/null 2>&1; then
					_httpok="yes"
				fi
				_retrywget=$(($_retrywget - 1))
			done

			if [ "yes" != "$_httpok" ]; then
				bn_log "wget returned error ($?) when fetching $_localfile"
				test_flag_set PTB_FILE_COPY $TEST_FAILED
				bn_error_exit
			elif [ ! -f "$_localfile" ]; then
				bn_log "Failed to wget $_localfile"
				test_flag_set PTB_FILE_COPY $TEST_FAILED
				bn_error_exit
			elif [ "4193933615" != $(cksum $_localfile | cut -d' ' -f1) ]; then
				bn_log "Incorrect checksum of $_localfile"
				test_flag_set PTB_FILE_CHECKSUM $TEST_FAILED
				bn_error_exit
			fi

			_idx=$(($_idx + 1))
		done

		if [ "6281" = "$(get_key_value $SYNOINFO unique | cut -d'_' -f2)" ]; then
			[ "xyes" == "x$bn_debug" ] && bn_log "Check network CRC errors for $_nif."
			_crcerr=$(ifconfig $_nif | grep 'RX' | grep errors | cut -d':' -f3 | cut -d' ' -f1)
			if [ "$_crcerr" -gt 2 ]; then
				led_power_off
			elif [ "$_crcerr" -gt 4 ]; then
				bn_log "Too many RX error packets for $_nif"
				test_flag_set PTB_NETWORK_CRC $TEST_FAILED
				bn_error_exit
			fi
		fi

	done
}

bn_stage2_selftest()
{
	#nothing in wget list, just leave
	[[ -n "$bn_stage2_selftest_list" ]] || return

	while ! bn_stage2_should_stop; do
		for _lan in ${bn_stage2_selftest_list}; do
			[ "xyes" == "x$bn_debug" ] && bn_log "Self test eth$_lan"
			if ! ethtool -t eth$_lan online 1>/dev/null 2>&1; then
				bn_log "Failed to self test eth$_lan"
				test_flag_set PTB_NETWORK_CRC $TEST_FAILED
				bn_error_exit
			fi

			sleep 10
		done
	done
}

bn_network_setup()
{
	local _lan=0
	local _i=0
	local _backup_ip=""

	bn_stage2_air_setup
	ip route flush table main

	if [ "x" != "x${bn_stage2_loopbak_list}" ]; then
		local loop_back_lan0=`echo $bn_stage2_loopbak_list | cut -d" " -f 1`
		local loop_back_lan1=`echo $bn_stage2_loopbak_list | cut -d" " -f 2`

		bn_stage2_self_loop_network_setup $loop_back_lan0 $loop_back_lan1
	fi

	for _lan in ${bn_stage2_wget_list}; do
		local _old=$(ifconfig eth$_lan | grep 'inet addr' | cut -d':' -f2 | cut -d' ' -f1 )
		if [ "x" = "x${_old}" ]; then
			_old=${_backup_ip}
		fi
		local _new=169.$((254 - $_i)).$(echo $_old | cut -d'.' -f3,4)

		_backup_ip=${_old}
		bn_log "Set IP address $_new to eth$_lan"
		ip route add 169.$((254 - $_i)).0.0/16 dev eth$_lan
		ifconfig eth$_lan $_new

		_i=$(($_i + 1))
	done

	sleep 3
}

bn_check_network_by_ping()
{
	local _i=0

	for _lan in ${bn_stage2_wget_list}; do
		local _ping_IP="169.$((254 - $_i)).1.1"

		ping -w 5 -c 5 $_ping_IP
		if [ "x0" != "x$?" ]; then
			bn_log "Failed to ping $_ping_IP by eth$_lan"
			bn_log "Restart rc.network"
			/etc/rc.network restart > /dev/null 2>&1
			bn_error_exit
		fi

		_i=$(($_i + 1))
	done
}

bn_links_detect_common()
{
	local _sleep_time=$1
	local _stage=$2

	#we check link only for wget eth
	for _lan in ${bn_stage2_wget_list}; do
		[ "xyes" == "x$bn_debug" ] && bn_log "Detect link of eth$_lan"
		if ! bn_link_detect "eth$_lan"; then
			bn_log "Failed to detect link of eth$_lan"
			[ "bn_precheck" = "$_stage" ] || test_flag_set PTB_NETWORK_CRC $TEST_FAILED
			bn_error_exit
		fi

		[ $_sleep_time -gt 0 ] && sleep $_sleep_time
	done
}

bn_stage2_links_detect()
{
	while ! bn_stage2_should_stop; do
		bn_links_detect_common 10 "bn_stage2"
	done
}

bn_stage2_self_loop_test()
{
	# do self loop socket transfer test between eth$1 and eth$2
	local _lan1=$1
	local _lan2=$2
	local _port=5566
	local _transfer_ok="yes"
	local _retry_connection=5

	/usr/sbin/socket_server ${IP1} ${_port} &
	/usr/sbin/socket_server ${IP2} ${_port} &

	while ! bn_stage2_should_stop; do
		_transfer_ok="yes"
		_retry_connection=5

		while [ $_retry_connection -gt 0 ]; do
			/usr/sbin/socket_client ${IP3} ${_port}
			if [ $? -ne 0 ]; then
				_transfer_ok="no"
			fi
			/usr/sbin/socket_client ${IP4} ${_port}
			if [ $? -ne 0 ]; then
				_transfer_ok="no"
			fi

			if [ "yes" = "$_transfer_ok" ]; then
				break
			else
				_retry_connection=$(($_retry_connection - 1))
				/usr/bin/killall socket_server > /dev/null 2>&1
				/usr/bin/killall socket_client > /dev/null 2>&1
				sleep 10
				bn_stage2_self_loop_network_setup ${_lan1} ${_lan2}
				/usr/sbin/socket_server ${IP1} ${_port} &
				/usr/sbin/socket_server ${IP2} ${_port} &
			fi
		done

		if [ "yes" != "$_transfer_ok" ]; then
			/usr/bin/killall socket_server > /dev/null 2>&1
			/usr/bin/killall socket_client > /dev/null 2>&1
			bn_log "Traffic transfer error between eth${_lan1} and eth${_lan2}"
			test_flag_set PTB_NETWORK_CRC $TEST_FAILED
			bn_error_exit
		fi
	done
}

bn_precheck_server()
{
	bn_log "Check B1 Server Environment"
	local exit_counter=3
	local _lan=`echo $bn_stage2_wget_list | cut -d' ' -f1`
	local wget_ret=1
	while [ 1 ]
	do
		if [ ${exit_counter} -le 0 ] ; then
			bn_log "ping B1 Server not found!\n"
			bn_error_exit
		fi

		if ping -c1 -I eth$_lan "${burnin_server_ipaddr}" >/dev/null 2>&1; then
			ip route add 169.254.1.0/24 dev eth$_lan
			wget http://${burnin_server_ipaddr}/testing -O /dev/null > /dev/null 2>&1
			wget_ret=$?
			ip route delete 169.254.1.0/24
			if [ ${wget_ret} -eq 0 ]; then
				break;
			fi
		fi

		exit_counter=$(($exit_counter - 1))
		sleep 3
	done
}

bn_precheck_f401()
{
	local Model="`get_key_value /etc.defaults/synoinfo.conf unique`"

	if [ "$Model" != "synology_monaco_ds216play" -a \
		 "xyes" != "x${is_rtd_mem_factorymodel}" ]; then
		bn_log "This model doesn't need f401 check"
		return
	fi

	if [ "xyes" = "x${is_rtd_mem_factorymodel}" ]; then
		if [ "f401" = "`cat /sys/bus/usb/devices/usb*/*/idVendor`" ] && [ "f401" = "`cat /sys/bus/usb/devices/usb*/*/idProduct`" ]; then
			return
		else
			bn_log "no f401 found ! skip B1\n"
			bn_error_exit
		fi
	fi

	#whether something is plugged on USB3 port
	if [ ! -e "/sys/bus/usb/devices/usb3/3-1/idVendor" ] || [ ! -e "/sys/bus/usb/devices/usb3/3-1/idProduct" ]; then
		bn_log "f401 not found ! Skip B1"
		bn_error_exit
	fi

	#whether f401 is plugged on USB3 port
	if [ "f401" = "`cat /sys/bus/usb/devices/usb3/3-1/idVendor`" ] && [ "f401" = "`cat /sys/bus/usb/devices/usb3/3-1/idProduct`" ]; then
		bn_log "f401 found ! Proceed B1"
	else
		bn_log "f401 not found ! Skip B1"
		bn_error_exit
	fi
}

bn_precheck_network()
{
	bn_log "Links detect"
	bn_links_detect_common 0 "bn_precheck"

	bn_log "Set up network interfaces"
	bn_network_setup

	bn_log "Check network by ping"
	bn_check_network_by_ping
}

bn_precheck()
{
	bn_precheck_server

	bn_copy_log_deamon &

	if [ "xyes" != "x$bn_skip_flag_checking" -a "Pass" != "$(test_flag_get PTF_COMPLETE)" ]; then
		bn_log "T1 PTF_COMPLETE not Pass!"
		bn_error_exit
	fi
	bn_precheck_f401
	bn_precheck_network
}

bn_remaining_force_stop()
{
	killall memtester >/dev/null 2>&1
	kill -TERM `cat ${BN_LOCK}`
	rm ${BN_LOCK}

	if [ "0" != `ls /tmp |grep -c burnin` ]; then
		bn_copy_log_to_server
	else
		bn_log "B1 not started"
		return 0
	fi

	for p in /tmp/burnin.*; do
		if [ -f "$p" ]; then
			kill -9 $(echo $p | cut -d'.' -f2) >/dev/null 2>&1
			rm -f $p
		fi
	done
}

bn_usage()
{
	cat <<USAGE
`basename $0`
	-k		skip memtester
	-s		show status
	-t MIN		assign duration of stage2
	-q		be more silent
	-f		force stop remaining sub-processes in background
USAGE
}

# remote command will get script from remote ip and run it.
# burnin_test will not be exec if remote command exec successfully
remote_command()
{
	local remote_exec_failed=1
	/usr/sbin/remote_command.sh
	remote_exec_failed=$?
	if [ "0" -eq "${remote_exec_failed}" ] ; then
		exit 0
	fi
}

bn_mount_log_dir()
{
	local burnin_server_log_nfs=""

	ping ${burnin_server_ipaddr} -c 1 -W 1
	if [ $? -ne 0 ]; then
		bn_log "ping server fail, do not copy log"
		return 1
	fi

	if [ ! -d ${burninlog_mnt} ]; then
		mkdir -p ${burninlog_mnt}
	fi

	if grep -qs ${burninlog_mnt} /proc/mounts; then
		umount ${burninlog_mnt}
	fi

	burnin_server_log_nfs=${bn_new_log_folder}
	mount ${burnin_server_ipaddr}:${burnin_server_log_nfs} ${burninlog_mnt}
	if [ $? -eq 0 ]; then
		bn_log "Use Log Folder: ${burnin_server_log_nfs}"
		return 0
	fi

	burnin_server_log_nfs="/volume1/log"
	mount ${burnin_server_ipaddr}:${burnin_server_log_nfs} ${burninlog_mnt}
	if [ $? -ne 0 ]; then
		bn_log "Error, mount server fail!"
		return 1
	fi
	bn_log "Use Log Folder: ${burnin_server_log_nfs}"
	return 0
}

bn_umount_log_dir()
{
	if grep -qs ${burninlog_mnt} /proc/mounts; then
		umount ${burninlog_mnt}
	fi
}

bn_timestamp()
{
	local upTime=`cat /proc/uptime`
	local lastUpdateTime=`date +"%Y%m%d%H%M%S"`
	echo "startTime:${startTime} lastUpdateTime:${lastUpdateTime} upTime:${upTime}"
}

bn_get_log_format()
{
	local result=$1
	if grep -qs ${bn_new_log_folder} /proc/mounts; then
		#Format will be like "B1_1860R4R9W4PK7_YYYYMMDDHHMMSS_ServerName_Fail_xxx.txt"
		echo ${label}_${product_serial}_${startTime}_${bn_server_name}_${result}
	else
		#Format will be like "B1_ServerName_1860R4R9W4PK7_YYYYMMDDHHMMSS_xxx.txt"
		echo ${label}_${bn_server_name}_${product_serial}_${startTime}
	fi
}

bn_copy_log_to_server()
{
	local model=`get_model_name`
	local log_dir=`get_log_dir`

	if [ -z ${bn_server_name} ]; then
		return 1
	fi

	if ! grep -qs ${burninlog_mnt} /proc/mounts; then
		bn_mount_log_dir
	fi

	local formatFail=$(bn_get_log_format "fail")
	if [ ! -d ${log_dir} ]; then
		mkdir -m 755 -p ${log_dir}
		chmod 755 ${burninlog_mnt}/${model}
	fi
	cp /var/log/messages ${log_dir}/${formatFail}_messages.txt
	bn_timestamp >> ${log_dir}/${formatFail}_messages.txt
	dmesg > ${log_dir}/${formatFail}_dmesg.txt
	[ ! -e ${FLAG_LIST_TMP} ] && mantool -test_flag_list >> ${FLAG_LIST_TMP}
	cp ${FLAG_LIST_TMP} ${log_dir}/${formatFail}_flag.txt

	return 0
}

bn_rename_log_to_pass()
{
	local model=`get_model_name`
	local log_dir=`get_log_dir`

	if [ -z ${bn_server_name} ]; then
		return 1
	fi

	local formatFail=$(bn_get_log_format "fail")
	local formatPass=$(bn_get_log_format "pass")
	if [ ! -d ${log_dir} ]; then
		mkdir -m 755 -p ${log_dir}
		chmod 755 ${burninlog_mnt}/${model}
	fi
	mv ${log_dir}/${formatFail}_messages.txt ${log_dir}/${formatPass}_messages.txt
	mv ${log_dir}/${formatFail}_dmesg.txt ${log_dir}/${formatPass}_dmesg.txt
	mv ${log_dir}/${formatFail}_flag.txt ${log_dir}/${formatPass}_flag.txt
	return 0
}

bn_copy_log_deamon()
{
	bn_mount_log_dir
	if [ $? -ne 0 ]; then
		return 1
	fi

	if [ ! -z ${bn_server_name} ]; then
		bn_copy_log_to_server
		while ! bn_stage2_fail_or_unknown_or_complete; do
			sleep 60
			bn_copy_log_to_server
		done

		# the last time log upload
		sleep 10
		bn_copy_log_to_server
		if [ "$BN_STATUS_COMPLETE" -eq "$(bn_status_get)" ]; then
			bn_rename_log_to_pass
		fi
	fi

	bn_umount_log_dir
}

bn_stage2_periodic_check()
{
	while ! bn_stage2_should_stop; do
		[ 0 -ne `grep -c "Machine Check Exception" /var/log/messages` ] && bn_log "encouter Machine Check Exception!" && bn_error_exit
		[ 0 -ne `grep -c "reset button pressed" /var/log/messages` ] && bn_log "reset button pressed!" && bn_error_exit
		sleep 10
	done
}

while getopts ":rkst:qfh" opt; do
	case "$opt" in
		r)
			# run remote command
			enable_remote_command=1
			;;
		k)
			bn_stage1_skip="yes"
			;;
		s)
			bn_status_show
			exit 0
			;;
		t)
			bn_stage2_duration=$OPTARG
			;;
		q)
			bn_debug="no"
			;;
		d)
			bn_debug="yes"
			;;
		f)
			bn_remaining_force_stop
			exit 0
			;;
		h)
			bn_usage
			exit 0
			;;
		\?)
			echo "Invalid option: -$OPTARG" >&2
			exit 1
			;;
		:)
			echo "Option -$OPTARG requires an argument." >&2
			exit 1
			;;
	esac
done

bn_lock

if bn_stage1_is_bootloader_support && bn_stage1_is_bootloader_mtest_tested; then
	bn_stage1_get_bootloader_mtest_ret
	/usr/syno/bin/mantool -reset_mtest
fi

if [ 1 -eq $enable_remote_command ]; then
    remote_command
fi

# download $BN_CONF in remote_command, so get_key_value after download complete
[ -z $bn_debug ] && bn_debug=$(get_key_value $BN_CONF bn_debug)
[ -z $bn_stage1_skip ] && bn_stage1_skip=$(get_key_value $BN_CONF bn_stage1_skip)
[ -z $bn_continue_after_pass ] && bn_continue_after_pass=$(get_key_value $BN_CONF bn_continue_after_pass)
[ -z $bn_server_name ] && bn_server_name=$(get_key_value $BN_CONF bn_server_name)
[ -z $bn_skip_flag_checking ] && bn_skip_flag_checking=$(get_key_value $BN_CONF bn_skip_flag_checking)
[ -z $bn_stage2_duration ] && bn_stage2_duration_adjust

bn_init
bn_precheck
bn_stage1
bn_stage2
bn_completed

