1# nbdkit 2# Common functions used by the tests. 3# @configure_input@ 4# Copyright (C) 2017-2020 Red Hat Inc. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are 8# met: 9# 10# * Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 13# * Redistributions in binary form must reproduce the above copyright 14# notice, this list of conditions and the following disclaimer in the 15# documentation and/or other materials provided with the distribution. 16# 17# * Neither the name of Red Hat nor the names of its contributors may be 18# used to endorse or promote products derived from this software without 19# specific prior written permission. 20# 21# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND 22# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 23# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR 25# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 28# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 29# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32# SUCH DAMAGE. 33 34# cleanup_fn f [args] 35# 36# A generic trap handling function. This runs the function or command 37# f plus optional args when the script exits for any reason. 38declare -a _cleanup_hook 39cleanup_fn () 40{ 41 _cleanup_hook[${#_cleanup_hook[@]}]="$@" 42} 43 44_run_cleanup_hooks () 45{ 46 local _status=$? _i 47 48 set +e 49 trap '' INT QUIT TERM EXIT ERR 50 echo $0: run cleanup hooks: exit code $_status 51 52 for (( _i = 0; _i < ${#_cleanup_hook[@]}; ++_i )); do 53 ${_cleanup_hook[_i]} 54 done 55 56 exit $_status 57} 58trap _run_cleanup_hooks INT QUIT TERM EXIT ERR 59 60# requires program [args] 61# 62# Check that ‘program [args]’ works. If not, skip the test. 63# For example to check that qemu-img is available, do: 64# 65# requires qemu-img --version 66requires () 67{ 68 ( "$@" ) </dev/null >/dev/null 2>&1 || { 69 echo "$0: ‘$*’ failed with error code $?" 70 echo "$0: test prerequisite is missing or not working" 71 exit 77 72 } 73} 74 75# qemu cannot connect to ::1 if IPv6 is disabled because of 76# the way it uses getaddrinfo. See: 77# https://bugzilla.redhat.com/show_bug.cgi?id=808147 78# https://lists.fedoraproject.org/archives/list/devel@lists.fedoraproject.org/thread/SXDLSZ3GKXL6NDAKP4MPJ25IMHKN67X3/ 79requires_ipv6_loopback () 80{ 81 requires qemu-img --version 82 83 # This should fail with "Connection refused". If IPv6 is broken 84 # then it fails with "Address family for hostname not supported" 85 # instead. It's very unlikely that port 1 is open. 86 if LANG=C qemu-img info "nbd:[::1]:1" |& \ 87 grep -sq "Address family for hostname not supported"; then 88 echo "$0: IPv6 loopback is not available, skipping this test" 89 exit 77 90 fi 91} 92 93# Test host kernel is Linux and minimum version. It's usually better 94# to test features rather than using this, but there are cases where 95# testing features of the current kernel is too hard. 96requires_linux_kernel_version () 97{ 98 local kver 99 100 # Test the kernel is Linux. 101 requires test "$(uname -s)" = "Linux" 102 103 # Test that it's the minimum version. 104 requires cut --version 105 requires bc --version 106 kver=$(uname -r | cut -d. -f1-2) 107 requires test "$(echo "$kver >= $1" | bc -l)" = "1" 108} 109 110# start_nbdkit -P pidfile args... 111# 112# Run nbdkit with args and wait for it to start up. If it fails to 113# start up, exit with an error message. Also a cleanup handler is 114# installed automatically which kills nbdkit on exit. 115start_nbdkit () 116{ 117 local _pidfile _i 118 119 # -P <pidfile> must be the first two parameters. 120 if [ "$1" != "-P" ]; then 121 echo "$0: start_nbdkit: -P <pidfile> option must be first" 122 exit 1 123 fi 124 _pidfile="$2" 125 126 # Run nbdkit. 127 nbdkit -v "$@" 128 129 # Wait for the pidfile to appear. 130 for _i in {1..60}; do 131 if test -s "$_pidfile"; then 132 break 133 fi 134 sleep 1 135 done 136 if ! test -s "$_pidfile"; then 137 echo "$0: start_nbdkit: PID file $_pidfile was not created" 138 exit 1 139 fi 140 141 # Kill nbdkit on exit. 142 cleanup_fn kill_nbdkit "$(cat "$_pidfile")" 143} 144 145# kill_nbdkit pid 146# 147# End the nbkdit process with the given pid. Exit this script with an 148# error if nbdkit does not gracefully shutdown in a timely manner. 149kill_nbdkit () 150{ 151 local pid=$1 i 152 153 # Start with SIGTERM, and wait for graceful exit 154 kill $pid 155 for i in {1..60}; do 156 if ! kill -0 $pid 2>/dev/null; then 157 break 158 fi 159 sleep 1 160 done 161 # If nbdkit has not exited, try SIGKILL and fail the test 162 if test $i = 60; then 163 echo "error: nbdkit pid $pid failed to respond to SIGTERM" 164 kill -9 $pid 165 # Append our failure after other cleanups 166 cleanup_fn exit 1 167 fi 168} 169 170# foreach_plugin f [args] 171# 172# For each plugin that was built, run the function or command f with 173# the plugin name as the first argument, optionally followed by the 174# remaining args. 175foreach_plugin () 176{ 177 local f d p 178 179 f="$1" 180 shift 181 182 for p in @plugins@; do 183 # Was the plugin built? 184 d="@top_builddir@/plugins/$p" 185 if [ -f "$d/.libs/nbdkit-$p-plugin.so" ] || 186 [ -f "$d/nbdkit-$p-plugin" ]; then 187 # Yes so run the test. 188 "$f" "$p" "$@" 189 fi 190 done 191} 192 193# pick_unused_port 194# 195# Picks and returns an "unused" port, setting the global variable 196# $port. 197# 198# This is inherently racy so we only use it where it's absolutely 199# necessary (eg. testing TLS because qemu cannot do TLS over a Unix 200# domain socket). 201pick_unused_port () 202{ 203 requires ss --version 204 205 # Start at a random port to make it less likely that two parallel 206 # tests will conflict. 207 port=$(( 50000 + (RANDOM%15000) )) 208 while ss -ltn | grep -sqE ":$port\b"; do 209 ((port++)) 210 if [ $port -eq 65000 ]; then port=50000; fi 211 done 212 echo picked unused port $port 213} 214