#!/bin/bash # 原子方式更新 watchdog。 # # 为什么必须原子: # watchdog 由 timer **每 10 秒**跑一次。如果直接覆盖 # /root/lanremote-relay/watchdog.sh,在"文件已截断、新内容还没写完" # 的那个瞬间被 timer 读到,就会执行一份半截脚本 —— # 日志里真的出现了: # watchdog.sh: 行 191: 未预期的记号 "else" 附近有语法错误 # 从 17:32:35 到 17:41:56 之间看门狗**完全没工作**。 # # 正确做法:写 .new 临时文件 -> 语法检查 -> mv 覆盖。 # 同一文件系统内 mv 是原子的,timer 要么读旧的、要么读新的。 set -u RELAY=/root/lanremote-relay SRC=/tmp/wd_new.sh NEW="$RELAY/.watchdog.sh.new" echo "════════ 1. 检查新脚本 ════════" if [ ! -s "$SRC" ]; then echo " ✗ $SRC 不存在或为空" exit 1 fi echo " 行数: $(wc -l < "$SRC")" if ! bash -n "$SRC" 2>&1; then echo " ✗ 语法检查不过,拒绝部署" exit 1 fi echo " ✓ 语法 OK" echo echo "════════ 2. 原子替换 ════════" echo " 替换前行数: $(wc -l < "$RELAY/watchdog.sh") md5=$(md5sum "$RELAY/watchdog.sh" | awk '{print $1}')" cp "$SRC" "$NEW" chmod 755 "$NEW" # 落盘,确保 timer 不会读到还在页缓存里的半截内容 sync mv -f "$NEW" "$RELAY/watchdog.sh" sync echo " 替换后行数: $(wc -l < "$RELAY/watchdog.sh") md5=$(md5sum "$RELAY/watchdog.sh" | awk '{print $1}')" echo " 拿掉临时文件: $(rm -f "$NEW" && echo 已清理)" echo echo "════════ 3. 复核 ════════" bash -n "$RELAY/watchdog.sh" && echo " 语法 OK" echo " 关键参数:" grep -E '^THRESHOLD=|^THRESHOLD_HEALTHY=|^GRACE=|^MIN_RESTART_GAP=|^BUILT_MIN=' \ "$RELAY/watchdog.sh" | sed 's/^/ /' echo echo "════════ 4. 连跑 5 次预演 + 真跑 1 次 ════════" for i in 1 2 3 4 5; do out=$(DRY_RUN=1 bash "$RELAY/watchdog.sh" 2>&1) echo " 预演 $i: ${out:-(无输出=正常)}" sleep 1 done echo " 真跑:" bash "$RELAY/watchdog.sh" 2>&1 | sed 's/^/ /' echo " 退出码 $? 状态: $(cat "$RELAY/watchdog.state" 2>/dev/null | tr '\n' ' ')" echo echo "════════ 5. 等 timer 跑两轮,确认没有语法错误 ════════" sleep 25 echo " 最近 6 行日志:" tail -6 "$RELAY/watchdog.log" | sed 's/^/ /' echo " 这个时间窗内有没有语法错误:" tail -20 "$RELAY/watchdog.log" | grep -c '语法错误\|未预期' | sed 's/^/ /' echo echo "════════ 6. 长测进度 ════════" echo " 健康样本: $(wc -l < /root/long2h_health.tsv 2>/dev/null || echo 0)" echo " 轮询: $(wc -l < /root/rp_poll30.tsv 2>/dev/null || echo 0) / 30"