1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
#!/hint/bash
# Copyright © Tavian Barnes <tavianator@tavianator.com>
# SPDX-License-Identifier: 0BSD
## Utility functions
# Portable realpath(1)
_realpath() (
cd "$(dirname -- "$1")"
echo "$PWD/$(basename -- "$1")"
)
# Globals
ROOT=$(_realpath "$(dirname -- "$TESTS")")
TESTS="$ROOT/tests"
BIN="$ROOT/bin"
MKSOCK="$BIN/tests/mksock"
PTYX="$BIN/tests/ptyx"
XTOUCH="$BIN/tests/xtouch"
UNAME=$(uname)
# Standardize the environment
stdenv() {
export LC_ALL=C
export TZ=UTC0
local SAN_OPTIONS="abort_on_error=1:halt_on_error=1:log_to_syslog=0"
export ASAN_OPTIONS="$SAN_OPTIONS"
export LSAN_OPTIONS="$SAN_OPTIONS"
export MSAN_OPTIONS="$SAN_OPTIONS"
export TSAN_OPTIONS="$SAN_OPTIONS"
export UBSAN_OPTIONS="$SAN_OPTIONS"
export LS_COLORS=""
unset BFS_COLORS
unset LSCOLORS
if [ "$UNAME" = Darwin ]; then
# ASan on macOS likes to report
#
# malloc: nano zone abandoned due to inability to preallocate reserved vm space.
#
# to syslog, which as a side effect opens a socket which might take the
# place of one of the standard streams if the process is launched with
# it closed. This environment variable avoids the message.
export MallocNanoZone=0
fi
# Count the inherited FDs
if [ -d /proc/self/fd ]; then
local fds=/proc/self/fd
else
local fds=/dev/fd
fi
# We use ls $fds on purpose, rather than e.g. ($fds/*), to avoid counting
# internal bash fds that are not exposed to spawned processes
NOPENFD=$(ls -1q "$fds/" 2>/dev/null | wc -l)
NOPENFD=$((NOPENFD > 3 ? NOPENFD - 1 : 3))
# Close stdin so bfs doesn't think we're interactive
# dup() the standard fds for logging even when redirected
exec </dev/null {DUPOUT}>&1 {DUPERR}>&2
# Get the ttyname
if [ -t $DUPOUT ]; then
TTY=$(tty <&$DUPOUT)
elif [ -t $DUPERR ]; then
TTY=$(tty <&$DUPERR)
else
TTY=
fi
}
# Drop root privileges or bail
drop_root() {
if command -v capsh &>/dev/null; then
if capsh --has-p=cap_dac_override &>/dev/null || capsh --has-p=cap_dac_read_search &>/dev/null; then
if [ -n "${BFS_TRIED_DROP:-}" ]; then
color cat >&2 <<EOF
${RED}error:${RST} Failed to drop capabilities.
EOF
exit 1
fi
color cat >&2 <<EOF
${YLW}warning:${RST} Running as ${BLD}$(id -un)${RST} is not recommended. Dropping ${BLD}cap_dac_override${RST} and
${BLD}cap_dac_read_search${RST}.
EOF
BFS_TRIED_DROP=y exec capsh \
--drop=cap_dac_override,cap_dac_read_search \
--caps=cap_dac_override,cap_dac_read_search-eip \
-- "$0" "$@"
fi
elif ((EUID == 0)); then
UNLESS=
if [ "$UNAME" = "Linux" ]; then
UNLESS=" unless ${GRN}capsh${RST} is installed"
fi
color cat >&2 <<EOF
${RED}error:${RST} These tests expect filesystem permissions to be enforced, and therefore
will not work when run as ${BLD}$(id -un)${RST}${UNLESS}.
EOF
exit 1
fi
}
## Debugging
# Get the bash call stack
callers() {
local frame=0
while caller $frame; do
((++frame))
done
}
# Print a message including path, line number, and command
debug() {
local file="$1"
local line="$2"
local msg="$3"
local cmd="$(awk "NR == $line" "$file" 2>/dev/null)" || :
file="${file/#*\/tests\//tests/}"
color printf "${BLD}%s:%d:${RST} %s\n %s\n" "$file" "$line" "$msg" "$cmd"
}
## Deferred cleanup
# Quote a command safely for eval
quote() {
printf '%q' "$1"
shift
if (($# > 0)); then
printf ' %q' "$@"
fi
}
DEFER_LEVEL=-1
# Run a command when this (sub)shell exits
defer() {
# Check if the EXIT trap is already set
if ((DEFER_LEVEL != BASH_SUBSHELL)); then
DEFER_LEVEL=$BASH_SUBSHELL
DEFER_CMDS=()
DEFER_LINES=()
DEFER_FILES=()
trap pop_defers EXIT
fi
DEFER_CMDS+=("$(quote "$@")")
local line file
read -r line file < <(caller)
DEFER_LINES+=("$line")
DEFER_FILES+=("$file")
}
# Pop a single command from the defer stack and run it
pop_defer() {
local cmd="${DEFER_CMDS[-1]}"
local file="${DEFER_FILES[-1]}"
local line="${DEFER_LINES[-1]}"
unset "DEFER_CMDS[-1]"
unset "DEFER_FILES[-1]"
unset "DEFER_LINES[-1]"
local ret=0
eval "$cmd" || ret=$?
if ((ret != 0)); then
debug "$file" $line "${RED}error $ret${RST}" >&$DUPERR
fi
return $ret
}
# Run all deferred commands
pop_defers() {
local ret=0
while ((${#DEFER_CMDS[@]} > 0)); do
pop_defer || ret=$?
done
return $ret
}
## Parallelism
# Get the number of processors
_nproc() {
{
nproc \
|| sysctl -n hw.ncpu \
|| getconf _NPROCESSORS_ONLN \
|| echo 1
} 2>/dev/null
}
# Run wait, looping if interrupted
_wait() {
local ret=130
# "If wait is interrupted by a signal, the return status will be greater than 128"
while ((ret > 128)); do
ret=0
wait "$@" || ret=$?
done
return $ret
}
|