# Exit status list
# shellcheck disable=SC2034
+EXIT_FAILURE=1
+# shellcheck disable=SC2034
EXIT_SKIP_TEST=77
# The list of the environment variables that tinc injects into the scripts it calls.
timeout() { gtimeout "$@"; }
fi
+# As usual, BSD tools require special handling, as they do not support -i without a suffix.
+# Note that there must be no space after -i, or it won't work on GNU sed.
+sed_cmd() {
+ sed -i.orig "$@"
+}
+
# Are the shell tools provided by busybox?
is_busybox() {
timeout --help 2>&1 | grep -q -i busybox
tr -d '\r'
}
+if is_windows; then
+ normalize_path() { cygpath --mixed -- "$@"; }
+else
+ normalize_path() { echo "$@"; }
+fi
+
# Executes whatever is passed to it, checking that the resulting exit code is non-zero.
must_fail() {
if "$@"; then
fi
}
+# Executes the passed command and checks two conditions:
+# 1. it must exit successfully (with code 0)
+# 2. its output (stdout + stderr) must include the substring from the first argument (ignoring case)
+# usage: expect_msg 'expected message' command --with --args
+expect_msg() {
+ message=$1
+ shift
+
+ if ! output=$("$@" 2>&1); then
+ bail 'expected 0 exit code'
+ fi
+
+ if ! echo "$output" | grep -q -i "$message"; then
+ bail "expected message '$message'"
+ fi
+}
+
+# The reverse of expect_msg. We cannot simply wrap expect_msg with must_fail
+# because there should be a separate check for tinc exit code.
+fail_on_msg() {
+ message=$1
+ shift
+
+ if ! output=$("$@" 2>&1); then
+ bail 'expected 0 exit code'
+ fi
+
+ if echo "$output" | grep -q -i "$message"; then
+ bail "unexpected message '$message'"
+ fi
+}
+
+# Like expect_msg, but the command must fail with a non-zero exit code.
+# usage: must_fail_with_msg 'expected message' command --with --args
+must_fail_with_msg() {
+ message=$1
+ shift
+
+ if output=$("$@" 2>&1); then
+ bail "expected a non-zero exit code"
+ fi
+
+ if ! echo "$output" | grep -i -q "$message"; then
+ bail "expected message '$message'"
+ fi
+}
+
+# Is the legacy protocol enabled?
+with_legacy() {
+ tincd foo --version | grep -q legacy_protocol
+}
+
+# Are we running with EUID 0?
+is_root() {
+ test "$(id -u)" = 0
+}
+
+# Executes whatever is passed to it, checking that the resulting exit code is equal to the first argument.
+expect_code() {
+ expected=$1
+ shift
+
+ code=0
+ "$@" || code=$?
+
+ if [ $code != "$expected" ]; then
+ bail "wrong exit code $code, expected $expected"
+ fi
+}
+
# Runs its arguments with timeout(1) or gtimeout(1) if either are installed.
# Usage: try_limit_time 10 command --with --args
if type timeout >/dev/null; then
}
peer_directory() {
+ peer=$1
case "$peer" in
foo) echo "$DIR_FOO" ;;
bar) echo "$DIR_BAR" ;;
# group by enabling job control, but this results in weird behavior when
# running tests in parallel on some interactive shells
# (e.g. when /bin/sh is symlinked to dash).
+ fifo=$(mktemp)
+ rm -f "$fifo"
+ mkfifo "$fifo"
+
+ # This weird thing is required to support old versions of ksh on NetBSD 8.2 and the like.
+ (tail -n +"$line" -f "$script_log" >"$fifo") &
+
new_line=$(
try_limit_time 60 sh -c "
- fifo=\$$.fifo
- cleanup() { rm -f \$fifo; }
- cleanup && trap cleanup EXIT
-
- mkfifo \$$.fifo
- tail -n '+$line' -f '$script_log' >\$fifo &
- grep -n -m '$count' '^$script,' <\$fifo
- kill \$!
+ grep -n -m $count '^$script,' <'$fifo'
" | awk -F: 'END { print $1 }'
)
+ # Try to stop the background tail, ignoring possible failure (some tails
+ # detect EOF, some don't, so it may have already exited), but do wait on
+ # it (which is required at least by old ksh).
+ kill $! || true
+ wait || true
+ rm -f "$fifo"
+
# Remember the next line number for future reference. We'll use it if
# wait_script is called again with same $peer and $script.
read -r "${line_var?}" <<EOF
) || true
}
+# If we're on a CI server, the test requires superuser privileges to run, and we're not
+# currently a superuser, try running the test as one and fail if it doesn't work (the
+# system must be configured to provide passwordless sudo for our user).
+require_root() {
+ if is_root; then
+ return
+ fi
+ if is_ci; then
+ echo "root is required for test $SCRIPTNAME, but we're a regular user; elevating privileges..."
+ if ! command -v sudo 2>/dev/null; then
+ bail "please install sudo and configure passwordless auth for user $USER"
+ fi
+ if ! sudo --preserve-env --non-interactive true; then
+ bail "sudo is not allowed or requires a password for user $USER"
+ fi
+ exec sudo --preserve-env "$@"
+ else
+ # Avoid these kinds of surprises outside CI. Just skip the test.
+ echo "root is required for test $SCRIPTNAME, but we're a regular user; skipping"
+ exit $EXIT_SKIP_TEST
+ fi
+}
+
# Generate path to current shell which can be used from Windows applications.
if is_windows; then
- MINGW_SHELL=$(cygpath --mixed -- "$SHELL")
+ MINGW_SHELL=$(normalize_path "$SHELL")
fi
# This was called from a tincd script. Skip executing commands with side effects.
# Cleanup leftovers from previous runs.
stop_all_tincs
-# On Windows this can actually fail. We don't want to suppress possible failure with -f.
-if [ -d "$DIR_FOO" ]; then rm -r "$DIR_FOO"; fi
-if [ -d "$DIR_BAR" ]; then rm -r "$DIR_BAR"; fi
-if [ -d "$DIR_BAZ" ]; then rm -r "$DIR_BAZ"; fi
+if [ -d "$DIR_FOO" ]; then rm -rf "$DIR_FOO"; fi
+if [ -d "$DIR_BAR" ]; then rm -rf "$DIR_BAR"; fi
+if [ -d "$DIR_BAZ" ]; then rm -rf "$DIR_BAZ"; fi
# Register cleanup function so we don't have to call it everywhere
# (and failed scripts do not leave stray tincd running).