fix(sync): inspect hidden server listeners via procfs
This commit is contained in:
parent
fbfa450e4a
commit
a9aefd470c
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -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",
|
||||||
|
|||||||
@ -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]
|
||||||
|
|||||||
@ -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"
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user