#	$NetBSD: t_simplehook.sh,v 1.1 2021/09/30 02:00:20 yamaguchi Exp $
#
# Copyright (c) 2021 Internet Initiative Japan Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#

DEBUG=${DEBUG:-false}
SOCK=unix://commsock
HIJACKING="env LD_PRELOAD=/usr/lib/librumphijack.so \
    RUMPHIJACK=path=/rump,socket=all:nolocal,sysctl=yes"

atf_sysctl_rd()
{
	local sysctl_key=$1
	local expected=$2

	atf_check -s exit:0 -o match:"$expected" \
	    rump.sysctl -n $sysctl_key
}

atf_sysctl_wr()
{
	local sysctl_key=$1
	local value=$2

	atf_check -s exit:0 -o ignore rump.sysctl -w $sysctl_key=$value
}

atf_sysctl_wait()
{
	local sysctl_key=$1
	local expected=$2
	local n=10
	local i
	local v

	for i in $(seq $n); do
		v=$(rump.sysctl -n $sysctl_key)
		if [ x"$v" = x"$expected" ]; then
			return
		fi
		sleep 0.5
	done

	atf_fail "Couldn't get the value for $n seconds."
}

atf_test_case simplehook_basic cleanup
simplehook_basic_head()
{

	atf_set "descr" "tests for basically functions of simplehook"
	atf_set "require.progs" "rump_server"
}


simplehook_basic_body()
{
	local key_hklist="kern.simplehook_tester.hook_list"
	local key_hk0="kern.simplehook_tester.hook0"
	local key_hk1="kern.simplehook_tester.hook1"

	rump_server -lrumpkern_simplehook_tester $SOCK

	export RUMP_SERVER=$SOCK

	$DEBUG && rump.sysctl -e kern.simplehook_tester
	atf_check -s exit:0 -o ignore rump.sysctl -e kern.simplehook_tester

	# create and destroy
	atf_sysctl_rd ${key_hklist}.created '0'
	atf_sysctl_wr ${key_hklist}.created '1'
	atf_sysctl_wr ${key_hklist}.created '0'
	$DEBUG && rump.sysctl -e kern.simplehook_tester

	# establish one hook
	atf_sysctl_wr ${key_hklist}.created '1'
	atf_sysctl_wr ${key_hk0}.established '1'
	atf_sysctl_wr ${key_hk0}.count '0'
	atf_sysctl_wr ${key_hklist}.dohooks '1'
	atf_sysctl_wr ${key_hklist}.dohooks '0'
	atf_sysctl_rd ${key_hk0}.count '1'
	atf_sysctl_wr ${key_hk0}.established '0'
	atf_sysctl_wr ${key_hklist}.created '0'

	# establish two hooks
	atf_sysctl_wr ${key_hklist}.created '1'
	atf_sysctl_wr ${key_hk0}.established '1'
	atf_sysctl_wr ${key_hk1}.established '1'
	atf_sysctl_wr ${key_hk0}.count '0'
	atf_sysctl_wr ${key_hk1}.count '0'

	atf_sysctl_wr ${key_hklist}.dohooks '1'
	atf_sysctl_wr ${key_hklist}.dohooks '0'
	atf_sysctl_rd ${key_hk0}.count '1'
	atf_sysctl_rd ${key_hk1}.count '1'

	atf_sysctl_wr ${key_hk0}.established '0'
	atf_sysctl_wr ${key_hk1}.established '0'
	atf_sysctl_wr ${key_hklist}.created '0'
}

simplehook_basic_cleanup()
{
	export RUMP_SERVER=$SOCK

	$DEBUG && rump.sysctl -e kern.simplehook_tester
	$DEBUG && $HIJACKING dmesg
	rump.halt
}

atf_test_case simplehook_disestablish cleanup
simplehook_disestablish_head()
{

	atf_set "descr" "tests for disestablish of simplehook"
	atf_set "require.progs" "rump_server"
}

