Просмотр исходного кода

fix(monitor): tell a hung server apart from a port conflict in the query hint (#5120)

When the query port is held by the game server's own process tree, say
the server did not answer and may be hung, instead of pointing at the
port settings. Name another process as a port conflict, show the real
protocols from ss instead of "TCP/UDP", and drop the unused test script.
Daniel Gibbs 4 дней назад
Родитель
Сommit
5bf2e74412
2 измененных файлов с 27 добавлено и 66 удалено
  1. 0 53
      .github/tests/monitor_query_port_hint.sh
  2. 27 13
      lgsm/modules/command_monitor.sh

+ 0 - 53
.github/tests/monitor_query_port_hint.sh

@@ -1,53 +0,0 @@
-#!/bin/bash
-set -euo pipefail
-cd "$(dirname "$0")/../.."
-# The monitor runs when sourced, so load only the helper under test.
-# shellcheck source=/dev/null
-source <(sed -n '/^fn_monitor_query_port_hint() {/,/^}/p' lgsm/modules/command_monitor.sh)
-
-tmp="$(mktemp -d)"
-trap 'rm -rf "$tmp"' EXIT
-fn_print_warn() { printf '%s\n' "$*" >> "$tmp/screen"; }
-fn_print_warn_eol_nl() { :; }
-fn_script_log_warn() { printf '%s\n' "$*" >> "$tmp/log"; }
-queryport=27016
-
-socket_state=absent
-ss() {
-	case "$socket_state" in
-		absent) printf '%s\n' 'udp UNCONN 0 0 0.0.0.0:270160 0.0.0.0:*' ;;
-		used) printf '%s\n' 'tcp LISTEN 0 128 0.0.0.0:27016 0.0.0.0:* users:(("srcds",pid=123,fd=5))' ;;
-		no_process) printf '%s\n' 'udp UNCONN 0 0 0.0.0.0:27016 0.0.0.0:*' ;;
-		unavailable) return 1 ;;
-	esac
-}
-check_output() {
-	grep -Fq "$1" "$tmp/log"
-	grep -Fq "$1" "$tmp/screen"
-	[ "$(wc -l < "$tmp/log")" -eq 1 ]
-	[ "$(wc -l < "$tmp/screen")" -eq 1 ]
-}
-: > "$tmp/log"
-: > "$tmp/screen"
-fn_monitor_query_port_hint
-check_output 'No local TCP/UDP listener on port 27016'
-
-: > "$tmp/log"
-: > "$tmp/screen"
-socket_state=used
-fn_monitor_query_port_hint
-check_output 'Local TCP/UDP port 27016 is in use by srcds'
-
-: > "$tmp/log"
-: > "$tmp/screen"
-socket_state=no_process
-fn_monitor_query_port_hint
-check_output 'Local TCP/UDP port 27016 is in use; check the query port and protocol'
-
-: > "$tmp/log"
-: > "$tmp/screen"
-socket_state=unavailable
-fn_monitor_query_port_hint
-[ ! -s "$tmp/log" ]
-[ ! -s "$tmp/screen" ]
-printf '%s\n' 'monitor query port hints: 4 checks passed'

+ 27 - 13
lgsm/modules/command_monitor.sh

@@ -359,30 +359,44 @@ fn_query_tcp() {
 	querystatus="$?"
 }
 
-# Explain a failed query without changing the monitor's retry/restart decision.
 fn_monitor_query_port_hint() {
 	local sockets protocol state recvq sendq address peer details
-	local listener="" process="" hint=""
+	local serverpid="" protocols="" process="" pid="" ancestor="" serverprocess="" otherprocess="" hint=""
 
 	command -v ss > /dev/null 2>&1 || return 0
 	sockets="$(ss -H -lntup 2> /dev/null)" || return 0
+	serverpid="$(TERM=screen tmux -L "${socketname}" list-sessions -F "#{session_name} #{pane_pid}" 2> /dev/null | awk -v session="${sessionname}" '$1 == session {print $2}')"
 
 	while read -r protocol state recvq sendq address peer details; do
-		if [ "${address##*:}" == "${queryport}" ]; then
-			listener=1
-			process="$(sed -nE 's/.*users:\(\("([^"]+)".*/\1/p' <<< "${details}")"
-			break
+		if [ "${address##*:}" != "${queryport}" ]; then
+			continue
+		fi
+		if [[ " ${protocols//\// } " != *" ${protocol} "* ]]; then
+			protocols+="${protocols:+/}${protocol}"
+		fi
+		process="$(sed -nE 's/.*users:\(\("([^"]+)".*/\1/p' <<< "${details}")"
+		pid="$(sed -nE 's/.*users:\(\("[^"]+",pid=([0-9]+).*/\1/p' <<< "${details}")"
+		ancestor="${pid}"
+		while [ -n "${serverpid}" ] && [ -n "${ancestor}" ] && [ "${ancestor}" -gt 1 ]; do
+			if [ "${ancestor}" == "${serverpid}" ]; then
+				serverprocess="${process}"
+				break
+			fi
+			ancestor="$(ps -o ppid= -p "${ancestor}" 2> /dev/null | tr -d ' ')"
+		done
+		if [ -z "${serverprocess}" ] && [ -z "${otherprocess}" ]; then
+			otherprocess="${process}"
 		fi
 	done <<< "${sockets}"
 
-	if [ -z "${listener}" ]; then
-		hint="No local TCP/UDP listener on port ${queryport}; check startup and port settings"
+	if [ -z "${protocols}" ]; then
+		hint="Nothing is listening on port ${queryport}; check the server started and the port settings"
+	elif [ -n "${serverprocess}" ]; then
+		hint="Port ${queryport}/${protocols} is open by the game server (${serverprocess}) but it did not answer; the server may be hung, or the query port or type may not match"
+	elif [ -n "${otherprocess}" ]; then
+		hint="Port ${queryport}/${protocols} is in use by another process (${otherprocess}); check for a port conflict"
 	else
-		hint="Local TCP/UDP port ${queryport} is in use"
-		if [ -n "${process}" ]; then
-			hint+=" by ${process}"
-		fi
-		hint+="; check the query port and protocol"
+		hint="Port ${queryport}/${protocols} is in use by a process owned by another user; check for a port conflict"
 	fi
 
 	fn_print_warn "Query port: ${hint}"