Skip to content

Commit 732dce0

Browse files
committed
feat(sub): 订阅定时自动更新
每隔 N 小时自动更新全部订阅,由 busybox crond 调度,随服务启停。 - subsched.sh(新增):调度守护。sync 按 module.conf 写 crontab 并(重)启 crond;stop 按 -c 工作目录标识精准杀守护,不误伤系统 crond;run 调 subscription.sh update-all - crontab 用标准 cron 表达式 `7 */N * * *`(第 7 分错峰避整点拥堵), 放 tmpfs /dev/netproxy/cron(不磨损 flash、重启自清) - module.conf:扩展区加 SUB_AUTO_UPDATE / SUB_UPDATE_INTERVAL - service.sh:随服务启停 crond 守护 - cli:sub 加 auto 子命令(status/on/off/interval,1~23 校验),改配置即时生效 - customize.sh:登记 subsched.sh 可执行 复用既有 update_all_subscriptions(容错、写 subscription.log),不改 subscription.sh 更新逻辑,亦不改 tproxy.sh。
1 parent 1598632 commit 732dce0

5 files changed

Lines changed: 176 additions & 0 deletions

File tree

src/module/config/module.conf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,13 @@ SELECTOR_MODE=urltest
99
GMS_FIX=0
1010
# 当前使用的 sing-box 出站配置文件路径
1111
CURRENT_CONFIG=""
12+
13+
# ##################################################################
14+
# # 以下为扩展功能配置,由 scripts/core/ 下的调度/监听脚本读取。
15+
# ##################################################################
16+
17+
# ===================== 订阅定时更新 (subsched.sh) =====================
18+
# 定时自动更新全部订阅 (1=启用, 0=禁用)
19+
SUB_AUTO_UPDATE=0
20+
# 更新间隔小时数 (正整数, 1~23)
21+
SUB_UPDATE_INTERVAL=12

src/module/customize.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ readonly EXECUTABLE_FILES="
4545
scripts/network/tproxy.sh
4646
scripts/network/netmon.sh
4747
scripts/core/subscription.sh
48+
scripts/core/subsched.sh
4849
scripts/utils/ipset.sh
4950
scripts/utils/gms_fix.sh
5051
"

src/module/scripts/cli

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ show_sub_help() {
199199
cli sub update <名称> 更新指定订阅
200200
cli sub update-all 更新全部订阅
201201
cli sub remove <名称> 删除订阅
202+
cli sub auto status 查看定时更新配置
203+
cli sub auto on | off 启用 / 禁用定时更新
204+
cli sub auto interval <小时> 设置更新间隔 (1~23)
202205
EOF
203206
}
204207

@@ -615,6 +618,52 @@ cmd_mode() {
615618
# sub 命令
616619
#######################################
617620

621+
readonly SUBSCHED_SCRIPT="$SCRIPTS_DIR/core/subsched.sh"
622+
623+
# 改动定时更新配置后应用:运行中则让 subsched 重启 crond,否则下次启动生效
624+
subsched_apply() {
625+
if is_running; then
626+
sh "$SUBSCHED_SCRIPT" sync > /dev/null 2>&1 || warn "定时更新应用失败"
627+
info "已应用 (即时生效)"
628+
else
629+
info "服务未运行,配置将在下次启动时生效"
630+
fi
631+
}
632+
633+
# sub auto 子命令:定时更新配置。参数: $1 status/on/off/interval $2 间隔
634+
cmd_sub_auto() {
635+
local act="${1:-status}"
636+
local val="${2:-}"
637+
638+
case "$act" in
639+
status)
640+
section "订阅定时更新"
641+
printf "定时更新: %s\n" "$(read_conf "$MODULE_CONF" "SUB_AUTO_UPDATE" "0")"
642+
printf "更新间隔: %s 小时\n" "$(read_conf "$MODULE_CONF" "SUB_UPDATE_INTERVAL" "12")"
643+
;;
644+
on)
645+
set_conf "$MODULE_CONF" "SUB_AUTO_UPDATE" "1"
646+
info "订阅定时更新已启用"
647+
subsched_apply
648+
;;
649+
off)
650+
set_conf "$MODULE_CONF" "SUB_AUTO_UPDATE" "0"
651+
info "订阅定时更新已禁用"
652+
subsched_apply
653+
;;
654+
interval)
655+
case "$val" in
656+
"" | *[!0-9]*) die_cli "用法: cli sub auto interval <1~23 的小时数>" ;;
657+
esac
658+
[ "$val" -ge 1 ] && [ "$val" -le 23 ] || die_cli "间隔需为 1~23 小时"
659+
set_conf "$MODULE_CONF" "SUB_UPDATE_INTERVAL" "$val"
660+
info "更新间隔已设置为: $val 小时"
661+
subsched_apply
662+
;;
663+
*) die_cli "用法: cli sub auto <status|on|off|interval>" ;;
664+
esac
665+
}
666+
618667
# sub 命令组分发 (透传给 subscription.sh)
619668
cmd_sub() {
620669
local subcmd="${1:-list}"
@@ -626,6 +675,7 @@ cmd_sub() {
626675
update) sh "$SUB_SCRIPT" update "${1:-}" ;;
627676
update-all) sh "$SUB_SCRIPT" update-all ;;
628677
remove | rm) sh "$SUB_SCRIPT" remove "${1:-}" ;;
678+
auto) cmd_sub_auto "$@" ;;
629679
help | -h | --help) show_sub_help ;;
630680
*) die_cli "用法错误,使用 cli sub help 查看帮助" ;;
631681
esac
@@ -1104,6 +1154,7 @@ NetProxy CLI
11041154
cli sub update <名称> 更新指定订阅
11051155
cli sub update-all 更新全部订阅
11061156
cli sub remove <名称> 删除订阅
1157+
cli sub auto <status|on|off|interval> 定时更新
11071158
11081159
控制接口:
11091160
cli api groups 查看代理组