simplehook_disestablish_body()
{
	local key_hklist="kern.simplehook_tester.hook_list"
	local key_hk0="kern.simplehook_tester.hook0"
	local key_hk1="kern.simplehook_tester.hook1"

	rump_server -lrumpkern_simplehook_tester $SOCK

	export RUMP_SERVER=$SOCK

	$DEBUG && rump.sysctl -e kern.simplehook_tester
	atf_check -s exit:0 -o ignore rump.sysctl -e kern.simplehook_tester

	#
	# disestablish on the running hook
	#
	atf_sysctl_wr ${key_hklist}.created '1'
	atf_sysctl_wr ${key_hk0}.established '1'
	atf_sysctl_wr ${key_hk0}.disestablish_in_hook '1'
	atf_sysctl_wr ${key_hklist}.dohooks '1'
	atf_sysctl_wr ${key_hklist}.dohooks '0'

	# already disestablished
	atf_sysctl_rd ${key_hk0}.established '0'
	atf_sysctl_wr ${key_hk0}.disestablish_in_hook '0'

	atf_sysctl_wr ${key_hklist}.created '0'

	#
	# disestablish called hook while doing other hook
	#
	atf_sysctl_wr ${key_hklist}.created '1'
	atf_sysctl_wr ${key_hk0}.established '1'
	atf_sysctl_wr ${key_hk1}.established '1'

	atf_sysctl_wr ${key_hk0}.count '0'
	atf_sysctl_wr ${key_hk1}.count '0'
	atf_sysctl_wr ${key_hk0}.stopping '1'

	# calls hook1 -> hook0
	atf_sysctl_wr ${key_hklist}.dohooks '1'

	# stop in hook0
	atf_sysctl_wait ${key_hk0}.stopped '1'

	atf_sysctl_rd ${key_hk1}.count '1'
	atf_sysctl_wr ${key_hk1}.established '0'

	# wakeup hook0
	atf_sysctl_wr ${key_hk0}.stopping '0'
	atf_sysctl_wr ${key_hklist}.dohooks '0'

	atf_sysctl_wr ${key_hk0}.established '0'
	atf_sysctl_wr ${key_hklist}.created '0'

	#
	# disestablish a hook in running hook list
	#
	atf_sysctl_wr ${key_hklist}.created '1'
	atf_sysctl_wr ${key_hk0}.established '1'
	atf_sysctl_wr ${key_hk1}.established '1'

	atf_sysctl_wr ${key_hk0}.count '0'
	atf_sysctl_wr ${key_hk1}.count '0'
	atf_sysctl_wr ${key_hk1}.stopping '1'

	# calls hook1 -> hook0
	atf_sysctl_wr ${key_hklist}.dohooks '1'

	# stop in hook1
	atf_sysctl_wait ${key_hk1}.stopped '1'

	atf_sysctl_wr ${key_hk0}.established '0'

	# wakeup hook1
	atf_sysctl_wr ${key_hk1}.stopping '0'
	atf_sysctl_wr ${key_hklist}.dohooks '0'

	# hook0 is not called
	atf_sysctl_rd ${key_hk0}.count '0'

	atf_sysctl_wr ${key_hk1}.established '0'
	atf_sysctl_wr ${key_hklist}.created '0'

	#
	# disestablish the running hook
	#
	atf_sysctl_wr ${key_hklist}.created '1'
	atf_sysctl_wr ${key_hk0}.established '1'
	atf_sysctl_wr ${key_hk0}.stopping '1'

	atf_sysctl_wr ${key_hklist}.dohooks '1'

	atf_sysctl_wait ${key_hk0}.stopped '1'
	atf_sysctl_wr ${key_hk0}.established '0'

	atf_sysctl_wr ${key_hklist}.dohooks '0'
	atf_sysctl_wr ${key_hklist}.created '0'
}

simplehook_disestablish_cleanup()
{
	export RUMP_SERVER=$SOCK

	$DEBUG && rump.sysctl -e kern.simplehook_tester
	$DEBUG && $HIJACKING dmesg
	rump.halt
}

atf_test_case simplehook_nolock cleanup
simplehook_nolock_head()
{

	atf_set "descr" "tests for hook that does not use lock in it"
	atf_set "require.progs" "rump_server"
}

simplehook_nolock_body()
{
	local key_hklist="kern.simplehook_tester.hook_list"
	local key_hk="kern.simplehook_tester.nbhook"

	rump_server -lrumpkern_simplehook_tester $SOCK

	export RUMP_SERVER=$SOCK
	$DEBUG && rump.sysctl -e kern.simplehook_tester
	atf_check -s exit:0 -o ignore rump.sysctl -e kern.simplehook_tester

	atf_sysctl_wr ${key_hklist}.created '1'
	atf_sysctl_wr ${key_hk}.established '1'

	atf_sysctl_wr ${key_hklist}.dohooks '1'
	atf_sysctl_wr ${key_hk}.established '0'
	atf_sysctl_wr ${key_hklist}.dohooks '0'

	atf_sysctl_wr ${key_hklist}.created '0'
}

simplehook_nolock_cleanup()
{
	export RUMP_SERVER=$SOCK

	$DEBUG && rump.sysctl -e kern.simplehook_tester
	$DEBUG && $HIJACKING dmesg
	rump.halt
}

atf_init_test_cases()
{

	atf_add_test_case simplehook_basic
	atf_add_test_case simplehook_disestablish
	atf_add_test_case simplehook_nolock
}
