fix(sync): clear stale server listeners before restart
This commit is contained in:
parent
898947a2b5
commit
5173e3cea7
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1642,7 +1642,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "lesavka_client"
|
||||
version = "0.14.32"
|
||||
version = "0.14.33"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@ -1676,7 +1676,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lesavka_common"
|
||||
version = "0.14.32"
|
||||
version = "0.14.33"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
@ -1688,7 +1688,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lesavka_server"
|
||||
version = "0.14.32"
|
||||
version = "0.14.33"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
||||
@ -4,7 +4,7 @@ path = "src/main.rs"
|
||||
|
||||
[package]
|
||||
name = "lesavka_client"
|
||||
version = "0.14.32"
|
||||
version = "0.14.33"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lesavka_common"
|
||||
version = "0.14.32"
|
||||
version = "0.14.33"
|
||||
edition = "2024"
|
||||
build = "build.rs"
|
||||
|
||||
|
||||
@ -113,6 +113,114 @@ is_attached_state() {
|
||||
return 1
|
||||
}
|
||||
|
||||
server_bind_addr() {
|
||||
printf '%s\n' "${LESAVKA_SERVER_BIND_ADDR:-0.0.0.0:50051}"
|
||||
}
|
||||
|
||||
server_bind_port() {
|
||||
local bind_addr port
|
||||
bind_addr=$(server_bind_addr)
|
||||
port=${bind_addr##*:}
|
||||
[[ $port =~ ^[0-9]+$ ]] || return 1
|
||||
printf '%s\n' "$port"
|
||||
}
|
||||
|
||||
list_server_listener_pids() {
|
||||
local port
|
||||
port=$(server_bind_port) || return 1
|
||||
sudo lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null | sort -u
|
||||
}
|
||||
|
||||
clear_stale_server_listener() {
|
||||
local port pid cmdline found=0 unexpected=0
|
||||
port=$(server_bind_port) || {
|
||||
echo "⚠️ could not parse LESAVKA_SERVER_BIND_ADDR='$(server_bind_addr)'; skipping stale-listener cleanup."
|
||||
return 0
|
||||
}
|
||||
|
||||
while read -r pid; do
|
||||
[[ -n $pid ]] || continue
|
||||
found=1
|
||||
cmdline=$(sudo ps -o args= -p "$pid" 2>/dev/null || true)
|
||||
if [[ $cmdline == *"/usr/local/bin/lesavka-server"* ]] || [[ $cmdline == *"lesavka-server"* ]]; then
|
||||
echo "⚠️ clearing stale lesavka-server listener on :$port (pid $pid)."
|
||||
sudo kill -TERM "$pid" 2>/dev/null || true
|
||||
else
|
||||
echo "❌ TCP :$port is already owned by an unexpected process (pid $pid): ${cmdline:-<unknown>}." >&2
|
||||
unexpected=1
|
||||
fi
|
||||
done < <(list_server_listener_pids || true)
|
||||
|
||||
if [[ "$unexpected" != "0" ]]; then
|
||||
return 1
|
||||
fi
|
||||
if [[ "$found" == "0" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
for _ in {1..30}; do
|
||||
if ! list_server_listener_pids | grep -q .; then
|
||||
echo "✅ cleared stale lesavka-server listeners on :$port."
|
||||
return 0
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
while read -r pid; do
|
||||
[[ -n $pid ]] || continue
|
||||
cmdline=$(sudo ps -o args= -p "$pid" 2>/dev/null || true)
|
||||
if [[ $cmdline == *"/usr/local/bin/lesavka-server"* ]] || [[ $cmdline == *"lesavka-server"* ]]; then
|
||||
echo "⚠️ stale lesavka-server listener on :$port survived SIGTERM; sending SIGKILL to pid $pid."
|
||||
sudo kill -KILL "$pid" 2>/dev/null || true
|
||||
else
|
||||
echo "❌ TCP :$port remained busy after cleanup and is owned by an unexpected process (pid $pid): ${cmdline:-<unknown>}." >&2
|
||||
return 1
|
||||
fi
|
||||
done < <(list_server_listener_pids || true)
|
||||
|
||||
for _ in {1..30}; do
|
||||
if ! list_server_listener_pids | grep -q .; then
|
||||
echo "✅ cleared stale lesavka-server listeners on :$port."
|
||||
return 0
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
|
||||
echo "❌ lesavka-server listener on :$port survived cleanup; refusing to start a duplicate server." >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_for_unit_running() {
|
||||
local unit=$1
|
||||
for _ in {1..50}; do
|
||||
if systemctl is-active --quiet "$unit"; then
|
||||
if [[ $(systemctl show "$unit" -p SubState --value 2>/dev/null || true) == "running" ]]; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
sleep 0.2
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
validate_server_ready() {
|
||||
local bind_addr
|
||||
bind_addr=$(server_bind_addr)
|
||||
if wait_for_unit_running lesavka-server; then
|
||||
echo "✅ lesavka-server is active and running on ${bind_addr}."
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "❌ lesavka-server failed to reach active/running state on ${bind_addr}." >&2
|
||||
sudo systemctl status lesavka-server --no-pager >&2 || true
|
||||
if [[ -s /tmp/lesavka-server.stderr ]]; then
|
||||
echo "---- /tmp/lesavka-server.stderr (tail) ----" >&2
|
||||
sudo tail -n 40 /tmp/lesavka-server.stderr >&2 || true
|
||||
echo "------------------------------------------" >&2
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
normalize_hdmi_connector() {
|
||||
local name="$1"
|
||||
if [[ $name =~ (HDMI-A-[0-9]+)$ ]]; then
|
||||
@ -719,7 +827,12 @@ fi
|
||||
|
||||
validate_uvc_gadget_ready
|
||||
|
||||
sudo truncate -s 0 /tmp/lesavka-server.stderr
|
||||
sudo systemctl stop lesavka-server >/dev/null 2>&1 || true
|
||||
clear_stale_server_listener
|
||||
sudo systemctl reset-failed lesavka-server >/dev/null 2>&1 || true
|
||||
sudo systemctl restart lesavka-server
|
||||
validate_server_ready
|
||||
INSTALLED_VERSION=$(manifest_package_version "$SRC_DIR/server/Cargo.toml" 2>/dev/null || true)
|
||||
INSTALLED_SHA=$(git -C "$SCRIPT_REPO_ROOT" rev-parse --short HEAD 2>/dev/null || true)
|
||||
PERSISTED_CAM_OUTPUT=$(grep '^LESAVKA_CAM_OUTPUT=' /etc/lesavka/server.env 2>/dev/null | tail -n1 | cut -d= -f2- || true)
|
||||
|
||||
@ -10,7 +10,7 @@ bench = false
|
||||
|
||||
[package]
|
||||
name = "lesavka_server"
|
||||
version = "0.14.32"
|
||||
version = "0.14.33"
|
||||
edition = "2024"
|
||||
autobins = false
|
||||
|
||||
|
||||
@ -98,6 +98,26 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
|
||||
SERVER_INSTALL.contains("lesavka-uvc already active; runtime settings unchanged."),
|
||||
"install script should avoid unnecessary UVC restarts when nothing changed"
|
||||
);
|
||||
assert!(
|
||||
SERVER_INSTALL.contains("clear_stale_server_listener"),
|
||||
"install script should clear stale server listeners before restart"
|
||||
);
|
||||
assert!(
|
||||
SERVER_INSTALL.contains("lsof -tiTCP:"),
|
||||
"install script should inspect the bind port before starting a fresh server"
|
||||
);
|
||||
assert!(
|
||||
SERVER_INSTALL.contains("unexpected process"),
|
||||
"install script should fail loud instead of killing an unrelated process on the server port"
|
||||
);
|
||||
assert!(
|
||||
SERVER_INSTALL.contains("validate_server_ready"),
|
||||
"install script should verify that lesavka-server reaches a running state"
|
||||
);
|
||||
assert!(
|
||||
SERVER_INSTALL.contains("failed to reach active/running state"),
|
||||
"install script should explain server startup failures instead of claiming success"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user