src/module/scripts/core/service.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ readonly RUNTIME_DIR="$SINGBOX_DIR/runtime" # 运行时生成目录
2323
readonly SWITCH_SCRIPT="$MODDIR/scripts/core/switch.sh" # 模式/节点切换脚本
2424
readonly TPROXY_SCRIPT="$MODDIR/scripts/network/tproxy.sh" # 透明代理脚本
2525
readonly NETMON_SCRIPT="$MODDIR/scripts/network/netmon.sh" # WiFi 自动切换监听脚本
26+
readonly SUBSCHED_SCRIPT="$MODDIR/scripts/core/subsched.sh" # 订阅定时更新调度脚本
2627
readonly KILL_TIMEOUT=5 # 等待进程退出的秒数上限
2728
readonly LOG_TAG="service" # 日志组件标签
2829

@@ -166,6 +167,8 @@ EOF
166167
"$TPROXY_SCRIPT" start -d "$TPROXY_CONF_DIR" >> "$LOG_FILE" 2>&1 || die "透明代理规则加载失败"
167168
# WiFi 自动切换:按配置启停监听守护并做初始决策 (失败不影响主流程)
168169
sh "$NETMON_SCRIPT" sync > /dev/null 2>&1 || log "WARN" "WiFi 自动切换初始化失败"
170+
# 订阅定时更新:按配置启停 crond 守护
171+
sh "$SUBSCHED_SCRIPT" sync > /dev/null 2>&1 || log "WARN" "订阅定时更新初始化失败"
169172
fi
170173

