Files
ykxmz/_build/devssh.py
T
xmz7891 f4a146056f 修开机慢 2 分钟:掉电重启后要 2 分 10 秒才能恢复
用户早上撞上 Cloudflare Error 1033。查出来是设备**掉电重启**:
上次开机到重启之间没有任何日志落盘(直接断电的特征),
dmesg 里还有 "l13: voltage operation not allowed"。

重启本身不是软件问题,但**恢复太慢**:
    Startup finished in 3.846s (kernel) + 2min 6.358s (userspace) = 2分10秒
那 2 分钟里浏览器看到的就是 1033。

根因:
    这台机器 NetworkManager 和 systemd-networkd 同时在跑,
    但 wlan0 归 NetworkManager 管。
      NetworkManager-wait-online   -> 开机 9 秒成功
      systemd-networkd-wait-online -> 等一个不归它管的网络,
                                      干等 2 分钟超时,然后 failed
    network-online.target 要等**两个都完成**,
    被那个失败的拖了整整 2 分钟,
    relay / tunnel / net-tune 全跟着等。

修法:
  1. mask 掉 systemd-networkd-wait-online(网络不归它管,只会拖后腿)
  2. watchdog 首次检查从"开机后 2 分钟"提前到 45 秒

实测(重启验证过):
    修之前  Startup finished in 3.846s + 2min 6.358s = 2分10秒
    修之后  Startup finished in 3.218s +   14.060s =   17.3秒
    掉电重启后公网恢复时间:**24 秒**(快了 5 倍多)

配套加了 scripts/speed-up-boot.sh,换设备时直接跑一遍。

顺带确认重启后一切自动恢复:
    三个服务 active + enabled ✓
    keepalive 30/10/3、MTU 探测、MSS 钳制、slow_start=0 全部持久化 ✓
    隧道 4 条连接 http2 ✓
    官网 / 管理页 / 截图全部 200 ✓
2026-10-07 10:27:07 +08:00

96 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""随身 WiFi(Debian)远程执行小工具。
用法:
python _build/devssh.py "命令" # 跑一条命令
python _build/devssh.py --script 文件 # 跑一段脚本
"""
from __future__ import annotations
import sys
import time
from pathlib import Path
import paramiko
# 随身 WiFi(Debian)的地址。
#
# 这台设备跟着教室网络走,地址会变:
# 192.168.2.156 -> 192.168.10.105(换网络了)
# 所以优先读环境变量 YKXMZ_DEV_HOST,没有才用下面的默认值。
# 换网络时不用改代码:
# $env:YKXMZ_DEV_HOST = "192.168.10.105"
import os
HOST = os.environ.get("YKXMZ_DEV_HOST", "192.168.10.105")
USER = "root"
PASSWORD = os.environ.get("YKXMZ_DEV_PASS", "1313144")
# 旧地址留一份,方便回退或同时探测
HOST_FALLBACKS = ("192.168.10.105", "192.168.2.156")
def run(command: str, timeout: int = 600, quiet: bool = False) -> tuple:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(HOST, username=USER, password=PASSWORD, timeout=15,
banner_timeout=20, auth_timeout=20,
look_for_keys=False, allow_agent=False)
try:
_stdin, stdout, stderr = client.exec_command(command, timeout=timeout)
out = stdout.read().decode("utf-8", "replace")
err = stderr.read().decode("utf-8", "replace")
code = stdout.channel.recv_exit_status()
if not quiet:
if out.strip():
print(out.rstrip())
if err.strip():
print("[stderr] " + err.rstrip())
return code, out, err
finally:
client.close()
def upload(local: str, remote: str, mode: int = 0o644) -> None:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(HOST, username=USER, password=PASSWORD, timeout=15,
look_for_keys=False, allow_agent=False)
try:
sftp = client.open_sftp()
sftp.put(local, remote)
sftp.chmod(remote, mode)
sftp.close()
print(f" 已上传 {Path(local).name} -> {remote}")
finally:
client.close()
def download(remote: str, local: str) -> None:
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(HOST, username=USER, password=PASSWORD, timeout=15,
look_for_keys=False, allow_agent=False)
try:
sftp = client.open_sftp()
sftp.get(remote, local)
sftp.close()
print(f" 已下载 {remote} -> {local}")
finally:
client.close()
if __name__ == "__main__":
if len(sys.argv) < 2:
print(__doc__)
sys.exit(1)
if sys.argv[1] == "--script":
run(Path(sys.argv[2]).read_text(encoding="utf-8"), timeout=600)
else:
args = sys.argv[1:]
timeout = 600
if args and args[0] == '--timeout':
timeout = int(args[1])
args = args[2:]
run(" ".join(args), timeout=timeout)