# -*- coding: utf-8 -*- r'''解开"nettune 死锁",然后把根因修掉。 == 死锁是怎么回事 == lanremote-nettune.service: Before=lanremote-tunnel.service <- 关键 ExecStart=/bin/bash net-tune.sh net-tune.sh 第 4 步: systemctl restart lanremote-tunnel <- 在这里永远阻塞 为什么阻塞:systemd 正在启动 nettune,而 nettune 声明了"我必须在 隧道之前启动完"。脚本却在**自己还没结束的时候**去 restart 隧道 —— 那要求隧道先停、再等 nettune 结束……systemd 检测到这是顺序冲突, restart 就一直挂着不返回。 证据:CGroup 里能看到 net-tune.sh(13388)下面挂着 systemctl restart lanremote-tunnel(13428), etime 09:32 —— 卡了 9 分半,nettune 一直是 activating。 后果:nettune 卡住 -> 隧道被 Before= 挡着起不来 -> 公网 530。 这就是用户说的"网站还是容易挂"的真因之一。 == 为什么以前没暴露 == 设备上原来那份 unit 的 ExecStart 里有个 `iptables ... || ...`, systemd 不支持 ||,所以它**在第 2 条 ExecStart 就报错退出了**, 根本没走到"重启隧道"那一步 —— 死锁被那个 bug 遮住了。 我把 unit 换成正确形状之后,死锁才露出来。 == 这一步做什么 == 1. 杀掉卡住的 systemctl 和 net-tune.sh(解开当前死锁) 2. 拉起隧道(网站先恢复) 3. 把 net-tune.sh 的"重启隧道"改成**只在手动跑时做** ''' from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "_build")) from devssh import run # noqa: E402 REMOTE = r''' echo "════ 1. 解开死锁 ════" # 先杀卡住的 systemctl(不然它会一直拽着 systemd 的 job) pkill -f 'systemctl restart lanremote-tunnel' 2>/dev/null && echo " 杀了卡住的 systemctl" || echo " 没有卡住的 systemctl" sleep 2 # 再让 nettune 的脚本结束(杀它的子进程已经让主脚本继续往下走了) systemctl stop lanremote-nettune 2>/dev/null systemctl reset-failed lanremote-nettune 2>/dev/null sleep 2 echo " nettune 现在: $(systemctl is-active lanremote-nettune)" echo echo "════ 2. 拉起隧道(网站先恢复)════" systemctl reset-failed lanremote-tunnel lanremote-tunnel-h2 2>/dev/null systemctl start lanremote-tunnel lanremote-tunnel-h2 sleep 15 echo " tunnel: $(systemctl is-active lanremote-tunnel)" echo " tunnel-h2: $(systemctl is-active lanremote-tunnel-h2)" echo echo "════ 3. 验公网 ════" for i in 1 2 3; do code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 15 https://relay.xxmxxs.top/v1/health) echo " 第 $i 次: $code" [ "$code" = "200" ] && break sleep 6 done echo echo "════ 4. 隧道连接数 ════" for p in 20241 20242; do n=$(curl -s --max-time 5 http://127.0.0.1:$p/metrics 2>/dev/null | grep '^cloudflared_tunnel_ha_connections' | awk '{print $2}' | head -1) echo " $p -> ${n:-拿不到}" done ''' def main() -> int: out = run(REMOTE, timeout=300) text = out[1] if isinstance(out, tuple) else str(out) print(text) return 0 if __name__ == "__main__": sys.exit(main())