171174
log "INFO" "sing-box 服务启动完成"
@@ -188,6 +191,8 @@ do_stop() {
188191
if [ "$skip_tproxy" != "1" ]; then
189192
# 先停掉 WiFi 自动切换监听守护,避免其在清理期间误触发切换
190193
sh "$NETMON_SCRIPT" stop > /dev/null 2>&1 || true
194+
# 停掉订阅定时更新 crond 守护
195+
sh "$SUBSCHED_SCRIPT" stop > /dev/null 2>&1 || true
191196
log "DEBUG" "正在清理透明代理规则..."
192197
"$TPROXY_SCRIPT" stop -d "$TPROXY_CONF_DIR" >> "$LOG_FILE" 2>&1 || true
193198
fi
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/system/bin/sh
2+
#######################################
3+
# 文件: subsched.sh
4+
# 功能: 订阅定时更新调度守护。用 busybox crond 按配置的间隔小时数周期性
5+
# 调用 subscription.sh update-all 更新全部订阅。
6+
# 用法:
7+
# subsched.sh sync 按 module.conf 启停 crond 守护并写入 crontab
8+
# subsched.sh stop 停止本模块的 crond 守护
9+
# subsched.sh run 立即执行一次全部订阅更新 (供 cron 触发)
10+
# 依赖: common.sh、config.sh、subscription.sh、busybox(crond applet)。
11+
#######################################
12+
13+
set -u # 引用未定义变量报错
14+
15+
# 模块根目录与关键路径
16+
readonly MODDIR="$(cd "$(dirname "$0")/../.." && pwd)"
17+
readonly MODULE_CONF="$MODDIR/config/module.conf"
18+
readonly SUB_SCRIPT="$MODDIR/scripts/core/subscription.sh"
19+
# crond 工作目录放 tmpfs (/dev):不磨损 flash、重启自动清空
20+
readonly CRON_DIR="/dev/netproxy/cron"
21+
readonly CRONTAB_FILE="$CRON_DIR/root" # busybox crond 按 DIR/<用户名> 读取,root 运行即 root
22+
readonly LOG_FILE="$MODDIR/logs/service.log"
23+
readonly LOG_TAG="subsched"
24+
25+
. "$MODDIR/scripts/utils/common.sh"
26+
. "$MODDIR/scripts/utils/config.sh"
27+
28+
readonly BUSYBOX="$(detect_busybox)"
29+
30+
# PLACEHOLDER_SUBSCHED
31+
32+
#######################################
33+
# 停止本模块的 crond 守护 (按 -c CRON_DIR 标识匹配,避免误杀系统 crond)
34+
# 返回: 无
35+
#######################################
36+
stop_cron() {
37+
local pid
38+
for pid in $(pidof crond 2> /dev/null); do
39+
if [ -f "/proc/$pid/cmdline" ] && grep -q "$CRON_DIR" "/proc/$pid/cmdline" 2> /dev/null; then
40+
kill "$pid" 2> /dev/null || true
41+
fi
42+
done
43+
}
44+
45+
#######################################
46+
# 立即执行一次全部订阅更新 (供 cron 触发)
47+
# 返回: subscription.sh 的退出码
48+
#######################################
49+
run_update() {
50+
log "INFO" "定时任务触发:开始更新全部订阅"
51+
sh "$SUB_SCRIPT" update-all
52+
}
53+
54+
#######################################
55+
# 按 module.conf 启停 crond 守护
56+
# 关闭 -> 停 crond;开启 -> 写 crontab 并(重)启 crond
57+
# 返回: 无
58+
#######################################
59+
sync_cron() {
60+
local enabled interval minute self
61+
62+
enabled="$(read_conf "$MODULE_CONF" "SUB_AUTO_UPDATE" "0")"
63+
interval="$(read_conf "$MODULE_CONF" "SUB_UPDATE_INTERVAL" "12")"
64+
65+
# 先停旧守护,保证幂等
66+
stop_cron
67+
68+
if [ "$enabled" != "1" ]; then
69+
return 0
70+
fi
71+
72+
# 间隔校验:取正整数,超出 1..23 的按 cron 小时步进语义夹取/兜底
73+
case "$interval" in
74+
*[!0-9]* | "") interval=12 ;;
75+
esac
76+
[ "$interval" -ge 1 ] 2> /dev/null || interval=12
77+
[ "$interval" -le 23 ] 2> /dev/null || interval=23
78+
79+
# 错峰分钟 (避免整点拥堵),固定 7 分
80+
minute=7
81+
self="$(realpath "$0" 2> /dev/null || echo "$0")"
82+
83+
mkdir -p "$CRON_DIR" 2> /dev/null || true
84+
# 标准 5 字段:分 时 日 月 周;每 interval 小时的第 minute 分执行
85+
printf '%s */%s * * * sh "%s" run\n' "$minute" "$interval" "$self" > "$CRONTAB_FILE"
86+
87+
# 后台启动 crond (-c 指定 crontab 目录,-b 后台)
88+
"$BUSYBOX" crond -c "$CRON_DIR" -b -L /dev/null 2> /dev/null \
89+
&& log "INFO" "订阅定时更新已启用:每 ${interval} 小时" \
90+
|| log "WARN" "crond 启动失败,定时更新未生效"
91+
}
92+
93+
#######################################
94+
# 主入口
95+
#######################################
96+
main() {
97+
case "${1:-}" in
98+
sync) sync_cron ;;
99+
stop) stop_cron ;;
100+
run) run_update ;;
101+
*)
102+
printf "用法: %s {sync|stop|run}\n" "$(basename "$0")" >&2
103+
exit 1
104+
;;
105+
esac
106+
}
107+
108+
main "$@"
109+

0 commit comments

Comments
 (0)