fix(sync): inspect hidden server listeners via procfs

This commit is contained in:
Brad Stein 2026-04-28 01:33:17 -03:00
parent fbfa450e4a
commit a9aefd470c
6 changed files with 80 additions and 7 deletions

6
Cargo.lock generated
View File

@ -1642,7 +1642,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]] [[package]]
name = "lesavka_client" name = "lesavka_client"
version = "0.14.35" version = "0.14.36"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@ -1676,7 +1676,7 @@ dependencies = [
[[package]] [[package]]
name = "lesavka_common" name = "lesavka_common"
version = "0.14.35" version = "0.14.36"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",
@ -1688,7 +1688,7 @@ dependencies = [
[[package]] [[package]]
name = "lesavka_server" name = "lesavka_server"
version = "0.14.35" version = "0.14.36"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",

View File

@ -4,7 +4,7 @@ path = "src/main.rs"
[package] [package]
name = "lesavka_client" name = "lesavka_client"
version = "0.14.35" version = "0.14.36"
edition = "2024" edition = "2024"
[dependencies] [dependencies]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "lesavka_common" name = "lesavka_common"
version = "0.14.35" version = "0.14.36"
edition = "2024" edition = "2024"
build = "build.rs" build = "build.rs"

View File

@ -128,7 +128,59 @@ server_bind_port() {
list_server_listener_pids() { list_server_listener_pids() {
local port local port
port=$(server_bind_port) || return 1 port=$(server_bind_port) || return 1
sudo lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null | sort -u {
sudo lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null || true
list_server_listener_pids_proc "$port"
} | sed '/^$/d' | sort -u
}
list_server_listener_inodes_proc() {
local port=$1 hex_port
hex_port=$(printf '%04X' "$port")
sudo awk -v target="$hex_port" '
FNR == 1 { next }
$4 == "0A" {
split($2, local, ":")
if (toupper(local[2]) == target) print $10
}
' /proc/net/tcp /proc/net/tcp6 2>/dev/null | sed '/^$/d' | sort -u
}
list_server_listener_pids_proc() {
local port=$1
local -a inodes=()
mapfile -t inodes < <(list_server_listener_inodes_proc "$port")
if (( ${#inodes[@]} == 0 )); then
return 0
fi
sudo bash -s -- "${inodes[@]}" <<'EOF'
set -euo pipefail
inodes=("$@")
shopt -s nullglob
for fd in /proc/[0-9]*/fd/*; do
target=$(readlink "$fd" 2>/dev/null || true)
for inode in "${inodes[@]}"; do
if [[ "$target" == "socket:[$inode]" ]]; then
pid=${fd#/proc/}
pid=${pid%%/*}
printf '%s\n' "$pid"
break
fi
done
done | sort -u
EOF
}
server_listener_presence_detail() {
local port=$1
local -a inodes=()
mapfile -t inodes < <(list_server_listener_inodes_proc "$port")
if (( ${#inodes[@]} == 0 )); then
printf 'no /proc listener entries for :%s' "$port"
return 0
fi
printf 'listener inodes on :%s => %s' "$port" "${inodes[*]}"
} }
clear_stale_server_listener() { clear_stale_server_listener() {
@ -155,6 +207,10 @@ clear_stale_server_listener() {
return 1 return 1
fi fi
if [[ "$found" == "0" ]]; then if [[ "$found" == "0" ]]; then
if list_server_listener_inodes_proc "$port" | grep -q .; then
echo "❌ TCP :$port is listening but no owning PID could be identified; $(server_listener_presence_detail "$port")." >&2
return 1
fi
return 0 return 0
fi fi
@ -186,6 +242,11 @@ clear_stale_server_listener() {
sleep 0.1 sleep 0.1
done done
if list_server_listener_inodes_proc "$port" | grep -q .; then
echo "❌ TCP :$port still appears to be listening after cleanup; $(server_listener_presence_detail "$port")." >&2
return 1
fi
echo "❌ lesavka-server listener on :$port survived cleanup; refusing to start a duplicate server." >&2 echo "❌ lesavka-server listener on :$port survived cleanup; refusing to start a duplicate server." >&2
return 1 return 1
} }

View File

@ -10,7 +10,7 @@ bench = false
[package] [package]
name = "lesavka_server" name = "lesavka_server"
version = "0.14.35" version = "0.14.36"
edition = "2024" edition = "2024"
autobins = false autobins = false

View File

@ -114,10 +114,22 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
SERVER_INSTALL.contains("lsof -tiTCP:"), SERVER_INSTALL.contains("lsof -tiTCP:"),
"install script should inspect the bind port before starting a fresh server" "install script should inspect the bind port before starting a fresh server"
); );
assert!(
SERVER_INSTALL.contains("/proc/net/tcp"),
"install script should fall back to procfs listener detection when lsof misses the owner"
);
assert!(
SERVER_INSTALL.contains("socket:["),
"install script should map listening socket inodes back to owning PIDs"
);
assert!( assert!(
SERVER_INSTALL.contains("unexpected process"), SERVER_INSTALL.contains("unexpected process"),
"install script should fail loud instead of killing an unrelated process on the server port" "install script should fail loud instead of killing an unrelated process on the server port"
); );
assert!(
SERVER_INSTALL.contains("no owning PID could be identified"),
"install script should fail loud when a port is listening but procfs cannot identify the owner"
);
assert!( assert!(
SERVER_INSTALL.contains("validate_server_ready"), SERVER_INSTALL.contains("validate_server_ready"),
"install script should verify that lesavka-server reaches a running state" "install script should verify that lesavka-server reaches a running state"