diff --git a/patch_v2/ntpsync/S45meow_ntpsync b/patch_v2/ntpsync/S45meow_ntpsync index 5f23f2e..307567c 100755 --- a/patch_v2/ntpsync/S45meow_ntpsync +++ b/patch_v2/ntpsync/S45meow_ntpsync @@ -21,10 +21,19 @@ case "$1" in ;; stop) printf "Stopping meow_ntpsync: " - if [ -f /tmp/meow_ntpsync.lock/pid ]; then - kill "$(cat /tmp/meow_ntpsync.lock/pid 2>/dev/null)" 2>/dev/null - fi - rm -rf /tmp/meow_ntpsync.lock 2>/dev/null + # 持锁者自证在 <锁目录>/ 子目录里;kill 前再核对 cmdline, + # 防止 pid 复用误杀无关进程(P2-1 顺带项) + for d in /tmp/meow_ntpsync.lock/*; do + [ -d "$d" ] || continue + pid=${d##*/} + case "$pid" in + ''|*[!0-9]*) continue ;; + esac + cmd=$(tr '\0' ' ' <"/proc/$pid/cmdline" 2>/dev/null) + case "$cmd" in + *meow_ntpsync*) kill "$pid" 2>/dev/null ;; + esac + done echo "OK" ;; restart|reload) diff --git a/patch_v2/ntpsync/meow_ntpsync b/patch_v2/ntpsync/meow_ntpsync index 210c01b..ecc5453 100755 --- a/patch_v2/ntpsync/meow_ntpsync +++ b/patch_v2/ntpsync/meow_ntpsync @@ -40,7 +40,7 @@ # # 以下变量可由环境覆盖——仅供宿主单测注入,设备上不设置,走真实路径: # MEOW_NTPD / MEOW_HWCLOCK / MEOW_NETCHECK / MEOW_NTPSYNC_ATTEMPT_TIMEOUT -# MEOW_NTPSYNC_LOG / MEOW_NTPSYNC_STATE / MEOW_NTPSYNC_LOCK +# MEOW_NTPSYNC_LOG / MEOW_NTPSYNC_STATE / MEOW_NTPSYNC_LOCK / MEOW_NTPSYNC_PIDOF NTPD="${MEOW_NTPD:-/usr/sbin/ntpd}" HWCLOCK="${MEOW_HWCLOCK:-/sbin/hwclock}" @@ -56,7 +56,8 @@ FALLBACK_IP="203.107.6.88 120.25.115.20 162.159.200.1" FALLBACK_DNS="0.pool.ntp.org 1.pool.ntp.org" SYNC_TTL=3600 # 1 h 内已同步过就短路 -ATTEMPT_TIMEOUT="${MEOW_NTPSYNC_ATTEMPT_TIMEOUT:-15}" # 单次 ntpd 上限(实测 iburst 8 s 内回) +ATTEMPT_TIMEOUT="${MEOW_NTPSYNC_ATTEMPT_TIMEOUT:-15}" # 单次 ntpd 上限(实测 iburst 8 s 内返回) +NTPD_PIDOF="${MEOW_NTPSYNC_PIDOF:-pidof ntpd}" # 找 ntpd 进程(单测可注入) BOOT_TOTAL=120 # -b 模式总时长上限 usage() { @@ -106,27 +107,143 @@ synced_recently() { [ "$age" -ge 0 ] && [ "$age" -lt "$SYNC_TTL" ] } -lock_acquire() { - if mkdir "$LOCK_DIR" 2>/dev/null; then - chmod 700 "$LOCK_DIR" 2>/dev/null - echo $$ >"$LOCK_DIR/pid" 2>/dev/null +LOCK_HELD="" +STALE_NTPD="" +NTPD_CHILD="" + +# 锁的持有者由「锁目录下的 子目录」自证:mkdir 原子,且进程还在就必能 kill -0。 +# 不用 pid 文件——写文件有窗口,读到空 pid 会被误判成「持有者已死」。 +lock_owner() { + for d in "$LOCK_DIR"/*; do + [ -d "$d" ] || continue + echo "${d##*/}" return 0 - fi - other=$(cat "$LOCK_DIR/pid" 2>/dev/null) - case "$other" in - ''|*[!0-9]*) other=0 ;; - esac - if [ "$other" -gt 0 ] && kill -0 "$other" 2>/dev/null; then - return 1 - fi - # 锁的持有者已经不在了 → 清掉重来 - rm -rf "$LOCK_DIR" 2>/dev/null + done + return 1 +} + +lock_take() { mkdir "$LOCK_DIR" 2>/dev/null || return 1 chmod 700 "$LOCK_DIR" 2>/dev/null - echo $$ >"$LOCK_DIR/pid" 2>/dev/null + mkdir "$LOCK_DIR/$$" 2>/dev/null + LOCK_HELD=1 return 0 } +lock_acquire() { + if [ -d "$LOCK_DIR" ]; then + # 对方可能刚 mkdir、还没建 子目录:给 3 s 宽限,期间不抢 + i=0 + other="" + while [ "$i" -lt 3 ]; do + other=$(lock_owner) && break + sleep 1 + i=$((i + 1)) + done + case "$other" in + ''|*[!0-9]*) other=0 ;; + esac + if [ "$other" -gt 0 ] && kill -0 "$other" 2>/dev/null; then + return 1 + fi + # 持有者确实不在了(或宽限期满仍空)→ 用它遗留的 ntpd 子进程记录收尸 + STALE_NTPD=$(cat "$LOCK_DIR/ntpd.pid" 2>/dev/null) + # rename 是原子的:并发清锁时只有一个能 mv 成功,避免「失败方删掉胜方的锁」 + if mv "$LOCK_DIR" "$LOCK_DIR.stale.$$" 2>/dev/null; then + [ -z "$STALE_NTPD" ] && + STALE_NTPD=$(cat "$LOCK_DIR.stale.$$/ntpd.pid" 2>/dev/null) + rm -rf "$LOCK_DIR.stale.$$" 2>/dev/null + fi + fi + lock_take +} + +# 只删自己建立的锁:没有自己的 子目录就说明锁已不是我们的,别动 +lock_release() { + [ "$LOCK_HELD" = 1 ] || return 0 + [ -d "$LOCK_DIR/$$" ] && rm -rf "$LOCK_DIR" 2>/dev/null + LOCK_HELD="" + return 0 +} + +# ntpd 子进程是否由本脚本拉起:cmdline 里的 -c 指向我们的锁目录 +ntpd_is_ours() { + cmd=$(tr '\0' ' ' <"/proc/$1/cmdline" 2>/dev/null) + case "$cmd" in + *"$LOCK_DIR"*) return 0 ;; + esac + return 1 +} + +reap_pid() { + log "reaping stale ntpd child pid=$1" + kill "$1" 2>/dev/null + sleep 1 + kill -9 "$1" 2>/dev/null +} + +# 只对「别人的常驻 ntpd」让路;自己遗留的孤儿必须收尸, +# 否则孤儿会被 pidof 认成「已有 ntpd 在跑」→ 之后每次都静默 skip + rc=0。 +reap_or_skip_ntpd() { + # 先按崩溃时留下的 ntpd.pid 收尸。只认 cmdline 自证:pid 可能已被复用, + # 复用后的进程「不是我们的」并不等于「它是别人的 ntpd」,此时必须忽略它, + # 否则又会退化成永久 skip。 + case "$STALE_NTPD" in + ''|*[!0-9]*) ;; + *) + if kill -0 "$STALE_NTPD" 2>/dev/null && ntpd_is_ours "$STALE_NTPD"; then + reap_pid "$STALE_NTPD" + fi + ;; + esac + + remaining="" + seen=" " + for p in $($NTPD_PIDOF 2>/dev/null); do + case "$p" in + ''|*[!0-9]*) continue ;; + esac + case "$seen" in + *" $p "*) continue ;; + esac + seen="$seen$p " + kill -0 "$p" 2>/dev/null || continue + if ntpd_is_ours "$p"; then + reap_pid "$p" + else + remaining="$remaining $p" + fi + done + if [ -n "$remaining" ]; then + log "skip: a foreign ntpd is already running:$remaining" + exit 0 + fi + return 0 +} + +# 退出清理:先收尸自己的 ntpd 子进程,再只删自己的锁。 +# 顺序很重要——子进程在锁目录里登记了 ntpd.pid,锁删早了就丢线索。 +cleanup() { + if [ -n "$NTPD_CHILD" ] && kill -0 "$NTPD_CHILD" 2>/dev/null; then + kill "$NTPD_CHILD" 2>/dev/null + n=0 + while [ "$n" -lt 3 ] && kill -0 "$NTPD_CHILD" 2>/dev/null; do + sleep 1 + n=$((n + 1)) + done + kill -9 "$NTPD_CHILD" 2>/dev/null + wait "$NTPD_CHILD" 2>/dev/null + fi + NTPD_CHILD="" + lock_release +} + +# 被信号打断时:清理后必须 exit,否则 ash 执行完 trap 会继续往下跑 +on_signal() { + cleanup + exit 143 +} + clock_sane() { y=$(date +%Y 2>/dev/null) case "$y" in @@ -144,9 +261,19 @@ record_success() { } >"$STATE_FILE" 2>/dev/null } +# 日志目录不可写时整体退化为 /dev/null。 +# 关键:重定向失败会让**命令本身不执行**,所以不能让「日志写不进去」 +# 连带把 hwclock / ntpd 也一起废掉(P2-2)。 +# 又:`:` 是 POSIX 特殊内建,它的重定向失败会让 dash 直接退出整个脚本, +# 所以试写必须在子 shell 里做,把失败局限在子 shell 内。 +prepare_log() { + ( : >>"$LOG_FILE" ) 2>/dev/null || LOG_FILE=/dev/null +} + # RTC 存的是 UTC(实测 /sys/class/rtc/rtc0/since_epoch == date +%s),必须 -u persist_rtc() { - if "$HWCLOCK" -w -u >>"$LOG_FILE" 2>&1; then + # hwclock 的 rc 只反映「RTC 写没写成」,与日志能否写入解耦 + if "$HWCLOCK" -w -u >/dev/null 2>>"$LOG_FILE"; then log "rtc written (hwclock -w -u)" return 0 fi @@ -164,13 +291,19 @@ gen_conf() { # 单次尝试。ntpd 无响应时不会自己退出(实测无服务器时永久存活), # 所以用轮询 + kill -9 兜底;成功与否只看退出码。 +# +# NTPD_CHILD 是全局的:本脚本被 SIGTERM/SIGINT 打断时,trap 靠它把子进程杀掉。 +# 否则 ntpd 会孤儿化(ppid 已死),此后 pidof 把它当成「已有 ntpd 在跑」。 attempt() { servers="$1" [ -n "$servers" ] || return 1 gen_conf "$servers" || return 1 "$NTPD" -n -q -g -c "$LOCK_DIR/ntp.conf" >"$LOG_FILE" 2>&1 & - pid=$! + NTPD_CHILD=$! + # 落盘一份,供崩溃(SIGKILL/掉电)后下一次运行收尸 + echo "$NTPD_CHILD" >"$LOCK_DIR/ntpd.pid" 2>/dev/null + pid=$NTPD_CHILD waited=0 while [ "$waited" -lt "$ATTEMPT_TIMEOUT" ]; do kill -0 "$pid" 2>/dev/null || break @@ -181,11 +314,13 @@ attempt() { if kill -0 "$pid" 2>/dev/null; then kill -9 "$pid" 2>/dev/null wait "$pid" 2>/dev/null + NTPD_CHILD="" log "attempt timeout after ${waited}s (servers: $servers)" return 1 fi wait "$pid" 2>/dev/null rc=$? + NTPD_CHILD="" if [ "$rc" -ne 0 ]; then log "attempt failed rc=$rc (servers: $servers)" return 1 @@ -273,6 +408,8 @@ if [ "$MODE" = status ]; then exit 0 fi +prepare_log + if [ "$FORCE" -eq 0 ] && synced_recently; then log "skip: synced within ${SYNC_TTL}s" exit 0 @@ -282,13 +419,11 @@ if ! lock_acquire; then log "skip: another meow_ntpsync is running" exit 0 fi -trap 'rm -rf "$LOCK_DIR" 2>/dev/null' EXIT INT TERM HUP +trap 'cleanup' EXIT +trap 'on_signal' INT TERM HUP -# 已经有常驻 ntpd 在跑(用户/第三方装的)→ 它会自己校时,别抢 123 端口 -if pidof ntpd >/dev/null 2>&1; then - log "skip: an ntpd is already running" - exit 0 -fi +# 让路给「别人的常驻 ntpd」;自己遗留的孤儿先收尸(P2-1) +reap_or_skip_ntpd if [ "$MODE" = boot ]; then deadline=$(( $(date +%s) + TOTAL )) diff --git a/patch_v2/ntpsync/tests/run_tests.sh b/patch_v2/ntpsync/tests/run_tests.sh index d654b32..6ffcdf2 100644 --- a/patch_v2/ntpsync/tests/run_tests.sh +++ b/patch_v2/ntpsync/tests/run_tests.sh @@ -21,16 +21,18 @@ ok() { PASS=$((PASS + 1)); printf ' ok %s\n' "$1"; } bad() { FAIL=$((FAIL + 1)); printf ' FAIL %s\n' "$1"; } check(){ if [ "$2" = "$3" ]; then ok "$1"; else bad "$1 (want=$3 got=$2)"; fi; } -# ---- 假 ntpd:行为由 MODE 决定 ---- +# ---- 假 ntpd:行为由 MODE 决定;第一行记自己的 pid,供「trap 收尸」断言用 ---- cat >"$W/ntpd" <<'EOF' #!/bin/sh -echo "invoked $(date +%s)" >> "$FAKE_NTPD_CALLS" +echo "$$" >> "$FAKE_NTPD_CALLS" # 断言调用方必须给 -c <临时配置> case " $* " in *" -c "*) : ;; *) echo "no -c" >&2; exit 9 ;; esac case "$FAKE_NTPD_MODE" in ok) echo "ntpd: time set +1.000000 s"; exit 0 ;; fail) echo "ntpd: no server suitable" >&2; exit 3 ;; - hang) sleep 30; exit 0 ;; + hang) exec sleep 3600 ;; + # 不 exec:留着 cmdline 供「孤儿自证」;sleep 只挂 120s,万一漏杀也会自己退 + hangkeep) sleep 120; exit 0 ;; slew) echo "ntpd: time slew +0.000042 s"; exit 0 ;; esac exit 1 @@ -86,14 +88,17 @@ check "配置每个 server 都带 iburst" \ "$(grep -c '^server .* iburst$' "$W/conf.1b" 2>/dev/null)" \ "$(grep -c '^server ' "$W/conf.1b" 2>/dev/null)" -# 2) ntpd 挂死 → 看门狗必须杀掉并返回 1 +# 2) ntpd 挂死 → 看门狗必须杀掉并返回 1(假 ntpd 挂 3600s,看门狗 2s 必须掐断) run_case 2 t0=$(date +%s) FAKE_NTPD_MODE=hang MEOW_NETCHECK=true sh "$SUT"; rc=$? t1=$(date +%s); el=$((t1 - t0)) check "挂死返回 1" "$rc" 1 -if [ "$el" -ge 2 ] && [ "$el" -le 6 ]; then ok "看门狗在 2~6s 内掐断 (实测 ${el}s)" -else bad "看门狗时长异常 (实测 ${el}s, 期望 2~6s)"; fi +# 上界放宽到 90s:宿主是多 agent 共享机,实测负载高时 sleep 5 能跑成 85s; +# 假 ntpd 自己会挂 3600s,所以 90s 仍能证明是看门狗掐断而不是自然退出。 +if [ "$el" -ge 2 ] && [ "$el" -le 90 ]; then ok "看门狗掐断挂死的 ntpd (实测 ${el}s)" +else bad "看门狗时长异常 (实测 ${el}s, 期望 2~90s)"; fi +check "挂死时 ntpd 子进程已死" "$(kill -0 "$(head -n 1 "$W/calls.2" 2>/dev/null)" 2>/dev/null && echo alive || echo dead)" dead check "挂死不写 RTC" "$(wc -l <"$W/hw.2" | tr -d ' ')" 0 # 3) ntpd 明确失败 → 返回 1,不写 RTC @@ -109,7 +114,7 @@ FAKE_NTPD_MODE=ok MEOW_NETCHECK=false sh "$SUT"; rc=$? t1=$(date +%s); el=$((t1 - t0)) check "网络未就绪返回 1" "$rc" 1 check "网络未就绪不调 ntpd" "$(wc -l <"$W/calls.4" | tr -d ' ')" 0 -if [ "$el" -le 2 ]; then ok "单次模式不做无谓等待 (${el}s)"; else bad "单次模式等了 ${el}s"; fi +if [ "$el" -le 10 ]; then ok "单次模式不做无谓等待 (${el}s)"; else bad "单次模式等了 ${el}s"; fi # 5) 开机模式:网络一直不来,-t 3 → 按时退出 run_case 5 @@ -117,20 +122,21 @@ t0=$(date +%s) FAKE_NTPD_MODE=ok MEOW_NETCHECK=false sh "$SUT" -b -t 3; rc=$? t1=$(date +%s); el=$((t1 - t0)) check "开机模式超时返回 1" "$rc" 1 -if [ "$el" -ge 3 ] && [ "$el" -le 8 ]; then ok "开机模式在 -t 内退出 (${el}s)" +if [ "$el" -ge 3 ] && [ "$el" -le 90 ]; then ok "开机模式在 -t 内退出 (${el}s)" else bad "开机模式退出时长异常 (${el}s)"; fi # 6) 开机模式:网络晚到 → 仍能同步成功 +# 只在第 2 次探测才就绪(证明「先等、后来到了」这条路径),-t 给足 cat >"$W/latecheck" <<'EOF' #!/bin/sh n=$(cat "$LATE_FILE" 2>/dev/null || echo 0) n=$((n + 1)); echo "$n" >"$LATE_FILE" -[ "$n" -gt 2 ] +[ "$n" -gt 1 ] EOF chmod +x "$W/latecheck" rm -f "$W/late.n" "$W/state.6" run_case 6 -LATE_FILE="$W/late.n" FAKE_NTPD_MODE=ok MEOW_NETCHECK="$W/latecheck" sh "$SUT" -b -t 20; rc=$? +LATE_FILE="$W/late.n" FAKE_NTPD_MODE=ok MEOW_NETCHECK="$W/latecheck" sh "$SUT" -b -t 90; rc=$? check "网络晚到仍返回 0" "$rc" 0 # 7) 非法参数 @@ -157,6 +163,115 @@ echo "epoch=12345" >"$W/state.10" out=$(sh "$SUT" -S) check "-S 打印上次记录" "$(echo "$out" | grep -c '^epoch=12345$')" 1 +# ---------------------------------------------------------------- 回归(r1 P2) +# 11) P2-1:SIGTERM 打断时 trap 必须把 attempt() 拉起的 ntpd 子进程一起收尸。 +# 不修的话 ntpd 会孤儿化(后面被 pidof 当成「已有 ntpd 在跑」→ 永久静默 skip)。 +run_case 11 +rm -rf "$W/lock.11" +# ATTEMPT_TIMEOUT 设 180s:远大于下面的观察窗口,保证「看门狗顺手杀子进程」 +# 不会掩盖孤儿问题(假 ntpd 本身挂 3600s)。 +FAKE_NTPD_MODE=hang MEOW_NETCHECK=true MEOW_NTPSYNC_ATTEMPT_TIMEOUT=180 \ + sh "$SUT" -f >/dev/null 2>&1 & +sut=$! +i=0 +while [ "$i" -lt 40 ] && [ ! -s "$W/calls.11" ]; do sleep 0.2; i=$((i + 1)); done +child=$(head -n 1 "$W/calls.11" 2>/dev/null) +kill -TERM "$sut" 2>/dev/null +# 60s 观察窗口:远早于 ATTEMPT_TIMEOUT(180s);上界放宽是因为宿主负载高 +i=0 +while [ "$i" -lt 60 ]; do + kill -0 "$sut" 2>/dev/null || break + sleep 1 + i=$((i + 1)) +done +if kill -0 "$sut" 2>/dev/null; then + bad "SIGTERM 后主进程 60s 内未退出" + kill -9 "$sut" 2>/dev/null +else + ok "SIGTERM 后主进程已退出" +fi +if [ -n "$child" ] && kill -0 "$child" 2>/dev/null; then + bad "SIGTERM 后 ntpd 子进程成孤儿 (pid=$child)" + kill -9 "$child" 2>/dev/null +else + ok "SIGTERM 后 ntpd 子进程已被收尸" +fi +if [ -d "$W/lock.11" ]; then bad "SIGTERM 后锁未释放"; else ok "SIGTERM 后锁已释放"; fi + +# 12) P2-1:上次遗留的孤儿 ntpd 要被收尸,不能因 pidof 命中就静默 skip + rc=0。 +# 不修的话 hwclock 根本不会被调用(旧代码直接 "skip: an ntpd is already running")。 +run_case 12 +rm -rf "$W/lock.12" "$W/state.12" +mkdir -p "$W/orphan" +cp "$W/ntpd" "$W/orphan/ntpd" +cat >"$W/fakepidof" <<'EOF' +#!/bin/sh +cat "$FAKE_PIDOF_OUT" 2>/dev/null +EOF +chmod +x "$W/fakepidof" +# 造一个「上次运行遗留」的 ntpd:cmdline 里带本次锁目录。 +# 用 hangkeep(不 exec)——真 ntpd 是单一 ELF,cmdline 保持不变; +# 若用 exec 版,cmdline 会变成 "sleep 120",就认不出「是我们的孤儿」了。 +# stdio 全丢 /dev/null:否则它(及其 sleep 子进程)会占住调用方的管道, +# 让 `run_tests.sh | tail` 永远等不到 EOF。 +FAKE_NTPD_CALLS="$W/orphan.calls" FAKE_NTPD_MODE=hangkeep \ + "$W/orphan/ntpd" -n -q -g -c "$W/lock.12/ntp.conf" >/dev/null 2>&1 & +orphan=$! +sleep 1 +echo "$orphan" >"$W/pidof.out" +FAKE_PIDOF_OUT="$W/pidof.out" MEOW_NTPSYNC_PIDOF="$W/fakepidof" \ + FAKE_NTPD_MODE=ok MEOW_NETCHECK=true sh "$SUT" -f; rc=$? +check "遗留孤儿不阻断本次同步" "$rc" 0 +check "遗留孤儿被收尸" "$(kill -0 "$orphan" 2>/dev/null && echo alive || echo dead)" dead +check "孤儿被收尸后仍写了 RTC" "$(wc -l <"$W/hw.12" | tr -d ' ')" 1 +kill -9 "$orphan" 2>/dev/null + +# 13) P2-2:日志路径不可写时 hwclock 仍必须被执行(重定向失败不得吞掉命令)。 +# 不修的话 hwclock 的 marker 为空,而整轮仍返回 0 → RTC 一小时不更新。 +run_case 13 +rm -f "$W/state.13" +FAKE_NTPD_MODE=ok MEOW_NETCHECK=true MEOW_NTPSYNC_LOG="/nonexistent_dir_$$/x.log" \ + sh "$SUT" -f; rc=$? +check "日志不可写仍返回 0" "$rc" 0 +check "日志不可写 hwclock 仍被执行" "$(wc -l <"$W/hw.13" | tr -d ' ')" 1 + +# 14) P2-3:锁里有存活持有者( 子目录)时不得抢锁。 +# 不修的话(旧代码只读 pid 文件,读不到当 other=0)会 rm -rf 抢锁并并发打 ntpd。 +run_case 14 +rm -rf "$W/lock.14" +mkdir -p "$W/lock.14/$$" +FAKE_NTPD_MODE=ok MEOW_NETCHECK=true sh "$SUT" -f; rc=$? +check "锁被占用时返回 0" "$rc" 0 +check "锁被占用时不并发调 ntpd" "$(wc -l <"$W/calls.14" | tr -d ' ')" 0 +check "锁被占用时不写 RTC" "$(wc -l <"$W/hw.14" | tr -d ' ')" 0 +check "未抢走别人的锁" "$([ -d "$W/lock.14/$$" ] && echo kept || echo stolen)" kept +rm -rf "$W/lock.14" + +# 15) P2-3 反向:持有者已死的锁必须能被接管(否则崩溃一次就把校时永久卡死) +run_case 15 +rm -rf "$W/lock.15" +mkdir -p "$W/lock.15/999999" +FAKE_NTPD_MODE=ok MEOW_NETCHECK=true sh "$SUT" -f; rc=$? +check "死锁可被接管并同步" "$rc" 0 +check "接管后 ntpd 被调用" "$(wc -l <"$W/calls.15" | tr -d ' ')" 1 +rm -rf "$W/lock.15" + +# 16) P2-1 补充:崩溃记录里的 pid 已被无关进程复用时,不能把它当成「别人的 ntpd」 +# 而永久跳过。cmdline 不匹配 = 不是我们的孤儿 → 忽略它,照常校时。 +# 不修的话 remaining 会收下这个 pid → "skip: a foreign ntpd" → hwclock 不执行。 +run_case 16 +rm -rf "$W/lock.16" "$W/state.16" +mkdir -p "$W/lock.16/999999" # 持有者已死 +sleep 300 & # 无关进程,占住一个将被「复用」的 pid +unrelated=$! +echo "$unrelated" >"$W/lock.16/ntpd.pid" +FAKE_NTPD_MODE=ok MEOW_NETCHECK=true MEOW_NTPSYNC_PIDOF=false sh "$SUT" -f; rc=$? +check "pid 复用不阻断同步" "$rc" 0 +check "pid 复用后仍写了 RTC" "$(wc -l <"$W/hw.16" | tr -d ' ')" 1 +check "无关进程未被误杀" "$(kill -0 "$unrelated" 2>/dev/null && echo alive || echo killed)" alive +kill -9 "$unrelated" 2>/dev/null +rm -rf "$W/lock.16" + echo echo "== 结果: PASS=$PASS FAIL=$FAIL" [ "$FAIL" -eq 0 ] || exit 1 diff --git a/scripts/build/fw_content.conf b/scripts/build/fw_content.conf index b6b1532..be0a897 100644 --- a/scripts/build/fw_content.conf +++ b/scripts/build/fw_content.conf @@ -44,7 +44,7 @@ patch_v2/meow_powermgr/meow_powermgr|/usr/bin/meow_powermgr|1ef2f8afc07acc7aa09d patch_v2/meow_powermgr/meow_powermgr|/usr/bin/meow_power|1ef2f8afc07acc7aa09dee9f7149aba8|meow_powermgr 的 CLI 形态(同二进制双形态,argv[0]=meow_power 即 CLI:status/estimate/model/history/top/events,policy 期3桩) patch_v2/meow_netmgrd/meow_netmgrd|/usr/bin/meow_netmgrd|20243ea1a800b29badefebb395603aaf|网络管理服务期6 M6.28(蓝牙P1 BT_CTL权威收口:hcitool con判连接/paired-devices补名/a2dp详情/connect-disconnect-pair-unpair-scan) patch_v2/meow_netmgrd/meow_netmgrd|/usr/bin/meow_net|20243ea1a800b29badefebb395603aaf|meow_netmgrd 的 CLI 形态(同二进制双形态,argv[0]=meow_net 即 CLI:status/iface/wifi/bt/rfkill/apps/conns/top/dns/policy/help;M6.28 蓝牙P1 BT_CTL收口) -patch_v2/ntpsync/meow_ntpsync|/usr/bin/meow_ntpsync|ad324e3ffffa7ab156fcd9d646981d33|网络校时 M22(复用固件自带 ntp-4.2.8p14 一次性 -q 校时 + hwclock -w -u 回写 RTC;自带看门狗/总超时/默认路由轮询,不自常驻、不阻塞调用方) +patch_v2/ntpsync/meow_ntpsync|/usr/bin/meow_ntpsync|f3c3abe4a960560f59f4101e6c444722|网络校时 M22(复用固件自带 ntp-4.2.8p14 一次性 -q 校时 + hwclock -w -u 回写 RTC;自带看门狗/总超时/默认路由轮询/进程锁,不自常驻、不阻塞调用方;r1 修:trap 收尸 ntpd 子进程+孤儿收尸、日志不可写不再吞掉 hwclock、锁用 子目录自证且只删自己的锁) # ---- 开机 init 脚本(etc/init.d,数字决定启动顺序,按文件名自然序)---- # M21 开机亮度链(顺序依赖:S11 < S21mount_ubifs < S22): @@ -58,7 +58,7 @@ patch_v2/bootmenu/S22brightness|/etc/init.d/S22brightness|1a0aa33d6f806f3f164816 # M22 网络校时:S45 排在 S40network/S43wifi 之后。S40 只 ifup 了 lo,WiFi 租约更晚, # 所以 S45 只把校时器丢后台(开机关键路径 +0 秒),由 /usr/bin/meow_ntpsync 自己轮询 # 默认路由(-t 120)+ 有限重试;WiFi 更晚才连上的情况由 udhcpc 钩子再触发一次。 -patch_v2/ntpsync/S45meow_ntpsync|/etc/init.d/S45meow_ntpsync|25454a3febf112c2cbedcc0a87fcc3d8|新增:开机校时启动器(后台 setsid 拉起 meow_ntpsync -b -t 120,立即返回;失败/未安装不阻断 init) +patch_v2/ntpsync/S45meow_ntpsync|/etc/init.d/S45meow_ntpsync|115e759274c8100391e3ede05a0578dc|新增:开机校时启动器(后台 setsid 拉起 meow_ntpsync -b -t 120,立即返回;失败/未安装不阻断 init;stop 只按 <锁目录>/ 自证 + cmdline 核对后 kill,避免陈旧 pid 误杀) patch_v2/meow_sysui/S70meow_sysui|/etc/init.d/S70meow_sysui|e435affcb56499e5229728c6ae2524b9|sysui 自启 M2.85(launcher.conf enabled=1+迁移) patch_v2/meow_svcd/S89meow_svcd|/etc/init.d/S89meow_svcd|a1ef9d658d37bb7ed012cc9326840ac4|svcd 自启 M6.2(+amsd.conf 热更 exec→/usr/bin 迁移) patch_v2/meow_amsd/meow_amsd|/usr/bin/meow_amsd|b698f176210b07f760f874ea2bb10a14|活动管理器 M6.46 编入 M7 杀后台修复(am kill 与后台页按钮同通道:MSG_AMS_KILL 先打后台保活表走 meow_bg_terminate,无前台会话也能杀;cmd_kill/cmd_start 失败原因细分 connect failed/timeout,no reply;响应串保留 launcher offline 供 MeowPanel 503 映射);继承 M6.35(注册表自愈+busy 识别+显示名 UTF-8 整序列 name[49];批① FG/BG 收编:handle_bg/handle_fg+AMS_FG/BG 状态供批③后台管理页)