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