#!/bin/bash
#-
# Copyright (c) 2011 UPLEX, Nils Goroll
# 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 AUTHOR 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 AUTHOR 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.
#
# $Id$
#
# simple test-tool for the ports acceptor

typeset -r mydir=$(dirname $0)

typeset -ri TRACE=${TRACE=0}
typeset -r PRIVILIGED_USER=${PRIVILIGED_USER=root}
typeset -r UNPRIVILIGED_USER=${UNPRIVILIGED_USER=nobody}

typeset BASE
if [[ ${mydir} == /* ]] ; then
    BASE=${mydir}
else
    BASE=$PWD/${mydir}
fi
typeset -r BASE
typeset -r VARNISH=$BASE/varnishd

check_rctl() {
    typeset -r rctl="$1"; shift;
    typeset -r priv="$1"; shift;
    typeset -ri val="$1"; shift;
    typeset -ri pid="$1"; shift;

    typeset -r out=$(prctl -P -t "${priv}" -n "${rctl}" ${pid} | tail -1)

    if [[ -z "${out}" ]] ; then
	if [[ ${val} -eq 0 ]] ; then
	    return 0
	else
	    echo check_rctl "${rctl}" "${priv}": expected "${val}", got unset
	    return 1
	fi
    fi

    set -- ${out}

    typeset -r aval="$3"

    if [[ ${val} -eq 0 ]] ; then
	echo check_rctl "${rctl}" "${priv}": expected unset, got "${aval}"
	return 1
    fi

    if [[ ${val} -ne ${aval} ]] ; then
	echo check_rctl "${rctl}" "${priv}": expected ${val}, got "${aval}"
	return 1
    fi

    return 0
}

set_rctl() {
    typeset -r rctl="$1"; shift;
    typeset -r priv="$1"; shift;
    typeset -r val="$1"; shift;

    if [[ ${val} -eq 0 ]] ; then
	# (try to) remove the resource control
	prctl -x -t "${priv}" -n "${rctl}" $$ 2>/dev/null
	typeset -ri ret=$?

	if [[ ${ret} -ne 0 ]] ; then
	    echo set_rctl "${rctl}" "${priv}" unset failed: $ret
	    return $ret
	fi

	return 0
    fi

    prctl -r -t "${priv}" -n "${rctl}" -v "${val}" $$ 2>/dev/null || \
	prctl -s -t "${priv}" -n "${rctl}" -v "${val}" $$ 2>/dev/null
    typeset -ri ret=$?

    if [[ ${ret} -ne 0 ]] ; then
	echo set_rctl "${rctl}" "${priv}" "${val}" failed: $ret
	return $ret
    fi

    return 0
}

test_nevents_run() {
    typeset -r user="$1"; shift;
    typeset -r name="$1"; shift;

    typeset -r set_nfd_priv="$1"; shift;
    typeset -r set_nfd_basic="$1"; shift;
    typeset -r set_nev_priv="$1"; shift;
    typeset -r set_nev_basic="$1"; shift;

    typeset -r exp_nfd_priv="$1"; shift;
    typeset -r exp_nfd_basic="$1"; shift;
    typeset -r exp_nev_priv="$1"; shift;
    typeset -r exp_nev_basic="$1"; shift;

    typeset -i err=0;

    set_rctl process.max-file-descriptor privileged ${set_nfd_priv}
    set_rctl process.max-file-descriptor basic ${set_nfd_basic}
    set_rctl process.max-port-events privileged ${set_nev_priv}
    set_rctl process.max-port-events basic ${set_nev_basic}

    pfexec ${VARNISH} -F -b 127.0.0.1:80 -p acceptor=ports -u ${user} &
    typeset -r ppid=$!

    sleep 1
    while kill -0 $ppid && ! pgrep -P $ppid >/dev/null ; do
	echo waiting for varnish child
	sleep 2
    done

    if ! kill -0 $ppid ; then
	echo varnish parent died
	return 1
    fi


    # XXX: give child time to init the acceptor - can we find a better way?
    #sleep 2
    typeset -r cpid=$(pgrep -P $ppid)

    prctl -n process.max-file-descriptor $cpid
    prctl -n process.max-port-events $cpid

    check_rctl process.max-file-descriptor privileged ${exp_nfd_priv} ${cpid}
    err=$((err + $?))
    check_rctl process.max-file-descriptor basic ${exp_nfd_basic} ${cpid}
    err=$((err + $?))
    check_rctl process.max-port-events privileged ${exp_nev_priv} ${cpid}
    err=$((err + $?))
    check_rctl process.max-port-events basic ${exp_nev_basic} ${cpid}
    err=$((err + $?))

    kill $ppid $cpid
    wait
	
    return ${err}
}

test_nevents() {
    typeset -ri expect_errs="$1"; shift;
    typeset -r user="$1"
    typeset -r name="$2"

    typeset -r tmpfile=/tmp/cache_acceptor_ports_test.out.$$
    typeset -i err=$?

    if [[ ${TRACE} -eq 1 ]] ; then
	echo
	echo "BEGIN" Test $user $name
	echo ---------------------------------------------
	test_nevents_run "$@"
	if [[ ${err} -eq ${expect_errs} ]] ; then
	    echo "OK :" Test $user $name
	else
	    echo "ERR:" Test $user $name - ${err} errors, expected ${expect_errs}
	    echo ----------------------------------------------------------------
	    echo
	fi
    else
	rm -f "${tmpfile}" 2>/dev/null
	test_nevents_run "$@" >${tmpfile} 2>&1
	typeset -i err=$?

	if [[ ${err} -eq ${expect_errs} ]] ; then
	    echo "OK :" Test $user $name
	else
	    echo "ERR:" Test $user $name - ${err} errors, expected ${expect_errs}
	    echo ----------------------------------------------------------------
	    cat ${tmpfile}
	    echo
	fi
    fi
    return ${err}
}

## new tests

test_nevents 0 ${PRIVILIGED_USER} "CASE4: raise priv limit replace" \
	 512	 512	 256	   0 \
	 512	 512	 512	   0


## OLD tests - OK

#     expect_err user name
# 
#       nfd	nfd	nev	nev
# set	priv	basic	priv	basic
# exp	priv	basic	priv	basic

test_nevents 4 ${PRIVILIGED_USER} "check test logic" \
	 512	 256	 512	 256 \
	 511	 255	 513	 257

test_nevents 0 ${PRIVILIGED_USER} "CASE1: all nice" \
	 512	 256	 512	 256 \
	 512	 256	 512	 256

test_nevents 0 ${PRIVILIGED_USER} "CASE2: lower basic limit replace" \
	 512	 256	 512	 512 \
	 512	 256	 512	 256

test_nevents 0 ${PRIVILIGED_USER} "CASE3: lower basic limit insert" \
	 512	 256	 512	   0 \
	 512	 256	 512	 256

test_nevents 0 ${PRIVILIGED_USER} "CASE4: raise priv limit replace" \
	 512	 512	 256	   0 \
	 512	 512	 512	   0

test_nevents 0 ${PRIVILIGED_USER} "CASE5: raise basic limit replace" \
	 512	 512	 512	 256 \
	 512	 512	 512	 512


