#!/bin/bash # Authors: # Daniel Kahn Gillmor # Thomas Klute set -e testid="${1##t-}" if [ -z "$testid" ] ; then echo -e "No test case selected.\nUsage: ${0} t-N" >&2 exit 1 else testid=${srcdir}/tests/"$(printf "%02d" "$testid")"_* fi BADVARS=0 for v in APACHE2 TEST_HOST TEST_IP TEST_PORT TEST_QUERY_DELAY TEST_MSVA_WAIT \ MSVA_PORT TEST_LOCK; do if [ ! -v "$v" ]; then printf "You need to set the %s environment variable\n" "$v" >&2 BADVARS=1 fi done if [ 0 != "$BADVARS" ]; then exit 1 fi # write script file and line to stderr on error function pinpoint_error() { echo "${1} failed at line ${2}!" >&2 } trap 'pinpoint_error ${BASH_SOURCE} ${LINENO}' ERR function stop_msva() { kill_by_pidfile "${msva_pidfile}" unset msva_pidfile } # Compare expected/actual outputs, filtering out headers from actual # output that are expected to change between runs or builds (currently # "Date" and "Server"). The headers must be excluded in the expected # output. # # Parameters: # $1: path to expected output # $2: path to actual output # $3: additional options for diff (optional) function diff_output_filter_headers() { local expected="$1" local actual="$2" diff $3 -u "${expected}" <( cat "${actual}" | \ grep -v -P '^Date:\s.*GMT\s?$' | \ grep -v -P '^Server:\sApache' | \ tail -n "$(wc -l < ${expected})" ) } # Run a command, storing its PID in the given file # Usage: run_with_pidfile PIDFILE COMMAND [ARGS] function run_with_pidfile() { local pidfile=$1 local cmd=$2 shift 2 echo $BASHPID >${pidfile} exec ${cmd} $* } # Kills the process with the PID contained in a given file, then # deletes the file. # Usage: kill_by_pidfile PIDFILE function kill_by_pidfile() { local pidfile="${1}" # In some testcases with expected failure, gnutls-cli sometimes # failed before the subshell in front of the pipe (see gnutls-cli # call below) got so far as to write the PID, much less exec # sleep. So we need to check if there actually is anything to # kill. if [ -n "${pidfile}" ]; then local pid=$(cat "${pidfile}") if [ -n "${pid}" ] && ps -p "${pid}"; then kill "${pid}" fi rm "${pidfile}" fi } function apache_down_err() { printf "FAILURE: %s\n" "$TEST_NAME" ${APACHE2} -f "${t}/apache.conf" -k stop || true if [ -e output ]; then printf "\ngnutls-cli outputs:\n" diff_output_filter_headers "output" "$output" || true fi if [ -n "${sleep_pidfile}" ]; then kill_by_pidfile "${sleep_pidfile}" fi local errlog="logs/${TEST_NAME}.error.log" if [ -r "${errlog}" ]; then printf "\nApache error logs:\n" tail "${errlog}" fi if [ -n "${USE_MSVA}" ]; then stop_msva fi } if [ -n "${USE_MSVA}" ]; then msva_pidfile="$(mktemp mod_gnutls_test-XXXXXX.pid)" GNUPGHOME=msva.gnupghome MSVA_KEYSERVER_POLICY=never run_with_pidfile "${msva_pidfile}" monkeysphere-validation-agent & trap stop_msva EXIT printf "TESTING: initial MSVA verification\n" # set to 0 if MSVA is up ret=1 export MONKEYSPHERE_VALIDATION_AGENT_SOCKET="http://127.0.0.1:$MSVA_PORT" # convert TEST_MSVA_WAIT to seconds because that's what "sleep" expects TEST_MSVA_SLEEP="$((${TEST_MSVA_WAIT} / 1000)).$((${TEST_MSVA_WAIT} % 1000))" # wait at most TEST_MSVA_MAX_WAIT milliseconds for MSVA to get ready waited=0 until [ ${ret} -eq 0 ] \ || [ ${waited} -ge ${TEST_MSVA_MAX_WAIT} ]; do if msva-query-agent https "$(cat client.uid)" x509pem client < client/x509.pem then ret=0 else echo "MSVA not ready yet" fi sleep "${TEST_MSVA_SLEEP}" waited=$((${waited} + ${TEST_MSVA_WAIT})) done # check if MSVA is up, fail if not if [ ${ret} -eq 0 ]; then printf "\nSUCCESS: initial MSVA verification\n" else printf "\nFAIL: initial MSVA verification\n" exit 1 fi fi # configure locking for the Apache process flock_cmd="flock -w ${TEST_LOCK_WAIT} $(realpath ${TEST_LOCK})" t="$(realpath ${testid})" export srcdir="$(realpath ${srcdir})" export TEST_NAME="$(basename "$t")" output="outputs/${TEST_NAME}.output" rm -f "$output" if [ -e ${t}/fail.* ]; then EXPECTED_FAILURE="$(printf " (expected: %s)" fail.*)" else unset EXPECTED_FAILURE fi printf "TESTING: %s%s\n" "$TEST_NAME" "$EXPECTED_FAILURE" trap apache_down_err EXIT if [ -n "${USE_MSVA}" ]; then MONKEYSPHERE_VALIDATION_AGENT_SOCKET="http://127.0.0.1:$MSVA_PORT" \ ${flock_cmd} \ ${APACHE2} -f "${t}/apache.conf" -k start \ || [ -e "${t}/fail.server" ] else ${flock_cmd} \ ${APACHE2} -f "${t}/apache.conf" -k start \ || [ -e "${t}/fail.server" ] fi # PID file for sleep command (explanation below) sleep_pidfile="$(mktemp mod_gnutls_test-XXXXXX.pid)" # The sleep call keeps the pipe from the subshell to gnutls-cli # open. Without it gnutls-cli would terminate as soon as sed is # done, and not wait for a response from the server, leading to # failing tests. Sending sleep to the background allows the test # case to proceed instead of waiting for it to return. The sleep # process is stopped after gnutls-cli terminates. if (sed "s/__HOSTNAME__/${TEST_HOST}/" <${t}/input && \ run_with_pidfile "${sleep_pidfile}" sleep "${TEST_QUERY_DELAY}" &) | \ gnutls-cli -p "${TEST_PORT}" $(cat ${t}/gnutls-cli.args) "${TEST_HOST}" \ >"$output"; then if [ -e ${t}/fail* ]; then printf "%s should have failed but succeeded\n" "$(basename "$t")" >&2 exit 1 fi else if [ ! -e ${t}/fail* ]; then printf "%s should have succeeded but failed\n" "$(basename "$t")" >&2 exit 1 fi fi kill_by_pidfile "${sleep_pidfile}" unset sleep_pidfile if [ -e ${t}/output ] ; then diff_output_filter_headers "${t}/output" "$output" "-q" fi if [ -n "${USE_MSVA}" ]; then trap stop_msva EXIT else trap - EXIT fi ${APACHE2} -f "${t}/apache.conf" -k stop || [ -e ${t}/fail.server ] printf "SUCCESS: %s\n" "$TEST_NAME" if [ -n "${USE_MSVA}" ]; then stop_msva # Without explicitly resetting the trap function, it would be # called again on exit. Of course, we could just not stop MSVA and # let the trap do the work, but I think the code is easier to # understand like this. trap - EXIT fi