r1 评审 verdict=p2-3items,三条均已按评审给的修法修掉,且都补了可证伪的回归断言。 P2-1 SIGTERM 后遗留孤儿 ntpd,之后每次静默 skip 且 rc=0 - attempt() 把子进程记进全局 NTPD_CHILD 并落盘 <锁目录>/ntpd.pid; 新增 cleanup()(EXIT trap)先收尸子进程再释放锁;新增 on_signal() (INT/TERM/HUP)清理后 exit 143——旧代码的 TERM trap 只清锁、不 exit, 被杀后还会继续跑完看门狗。 - 取代原来的 "pidof ntpd 就 skip":新增 reap_or_skip_ntpd(),用 /proc/<pid>/cmdline 是否含本锁目录来区分「我们自己遗留的孤儿」(收尸) 与「别人的常驻 ntpd」(让路)。崩溃后留下的 ntpd.pid 也据此校验, pid 被复用时不会误判成外部 daemon(不会永久 skip)。 - S45 stop 不再按陈旧 pid 文件盲杀:只从 <锁目录>/<pid> 取持有者, 再核对 /proc/<pid>/cmdline 含 meow_ntpsync 才 kill。 P2-2 日志重定向失败导致 hwclock 根本不执行 - persist_rtc 改 ">/dev/null 2>>$LOG_FILE":hwclock 的 rc 与日志写入解耦。 - 新增 prepare_log():启动时在子 shell 里试写日志,不可写就整体退化 /dev/null。 必须用子 shell——`:` 是 POSIX 特殊内建,它的重定向失败会让 dash 直接 退出整个脚本(本单自测先踩到了,rc=2)。 P2-3 锁 mkdir→写 pid 有窗口,并发抑制非原子 - 锁的持有者改由 <锁目录>/<pid> 子目录自证(mkdir 原子),不再写 pid 文件; 读到空锁时给 3s 宽限而不是立刻判死。 - 清失效锁改用 mv 到 <锁目录>.stale.$$(rename 原子),并发清锁时只有 一个能成功,避免「抢锁失败方删掉胜方的锁」。 - 退出只删自己的锁:没有自己的 <pid> 子目录就不动锁目录。 验证 - 新增回归断言 11-16(run_tests.sh):SIGTERM 收尸/锁释放、遗留孤儿收尸、 日志不可写仍写 RTC、锁被占用不抢、死锁可接管、pid 复用不阻断。 - 可证伪性:新套件跑旧代码(c9f0cba 的 meow_ntpsync)→ 6 条 FAIL (孤儿 alive、日志不可写 rc=1 且 hwclock 未执行、抢锁并发调 ntpd×3)。 再跑「仅回退 STALE_NTPD 硬化」的变体 → pid 复用那条 FAIL (rc=0 但 RTC 未写,正是评审指出的静默失效)。 - dash 与 busybox ash 各 41/41 PASS,rc=0;dash -n / busybox sh -n 均 0 错。 - 时间断言放宽为负载容忍(宿主是多 agent 共享机,实测 sleep 5 能跑成 85s), 假 ntpd 改为挂 3600s,保证上界仍能证明是看门狗掐断而非自然退出。 fw_content.conf: meow_ntpsync md5 ad324e3f→f3c3abe4, S45meow_ntpsync md5 25454a3f→115e7592(50meow_ntpsync 未改,md5 不变)。
449 lines
13 KiB
Bash
Executable File
449 lines
13 KiB
Bash
Executable File
#!/bin/sh
|
||
#
|
||
# meow_ntpsync — 网络校时(一次性,不常驻)M22
|
||
#
|
||
# ---- 为什么需要它(2026-11-06 固件 0.8.47 实测)----
|
||
# * 设备有 RTC:/dev/rtc0(驱动 10003000.rtc),内核开机 hctosys=1,
|
||
# 即系统时间来自 RTC;RTC 掉电保持(冷启动后不是 1970)。
|
||
# * 但 RTC 走时偏慢且**没有任何东西纠正**:实测系统时间比 NTP 慢 49 s,
|
||
# 把系统时间人为拨错 4 h 后观察 65 s,无人纠正,RTC 也不受系统时间影响。
|
||
# * 固件其实自带 ntp-4.2.8p14(/usr/sbin/ntpd)和 /etc/ntp.conf
|
||
# (4 个 pool.ntp.org),但**没有任何 init 脚本/进程启动它**
|
||
# → 根因是「有客户端、没启动」,不是「没有 NTP 客户端」。
|
||
# * udhcpc 的 default.script 不处理 option 42,实测热点也不下发。
|
||
#
|
||
# ---- 三条路线的取舍 ----
|
||
# ① 自写轻量 SNTP:要新增/交叉编译一个二进制,且 UDP 123 收发、超时、
|
||
# 时间戳换算都要自己写;收益与 ntpd 相同 → 放弃。
|
||
# ② 复用固件自带 ntpd(本脚本):零新增依赖、零体积增长,协议实现成熟,
|
||
# -g 允许开机大步进跳时。唯一坑是 ntpd 没有超时参数(无响应会一直等),
|
||
# 所以本脚本自带看门狗 kill -9。
|
||
# ③ 依赖 DHCP option 42:实测热点不下发,且 udhcpc 的 default.script
|
||
# 根本不读 ntpsrv → 不能作为唯一来源;但企业网可能有,
|
||
# 所以 dhcp 钩子把它当「网络就绪事件 + 可选服务器源」,而非唯一依赖。
|
||
#
|
||
# ---- 设计约束 ----
|
||
# * 不阻塞开机:调用方(S45 / udhcpc 钩子)一律后台拉起本脚本后立即返回。
|
||
# * 严格有界:单次 ntpd 上限 ATTEMPT_TIMEOUT 秒,-b 模式总时长受 -t 限制,
|
||
# 进程生命周期最长 ≈ -t + 2×ATTEMPT_TIMEOUT,之后自杀。
|
||
# * 网络未就绪不硬等:先轮询默认路由,出现才尝试校时。
|
||
# * 失败静默:只写 /var/log(tmpfs,不磨 NAND),不占 CPU、不留常驻进程。
|
||
# * 成功必须回写 RTC(hwclock -w -u),否则下次开机电又错了。
|
||
#
|
||
# 用法:
|
||
# meow_ntpsync -b [-t 秒] [-s "服务器 ..."] 开机模式:等默认路由 + 重试
|
||
# meow_ntpsync [-s "服务器 ..."] 立即试一次(udhcpc bound/renew)
|
||
# meow_ntpsync -S 打印上次同步记录
|
||
# meow_ntpsync -f ... 忽略「1 h 内已同步」短路
|
||
#
|
||
# 退出码: 0 = 时间可信(本次同步成功,或 1 h 内已同步过);1 = 失败/超时;2 = 用法错误
|
||
#
|
||
# 以下变量可由环境覆盖——仅供宿主单测注入,设备上不设置,走真实路径:
|
||
# MEOW_NTPD / MEOW_HWCLOCK / MEOW_NETCHECK / MEOW_NTPSYNC_ATTEMPT_TIMEOUT
|
||
# MEOW_NTPSYNC_LOG / MEOW_NTPSYNC_STATE / MEOW_NTPSYNC_LOCK / MEOW_NTPSYNC_PIDOF
|
||
|
||
NTPD="${MEOW_NTPD:-/usr/sbin/ntpd}"
|
||
HWCLOCK="${MEOW_HWCLOCK:-/sbin/hwclock}"
|
||
NETCHECK="${MEOW_NETCHECK:-}"
|
||
|
||
LOG_FILE="${MEOW_NTPSYNC_LOG:-/var/log/meow_ntpsync.log}"
|
||
STATE_FILE="${MEOW_NTPSYNC_STATE:-/tmp/meow_ntpsync.state}"
|
||
LOCK_DIR="${MEOW_NTPSYNC_LOCK:-/tmp/meow_ntpsync.lock}"
|
||
|
||
# 内置 IP 兜底(不依赖 DNS,国内可达):阿里 / 腾讯 / Cloudflare
|
||
FALLBACK_IP="203.107.6.88 120.25.115.20 162.159.200.1"
|
||
# DNS 名兜底:内置 IP 全废(被墙/下线)时最后再试一次
|
||
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 内返回)
|
||
NTPD_PIDOF="${MEOW_NTPSYNC_PIDOF:-pidof ntpd}" # 找 ntpd 进程(单测可注入)
|
||
BOOT_TOTAL=120 # -b 模式总时长上限
|
||
|
||
usage() {
|
||
cat <<EOF
|
||
用法: meow_ntpsync [-b] [-t 秒] [-s "服务器 ..."] [-f] [-q]
|
||
meow_ntpsync -S
|
||
-b 开机模式:先等默认路由出现,再校时,总时长受 -t 限制(默认 ${BOOT_TOTAL}s)
|
||
-t 开机模式总时长(秒)
|
||
-s DHCP option 42 下发的 NTP 服务器(空格分隔),优先于内置 IP
|
||
-f 强制同步,忽略「1 h 内已同步」短路
|
||
-q 不写日志
|
||
-S 打印上次同步记录
|
||
EOF
|
||
}
|
||
|
||
log() {
|
||
[ -n "$QUIET" ] && return 0
|
||
printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" >>"$LOG_FILE" 2>/dev/null
|
||
return 0
|
||
}
|
||
|
||
# 网络就绪 = 存在默认路由。不 ping、不解析 DNS,只查内核路由表(零网络开销)。
|
||
net_ready() {
|
||
if [ -n "$NETCHECK" ]; then
|
||
$NETCHECK >/dev/null 2>&1
|
||
return $?
|
||
fi
|
||
if [ -r /proc/net/route ]; then
|
||
awk 'NR>1 && $2=="00000000" {found=1; exit} END{exit !found}' \
|
||
/proc/net/route 2>/dev/null && return 0
|
||
fi
|
||
if command -v ip >/dev/null 2>&1; then
|
||
ip route 2>/dev/null | grep -q '^default ' && return 0
|
||
fi
|
||
return 1
|
||
}
|
||
|
||
synced_recently() {
|
||
[ -f "$STATE_FILE" ] || return 1
|
||
s_epoch=$(sed -n 's/^epoch=//p' "$STATE_FILE" 2>/dev/null | head -n 1)
|
||
case "$s_epoch" in
|
||
''|*[!0-9]*) return 1 ;;
|
||
esac
|
||
now=$(date +%s)
|
||
age=$((now - s_epoch))
|
||
# 时钟被往回拨时 age<0,视为未同步
|
||
[ "$age" -ge 0 ] && [ "$age" -lt "$SYNC_TTL" ]
|
||
}
|
||
|
||
LOCK_HELD=""
|
||
STALE_NTPD=""
|
||
NTPD_CHILD=""
|
||
|
||
# 锁的持有者由「锁目录下的 <pid> 子目录」自证:mkdir 原子,且进程还在就必能 kill -0。
|
||
# 不用 pid 文件——写文件有窗口,读到空 pid 会被误判成「持有者已死」。
|
||
lock_owner() {
|
||
for d in "$LOCK_DIR"/*; do
|
||
[ -d "$d" ] || continue
|
||
echo "${d##*/}"
|
||
return 0
|
||
done
|
||
return 1
|
||
}
|
||
|
||
lock_take() {
|
||
mkdir "$LOCK_DIR" 2>/dev/null || return 1
|
||
chmod 700 "$LOCK_DIR" 2>/dev/null
|
||
mkdir "$LOCK_DIR/$$" 2>/dev/null
|
||
LOCK_HELD=1
|
||
return 0
|
||
}
|
||
|
||
lock_acquire() {
|
||
if [ -d "$LOCK_DIR" ]; then
|
||
# 对方可能刚 mkdir、还没建 <pid> 子目录:给 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
|
||
}
|
||
|
||
# 只删自己建立的锁:没有自己的 <pid> 子目录就说明锁已不是我们的,别动
|
||
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
|
||
''|*[!0-9]*) return 1 ;;
|
||
esac
|
||
[ "$y" -ge 2020 ] && [ "$y" -le 2100 ]
|
||
}
|
||
|
||
record_success() {
|
||
now=$(date +%s)
|
||
{
|
||
echo "epoch=$now"
|
||
echo "date=$(date '+%Y-%m-%d %H:%M:%S')"
|
||
echo "servers=$1"
|
||
} >"$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() {
|
||
# hwclock 的 rc 只反映「RTC 写没写成」,与日志能否写入解耦
|
||
if "$HWCLOCK" -w -u >/dev/null 2>>"$LOG_FILE"; then
|
||
log "rtc written (hwclock -w -u)"
|
||
return 0
|
||
fi
|
||
log "rtc write FAILED (hwclock -w -u)"
|
||
return 1
|
||
}
|
||
|
||
# 生成 ntpd 临时配置。iburst 是必须的:实测不加 iburst 时首个 poll 要等 ~64 s,
|
||
# -q 根本来不及取到时间(加 iburst 后 8 s 内返回)。
|
||
gen_conf() {
|
||
for s in $1; do
|
||
echo "server $s iburst"
|
||
done >"$LOCK_DIR/ntp.conf" 2>/dev/null
|
||
}
|
||
|
||
# 单次尝试。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" </dev/null >>"$LOG_FILE" 2>&1 &
|
||
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
|
||
sleep 1
|
||
waited=$((waited + 1))
|
||
done
|
||
|
||
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
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
trim_log() {
|
||
[ -f "$LOG_FILE" ] || return 0
|
||
sz=$(wc -c <"$LOG_FILE" 2>/dev/null)
|
||
case "$sz" in
|
||
''|*[!0-9]*) return 0 ;;
|
||
esac
|
||
# /var/log 是 tmpfs,只防无限增长;保留最后 200 行
|
||
[ "$sz" -gt 65536 ] && tail -n 200 "$LOG_FILE" >"$LOG_FILE.tmp" 2>/dev/null &&
|
||
mv -f "$LOG_FILE.tmp" "$LOG_FILE" 2>/dev/null
|
||
return 0
|
||
}
|
||
|
||
# 一轮 = 先打「DHCP 服务器 + 内置 IP」(不需要 DNS),再打 DNS 名
|
||
sync_round() {
|
||
servers="$EXTRA $FALLBACK_IP"
|
||
trim_log
|
||
|
||
if attempt "$servers"; then
|
||
if clock_sane; then
|
||
record_success "$servers"
|
||
log "sync OK (servers: $servers)"
|
||
persist_rtc
|
||
return 0
|
||
fi
|
||
log "sync reported ok but clock still insane, ignore"
|
||
fi
|
||
|
||
if attempt "$FALLBACK_DNS"; then
|
||
if clock_sane; then
|
||
record_success "$FALLBACK_DNS"
|
||
log "sync OK (dns: $FALLBACK_DNS)"
|
||
persist_rtc
|
||
return 0
|
||
fi
|
||
log "dns sync reported ok but clock still insane, ignore"
|
||
fi
|
||
|
||
return 1
|
||
}
|
||
|
||
MODE=single
|
||
TOTAL=$BOOT_TOTAL
|
||
EXTRA=""
|
||
FORCE=0
|
||
QUIET=""
|
||
|
||
while [ $# -gt 0 ]; do
|
||
case "$1" in
|
||
-b) MODE=boot ;;
|
||
-t) TOTAL="$2"; shift ;;
|
||
-s) EXTRA="$2"; shift ;;
|
||
-f) FORCE=1 ;;
|
||
-q) QUIET=1 ;;
|
||
-S) MODE=status ;;
|
||
-h|--help) usage; exit 0 ;;
|
||
*)
|
||
echo "meow_ntpsync: unknown option: $1" >&2
|
||
usage >&2
|
||
exit 2
|
||
;;
|
||
esac
|
||
shift
|
||
done
|
||
|
||
case "$TOTAL" in
|
||
''|*[!0-9]*)
|
||
echo "meow_ntpsync: -t 必须是整数秒" >&2
|
||
exit 2
|
||
;;
|
||
esac
|
||
|
||
if [ "$MODE" = status ]; then
|
||
if [ -f "$STATE_FILE" ]; then
|
||
cat "$STATE_FILE"
|
||
else
|
||
echo "never synced"
|
||
fi
|
||
exit 0
|
||
fi
|
||
|
||
prepare_log
|
||
|
||
if [ "$FORCE" -eq 0 ] && synced_recently; then
|
||
log "skip: synced within ${SYNC_TTL}s"
|
||
exit 0
|
||
fi
|
||
|
||
if ! lock_acquire; then
|
||
log "skip: another meow_ntpsync is running"
|
||
exit 0
|
||
fi
|
||
trap 'cleanup' EXIT
|
||
trap 'on_signal' INT TERM HUP
|
||
|
||
# 让路给「别人的常驻 ntpd」;自己遗留的孤儿先收尸(P2-1)
|
||
reap_or_skip_ntpd
|
||
|
||
if [ "$MODE" = boot ]; then
|
||
deadline=$(( $(date +%s) + TOTAL ))
|
||
while :; do
|
||
if net_ready; then
|
||
log "network ready, syncing"
|
||
sync_round && exit 0
|
||
fi
|
||
[ "$(date +%s)" -ge "$deadline" ] && break
|
||
sleep 5
|
||
done
|
||
log "give up: no sync within ${TOTAL}s"
|
||
exit 1
|
||
fi
|
||
|
||
# 单次模式(udhcpc 钩子):此刻租约刚拿到,路由已装好,直接试
|
||
if ! net_ready; then
|
||
log "single: network not ready, skip"
|
||
exit 1
|
||
fi
|
||
sync_round && exit 0
|
||
exit 1
|