#!/hint/bash # Copyright © Tavian Barnes # SPDX-License-Identifier: 0BSD ## Utility functions # Portable realpath(1) _realpath() ( cd "$(dirname -- "$1")" echo "$PWD/$(basename -- "$1")" ) # Globals TESTS=$(_realpath "$TESTS") if [ "${BUILDDIR-}" ]; then BIN=$(_realpath "$BUILDDIR/bin") else BIN=$(_realpath "$TESTS/../bin") fi MKSOCK="$BIN/tests/mksock" 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 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 &1 {DUPERR}>&2 } # Drop root priviliges 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 <&2 <&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 }