wolfsshd: wait for the listener, claim the ports

start_wolfsshd returns once the daemon accepts connections, not
once it has written its pid: wolfSSHd saves the PID file just
before tcp_listen(), so a caller connecting straight away could be
refused while the daemon's log showed no connection at all. A run
also claims its port block rather than only probing it.

- wait for the daemon's own "Listening on port" line, matched on its pid
  so a previous daemon's line in the appended log cannot satisfy it
- take a block by creating its lock directory, which mkdir makes atomic,
  and release it in the teardown; probing alone let two runners pick the
  same block, and a claim whose owner is gone is treated as stale
- check that a pid from the PID file is still a wolfsshd, since kill -0
  answers only whether some process owns the number
- fail the OpenSSH cert test when mktemp gives it no work dir: an
  empty one reduced its teardown pattern to every wolfsshd present
pull/1256/head
John Safranek 2026-09-15 20:51:34 -07:00
parent ac532af87f
commit 347046f8fd
3 changed files with 67 additions and 2 deletions

View File

@ -96,12 +96,45 @@ port_in_use() {
(exec 3<>"/dev/tcp/127.0.0.1/$1") 2>/dev/null
}
# Where blocks are claimed. Sticky and world-writable like /tmp itself, so a
# run as the invoking user and a run under sudo can claim from the same pool.
PORT_LOCK_ROOT="${TMPDIR:-/tmp}/wolfssh-sshd-ports"
mkdir -p "$PORT_LOCK_ROOT" 2>/dev/null
chmod 1777 "$PORT_LOCK_ROOT" 2>/dev/null || true
# Take a block by creating its directory. mkdir is atomic, so of two runners
# racing for one block exactly one wins; probing alone could not do this,
# because both could see the same eight ports free before either had bound
# one. The claim is held for the whole run and released in run_teardown.
claim_port_block() {
local lock owner
lock="$PORT_LOCK_ROOT/$1"
if mkdir "$lock" 2>/dev/null; then
echo $$ > "$lock/pid" 2>/dev/null
return 0
fi
# A claim whose owner is gone is stale. Without this a run killed before
# its teardown would take its block out of circulation permanently. Two
# runners can reach this at once; the loser's mkdir just fails and it
# moves on to the next block.
owner=`cat "$lock/pid" 2>/dev/null`
if [ -n "$owner" ] && ! kill -0 "$owner" 2>/dev/null; then
rm -rf "$lock" 2>/dev/null
if mkdir "$lock" 2>/dev/null; then
echo $$ > "$lock/pid" 2>/dev/null
return 0
fi
fi
return 1
}
find_port_block() {
local start i n base busy
start=$(( $$ % PORT_BLOCK_COUNT ))
for (( i = 0; i < PORT_BLOCK_COUNT; i++ )); do
base=$(( PORT_BLOCK_FIRST \
+ ((start + i) % PORT_BLOCK_COUNT) * PORT_BLOCK_SIZE ))
claim_port_block "$base" || continue
busy=0
for (( n = 0; n < PORT_BLOCK_SIZE; n++ )); do
if port_in_use $(( base + n )); then
@ -113,11 +146,15 @@ find_port_block() {
printf '%s' "$base"
return 0
fi
# Claimed but unusable: something outside the suite holds a port in
# it. Give the claim back rather than sit on a block we cannot use.
rm -rf "$PORT_LOCK_ROOT/$base" 2>/dev/null
done
return 1
}
PORT_BASE=`find_port_block`
PORT_LOCK_DIR="$PORT_LOCK_ROOT/$PORT_BASE"
if [ -z "$PORT_BASE" ]; then
echo "Error: no free block of $PORT_BLOCK_SIZE ports found starting at" \
"$PORT_BLOCK_FIRST."
@ -171,6 +208,9 @@ run_teardown() {
stop_all_wolfsshd || true
fi
rm -f "$WOLFSSHD_TEST_PIDFILE" || true
if [ -n "$PORT_BASE" ]; then
rm -rf "$PORT_LOCK_DIR" || true
fi
}
trap run_teardown EXIT

View File

@ -49,6 +49,13 @@ command -v ssh-keygen >/dev/null 2>&1 || \
skip "ssh-keygen not found, skipping OpenSSH cert test"
WORK=$(mktemp -d)
# Checked before the trap below is installed: an empty WORK would reduce its
# pattern to "wolfsshd .*", which matches every wolfsshd on the machine and is
# exactly the host-wide teardown this suite no longer does.
if [ -z "$WORK" ] || [ ! -d "$WORK" ]; then
echo "FAIL: could not create a work directory for the OpenSSH cert test"
exit 1
fi
# Matched on $WORK, this run's own mktemp dir, not on the config basename:
# "sshd_config_ossh" appears in every concurrent run's command line too,
# so the basename pattern tore down another run's daemon along with this

View File

@ -134,8 +134,10 @@ EOF
sleep 0.1
done
# A pid file left by a daemon that has since died is worse than none:
# stop_wolfsshd would kill whatever has been given that pid since.
if [ -n "$PID" ] && ! sudo kill -0 "$PID" 2>/dev/null; then
# stop_wolfsshd would kill whatever has been given that pid since. Ask
# what the process is, not merely whether it exists: "kill -0" answers
# the second question only, and the pid may have been recycled.
if [ -n "$PID" ] && ! pgrep -x wolfsshd | grep -qx -- "$PID"; then
PID=""
fi
else
@ -157,6 +159,22 @@ EOF
sleep 0.1
done
fi
# wolfSSHd writes its PID file in StartSSHD() immediately before
# tcp_listen(), so the pid appearing does not mean the socket accepts yet.
# A caller that connects the moment this returns -- several do, with no
# sleep -- would be refused, and the daemon's log would show no connection
# at all. Wait for the daemon's own listening line, matched on its pid so
# that a previous daemon's line in this appended log cannot satisfy it.
if [ -n "$PID" ]; then
for i in $(seq 1 100); do
if sudo grep -qF "[PID $PID]: [SSHD] Listening on port" \
./log.txt 2>/dev/null; then
break
fi
sleep 0.1
done
fi
printf "SSHD running on PID $PID\n"
# Record the daemon in the run's registry, if the runner set one up. Test