From 88eb7efd8a6d70879351fba1a419d766ddbcdde8 Mon Sep 17 00:00:00 2001 From: cyxc1124 Date: Sat, 3 Oct 2026 04:25:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(cron):=20=E9=98=B2=E6=AD=A2=E6=9D=83?= =?UTF-8?q?=E9=99=90=E6=88=96=E8=AF=BB=E5=8F=96=E5=A4=B1=E8=B4=A5=E6=B8=85?= =?UTF-8?q?=E7=A9=BA=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/libs/set_cron.sh | 114 +++++++++++++++++++++++--------- scripts/start.sh | 18 ++--- scripts/starts/afstart.sh | 29 +++++--- scripts/starts/snapshot_init.sh | 3 +- 4 files changed, 116 insertions(+), 48 deletions(-) diff --git a/scripts/libs/set_cron.sh b/scripts/libs/set_cron.sh index a586d1d2..11eb8d83 100644 --- a/scripts/libs/set_cron.sh +++ b/scripts/libs/set_cron.sh @@ -1,41 +1,95 @@ - crondir="$(crond -h 2>&1 | grep -oE 'Default:.*' | awk -F ":" '{print $2}'| tr -d ' ')" [ ! -w "$crondir" ] && crondir="/etc/storage/cron/crontabs" [ ! -w "$crondir" ] && crondir="/var/spool/cron/crontabs" [ ! -w "$crondir" ] && crondir="/var/spool/cron" -[ -z "$USER" ] && USER=$(whoami 2>/dev/null) +[ -z "$USER" ] && USER=$(id -un) [ -z "$TASKCFGDIR" ] && TASKCFGDIR="$CRASHDIR"/configs/task -tmpcron=/tmp/cron_tmp -touch "$tmpcron" -cronadd() { #定时任务工具 - if crontab -h 2>&1 | grep -q '\-l'; then - crontab "$1" - elif [ -w "$crondir" ];then - cat "$1" >"$crondir"/"$USER" && cru a REFRESH "0 0 1 1 * /bin/true" 2>/dev/null - else - echo "找不到可用的crond或者crontab应用!No available crond or crontab application can be found!" - fi -} -cronload() { #定时任务工具 - if crontab -h 2>&1 | grep -q '\-l'; then - crontab -l - elif [ -f "$crondir/$USER" ];then - cat "$crondir"/"$USER" 2>/dev/null - else +# ShellCrash 使用的 ash/bash(以及 dash)均支持 test -O。 +# shellcheck disable=SC3067 +croncheck() { #在任何写操作前检查实例管理权限,保留普通用户自己的安装 + cron_uid=$(id -u) || return 1 + [ "$cron_uid" = 0 ] && return 0 + if [ ! -O "$CRASHDIR/configs/ShellCrash.cfg" ] || [ ! -w "$CRASHDIR/configs/ShellCrash.cfg" ]; then + echo '无权管理此 ShellCrash 实例,请使用安装用户或 root。' >&2 return 1 fi + for cron_pid in $(pidof CrashCore 2>/dev/null); do + if [ ! -O "/proc/$cron_pid" ]; then + echo '无权管理其他用户启动的 CrashCore。' >&2 + return 1 + fi + done } -cronset() { #定时任务设置 - # 参数1代表要移除的关键字,参数2代表要添加的任务语句 - cronload | grep -v '^$' | grep -vF "$1" >"$tmpcron" - [ -n "$2" ] && echo "$2" >>"$tmpcron" - cronadd "$tmpcron" - #华硕/Padavan固件存档在本地,其他则删除 - if [ -d /jffs ] || [ -d /etc/storage/ShellCrash ];then - mkdir -p "$TASKCFGDIR" - mv -f "$tmpcron" "$TASKCFGDIR"/cron + +cronadd() ( #定时任务工具;子 shell 隔离临时变量和清理 trap + croncheck || exit 1 + [ -f "$1" ] && [ -r "$1" ] || exit 1 + if crontab -h 2>&1 | grep -q '\-l'; then + crontab "$1" + elif [ -w "$crondir" ]; then + cron_user=$(id -un) || exit 1 + cron_stage=$(mktemp "$crondir/.shellcrash.XXXXXX") || exit 1 + trap 'rm -f "$cron_stage"' 0 + trap 'exit 1' 1 2 3 15 + [ ! -e "$crondir/$cron_user" ] || cp -p "$crondir/$cron_user" "$cron_stage" || exit 1 + cat "$1" >"$cron_stage" && mv -f "$cron_stage" "$crondir/$cron_user" || exit 1 + if command -v cru >/dev/null 2>&1; then + cru a REFRESH "0 0 1 1 * /bin/true" + fi else - rm -f "$tmpcron" + echo '找不到可用的 crond 或 crontab!No available crond or crontab!' >&2 + exit 1 fi -} +) + +cronload() ( + cron_user=$(id -un) || exit 1 + if crontab -h 2>&1 | grep -q '\-l'; then + cron_text=$(LC_ALL=C crontab -l 2>&1) + cron_status=$? + if [ "$cron_status" = 0 ]; then + [ -z "$cron_text" ] || printf '%s\n' "$cron_text" + exit 0 + fi + #首次使用没有 crontab 是合法空表,其他错误必须传回调用者。 + case "$cron_text" in + "no crontab for $cron_user" | "crontab: can't open '$cron_user': No such file or directory") exit 0 ;; + esac + printf '%s\n' "$cron_text" >&2 + exit "$cron_status" + elif [ -d "$crondir" ] && [ -r "$crondir" ] && [ -x "$crondir" ]; then + [ -e "$crondir/$cron_user" ] || [ -L "$crondir/$cron_user" ] || exit 0 + cat "$crondir/$cron_user" + else + echo '无法读取原有 crontab,已取消修改。' >&2 + exit 1 + fi +) + +cronupdate() ( #成功读取、生成后才提交;参数是读取 stdin 的过滤命令 + croncheck || exit 1 + cron_tmp=$(mktemp -d /tmp/ShellCrash-cron.XXXXXX) || exit 1 + cron_saved= + trap 'rm -rf "$cron_tmp"; [ -z "$cron_saved" ] || rm -f "$cron_saved"' 0 + trap 'exit 1' 1 2 3 15 + cronload >"$cron_tmp/old" || exit 1 + "$@" <"$cron_tmp/old" >"$cron_tmp/new" || exit 1 + #先准备华硕/Padavan 的持久化副本;任何提交前的写入失败都保留原表。 + if [ "$cron_persist" = true ] && { [ -d /jffs ] || [ -d /etc/storage/ShellCrash ]; }; then + mkdir -p "$TASKCFGDIR" || exit 1 + cron_saved=$(mktemp "$TASKCFGDIR/.cron.XXXXXX") || exit 1 + cat "$cron_tmp/new" >"$cron_saved" || exit 1 + fi + cronadd "$cron_tmp/new" || exit 1 + [ -z "$cron_saved" ] || mv -f "$cron_saved" "$TASKCFGDIR/cron" +) + +cronset() ( #参数1:移除的文字;参数2:新增任务。空结果也是合法 crontab。 + cron_persist=true + # shellcheck disable=SC2016 + CRON_KEY="$1" CRON_TASK="$2" cronupdate awk ' + NF && !index($0, ENVIRON["CRON_KEY"]) { print } + END { if (ENVIRON["CRON_TASK"] != "") print ENVIRON["CRON_TASK"] } + ' +) diff --git a/scripts/start.sh b/scripts/start.sh index 8fa387aa..f404de7d 100644 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -6,10 +6,11 @@ cd $(dirname $0) pwd ) +. "$CRASHDIR"/libs/set_cron.sh +croncheck || exit 1 . "$CRASHDIR"/libs/get_config.sh #加载工具 . "$CRASHDIR"/libs/set_config.sh -. "$CRASHDIR"/libs/set_cron.sh . "$CRASHDIR"/libs/check_cmd.sh . "$CRASHDIR"/libs/check_autostart.sh . "$CRASHDIR"/libs/compare.sh @@ -36,7 +37,9 @@ start_l(){ case "$1" in start) - [ -n "$(pidof CrashCore)" ] && $0 stop #禁止多实例 + if [ -n "$(pidof CrashCore)" ]; then #禁止多实例 + "$0" stop || exit 1 + fi stop_firewall #清理路由策略 rm -f "$CRASHDIR"/\.start_error #移除自启失败标记 #使用不同方式启动服务 @@ -69,9 +72,7 @@ stop) logger ShellCrash服务即将关闭...... [ -n "$(pidof CrashCore)" ] && web_save #保存面板配置 #清理定时任务 - cronload | grep -vE '^$|start_legacy_wd.sh|运行时每' > "$TMPDIR"/cron_tmp - cronadd "$TMPDIR"/cron_tmp - rm -f "$TMPDIR"/cron_tmp + cronupdate awk 'NF && !/start_legacy_wd.sh|运行时每/' || exit 1 #停止tg_bot . "$CRASHDIR"/menus/bot_tg_service.sh && bot_tg_stop #多种方式结束进程 @@ -96,8 +97,7 @@ stop) rm -rf "$TMPDIR"/CrashCore ;; restart) - $0 stop - $0 start + "$0" stop && "$0" start ;; init) . "$CRASHDIR"/starts/general_init.sh @@ -110,7 +110,9 @@ daemon) fi ;; debug) - [ -n "$(pidof CrashCore)" ] && $0 stop >/dev/null #禁止多实例 + if [ -n "$(pidof CrashCore)" ]; then #禁止多实例 + "$0" stop >/dev/null || exit 1 + fi stop_firewall >/dev/null #清理路由策略 bfstart if [ -n "$2" ]; then diff --git a/scripts/starts/afstart.sh b/scripts/starts/afstart.sh index d798a517..27c8216c 100644 --- a/scripts/starts/afstart.sh +++ b/scripts/starts/afstart.sh @@ -7,11 +7,12 @@ cd .. pwd ) +. "$CRASHDIR"/libs/set_cron.sh +croncheck || exit 1 . "$CRASHDIR"/libs/get_config.sh #加载工具 . "$CRASHDIR"/libs/check_cmd.sh . "$CRASHDIR"/libs/logger.sh -. "$CRASHDIR"/libs/set_cron.sh #缺省值 [ -z "$firewall_area" ] && firewall_area=1 #延迟启动 @@ -37,14 +38,24 @@ if [ -n "$test" -o -n "$(pidof CrashCore)" ]; then } & ckcmd mtd_storage.sh && mtd_storage.sh save >/dev/null 2>&1 #Padavan保存/etc/storage #加载定时任务 - cronload | grep -v '^$' >"$TMPDIR"/cron_tmp - [ -s "$TASKCFGDIR"/cron ] && cat "$TASKCFGDIR"/cron >>"$TMPDIR"/cron_tmp - [ -s "$TASKCFGDIR"/running ] && cat "$TASKCFGDIR"/running >>"$TMPDIR"/cron_tmp - [ "$bot_tg_service" = ON ] && echo "* * * * * /bin/sh $CRASHDIR/starts/start_legacy_wd.sh bot_tg #ShellCrash-TG_BOT守护进程" >>"$TMPDIR"/cron_tmp - [ "$start_old" = ON ] && echo "* * * * * /bin/sh $CRASHDIR/starts/start_legacy_wd.sh shellcrash #ShellCrash保守模式守护进程" >>"$TMPDIR"/cron_tmp - awk '!x[$0]++' "$TMPDIR"/cron_tmp >"$TMPDIR"/cron_tmp2 #删除重复行 - cronadd "$TMPDIR"/cron_tmp2 - rm -f "$TMPDIR"/cron_tmp "$TMPDIR"/cron_tmp2 + set -- - + for cron_file in "$TASKCFGDIR/cron" "$TASKCFGDIR/running"; do + [ ! -e "$cron_file" ] || set -- "$@" "$cron_file" + done + # shellcheck disable=SC2016 + CRON_BOT="$bot_tg_service" CRON_LEGACY="$start_old" CRON_DIR="$CRASHDIR" cronupdate awk ' + NF && !seen[$0]++ { print } + END { + if (ENVIRON["CRON_BOT"] == "ON") { + line = "* * * * * /bin/sh " ENVIRON["CRON_DIR"] "/starts/start_legacy_wd.sh bot_tg #ShellCrash-TG_BOT守护进程" + if (!seen[line]++) print line + } + if (ENVIRON["CRON_LEGACY"] == "ON") { + line = "* * * * * /bin/sh " ENVIRON["CRON_DIR"] "/starts/start_legacy_wd.sh shellcrash #ShellCrash保守模式守护进程" + if (!seen[line]++) print line + } + } + ' "$@" || exit 1 #加载条件任务 [ -s "$TASKCFGDIR"/afstart ] && { . "$TASKCFGDIR"/afstart; } & [ -s "$TASKCFGDIR"/affirewall -a -s /etc/init.d/firewall -a ! -f /etc/init.d/firewall.bak ] && { diff --git a/scripts/starts/snapshot_init.sh b/scripts/starts/snapshot_init.sh index 9445b7cf..514c6aa6 100644 --- a/scripts/starts/snapshot_init.sh +++ b/scripts/starts/snapshot_init.sh @@ -4,7 +4,6 @@ CRASHDIR="$(uci get firewall.ShellCrash.path | sed 's/\/starts.*//')" #防止提前自启 /etc/init.d/shellcrash disable -crontab -l | grep -v 'start_legacy_wd.sh shellcrash' | crontab - #防止usb未加载 i=0 while [ ! -f "$CRASHDIR/configs/ShellCrash.cfg" ]; do @@ -13,6 +12,8 @@ while [ ! -f "$CRASHDIR/configs/ShellCrash.cfg" ]; do sleep 3 done . "$CRASHDIR"/configs/ShellCrash.cfg +. "$CRASHDIR"/libs/set_cron.sh +cronset 'start_legacy_wd.sh shellcrash' || exit 1 autoSSH() { #自动开启SSH