fix: RDP tunnel status adopts detached/wild tunnels instead of false-disabled
- _rdp_find_tunnel_process(): find ssh.exe hosting -R 8080 forwarding via CIM - _rdp_status(): adopt actual tunnel process when pid file stale/missing - _rdp_enable(): adopt existing tunnel instead of spawning duplicate that fails on 'remote port forwarding failed' (8080 occupied) - Fixes 2026-07-23 incident: tunnel alive but dashboard showed disabled, repeated enable attempts failed because port was occupied by working tunnel
This commit is contained in:
@@ -317,6 +317,15 @@ def _rdp_enable():
|
||||
except Exception:
|
||||
pass
|
||||
_rdp_kill_tunnel()
|
||||
# 收养现存隧道:如果有 ssh 进程已在跑 -R 8080 转发(比如 bot 崩溃后留下的
|
||||
# detached ssh、或状态文件丢失后的野生隧道),直接写入 pid 文件收养,
|
||||
# 不再起新进程——否则新 ssh 会因 8080 被占报 "remote port forwarding failed",
|
||||
# 导致 dashboard 永远显示 disabled(2026-07-23 事故)。
|
||||
_existing = _rdp_find_tunnel_process()
|
||||
if _existing:
|
||||
_rdp_adopt_tunnel(_existing)
|
||||
log(f'RDP: tunnel already running (PID {_existing}), adopted — no new ssh needed')
|
||||
return True, f'RDP access enabled (adopted existing tunnel, PID {_existing})'
|
||||
# Health check: if RDP not responding, auto-restore
|
||||
_t.sleep(2)
|
||||
if not _rdp_health_check():
|
||||
@@ -403,6 +412,36 @@ def _rdp_kill_tunnel():
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _rdp_find_tunnel_process():
|
||||
"""Find a running ssh.exe hosting the -R 8080:localhost:3389 reverse tunnel.
|
||||
Returns PID or None. Used to adopt tunnels started outside _rdp_enable
|
||||
(e.g. by a crashed/restarted bot that left a detached ssh behind)."""
|
||||
import subprocess as _sp
|
||||
try:
|
||||
# 纯单引号写法:避免 powershell -Command 下双引号转义失效
|
||||
ps_cmd = ("Get-CimInstance Win32_Process | "
|
||||
"Where-Object { $_.Name -eq 'ssh.exe' -and $_.CommandLine -like '*8080:localhost:3389*' } | "
|
||||
"Select-Object -ExpandProperty ProcessId")
|
||||
r = _sp.run(['powershell', '-NoProfile', '-Command', ps_cmd],
|
||||
capture_output=True, text=True, timeout=15)
|
||||
for line in r.stdout.splitlines():
|
||||
line = line.strip()
|
||||
if line.isdigit():
|
||||
return int(line)
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def _rdp_adopt_tunnel(pid):
|
||||
"""Write pid file for an externally-started tunnel so status tracking works."""
|
||||
try:
|
||||
with open(_RDP_PID_FILE, 'w') as f:
|
||||
f.write(str(pid))
|
||||
log(f'RDP: adopted existing tunnel (PID {pid})')
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _rdp_status():
|
||||
import subprocess as _sp, winreg as _wr
|
||||
tunnel_on = False
|
||||
@@ -413,6 +452,12 @@ def _rdp_status():
|
||||
tunnel_on = 'ssh.exe' in r.stdout
|
||||
except Exception:
|
||||
pass
|
||||
# pid file missing or process dead → look for an actual tunnel process and adopt it
|
||||
if not tunnel_on:
|
||||
found = _rdp_find_tunnel_process()
|
||||
if found:
|
||||
_rdp_adopt_tunnel(found)
|
||||
tunnel_on = True
|
||||
rdp_on = False
|
||||
try:
|
||||
k = _wr.OpenKey(_wr.HKEY_LOCAL_MACHINE, r'SYSTEM\CurrentControlSet\Control\Terminal Server', 0, _wr.KEY_READ)
|
||||
|
||||
Reference in New Issue
Block a user