feat(vps-xray): 生成 timer/service/guard 三个单元文件

timer 每天唤醒,N 天间隔判断交给 guard 脚本,
绕开 OnCalendar 无法表达任意 N 天间隔的限制。
This commit is contained in:
2026-08-06 16:15:03 +08:00
parent dfcaf7e4d0
commit d19d337bf2
2 changed files with 132 additions and 0 deletions
+79
View File
@@ -148,6 +148,85 @@ update_env_field() {
return 0
}
# 生成 timer / service / guard 三个文件。
# 完整部署与 --restart 共用本函数,避免两条路径各写一份实现后漂移。
render_restart_units() {
local days="$1" time="$2" tz="$3"
mkdir -p "$SYSTEMD_DIR" "$(dirname "$GUARD_BIN")"
# 守卫脚本:systemd 的 OnCalendar 表达不了「每 N 天」——
# *-*-1/7 是「每月 1/8/15/22/29 号」,跨月会重置成 2~3 天。
# 所以 timer 只负责每天到点唤醒,间隔判断放到这里。
cat > "$GUARD_BIN" << EOF
#!/bin/bash
# 由 deploy.sh 生成,请勿手工编辑
STATE="${RESTART_STATE_FILE}"
DAYS=${days}
now=\$(date +%s)
last=0
if [ -f "\$STATE" ]; then
last=\$(cat "\$STATE" 2>/dev/null || echo 0)
# 内容被写坏时按 0 处理,即允许立即重启
[[ "\$last" =~ ^[0-9]+\$ ]] || last=0
fi
# 300 秒容差:systemd timer 默认 AccuracySec=1min,第 N 次触发的实际间隔
# 可能是 N*86400-30s。严格比较会判「不满」从而顺延整整一天,且误差会累积。
if [ "\$last" -gt 0 ] && [ \$(( now - last )) -lt \$(( DAYS * 86400 - 300 )) ]; then
echo "距上次重启不足 \${DAYS} 天(已过 \$(( (now - last) / 86400 )) 天),跳过"
exit 0
fi
mkdir -p "\$(dirname "\$STATE")"
# 必须先写时间戳:restart 会中断本脚本所在的 systemd 事务,
# 顺序反了会丢记录,退化成每天都重启。
# 代价是重启失败时本轮仍被记为已执行——这比陷入每日重启循环可接受。
echo "\$now" > "\$STATE"
echo "距上次重启已满 \${DAYS} 天,执行重启"
exec systemctl restart xray
EOF
chmod 755 "$GUARD_BIN"
cat > "${SYSTEMD_DIR}/xray-restart.service" << EOF
[Unit]
Description=Scheduled restart of Xray (every ${days} day(s))
After=network.target
[Service]
Type=oneshot
Environment=PATH=/usr/sbin:/usr/bin:/sbin:/bin
ExecStart=${GUARD_BIN}
EOF
cat > "${SYSTEMD_DIR}/xray-restart.timer" << EOF
[Unit]
Description=Wake xray-restart daily at ${time} ${tz}; the guard enforces the ${days}-day interval
[Timer]
OnCalendar=*-*-* ${time}:00
TimeZone=${tz}
Persistent=true
[Install]
WantedBy=timers.target
EOF
}
# 删除 timer / service / guard。状态文件刻意保留:
# 以后重新启用时,距上次已超 N 天则首次触发即重启,符合直觉。
remove_restart_units() {
if [ ! -f "${SYSTEMD_DIR}/xray-restart.timer" ] && [ ! -f "$GUARD_BIN" ]; then
return 1
fi
systemctl disable --now xray-restart.timer >/dev/null 2>&1 || true
rm -f "${SYSTEMD_DIR}/xray-restart.timer" \
"${SYSTEMD_DIR}/xray-restart.service" \
"$GUARD_BIN"
return 0
}
resolve_restart_config || exit 1
# 过渡兼容:harden_system / start_service / save_env 仍引用 DAILY_RESTART