overlay: keep plausible time across reboots without an RTC - #2336
Conversation
Cameras have no battery-backed clock, so every boot starts at the firmware build stamp and an offline camera stamps each session's recordings with the same times over and over (#1886). Restore is forward-only at S02: the newer of a checkpoint file and the newest mtime under /etc wins, and only ever moves the clock ahead, so it composes with the build-stamp guard in S30customizer in either order. The mtime floor means even a camera that dies mid-write or never checkpoints resumes from its last organic config write, the way OpenWrt's sysfixtime does. Checkpointing is bounded by the flash the overlay sits on: hourly on jffs2 (a NOR overlay is a handful of erase blocks that jffs2 can only wear-level within), 15 min on ubifs (UBI levels across the whole NAND), 10 min on eMMC/SD media, never on tmpfs. busybox ntpd's -S hook reports sync events so the first sync is checkpointed immediately, and a small fallback loop covers the offline camera an unsynced ntpd never fires events for. A checkpoint below the build stamp is refused: a clock nothing ever set only launders garbage into the file. Co-authored-by: Daniel Banar <daniel.banar5@gmail.com>
PR Summary by QodoPreserve plausible camera time across RTC-less reboots
AI Description
Diagram
High-Level Assessment
Files changed (4)
|
Code Review by Qodo
1.
|
…p for sysupgrade Three findings from the review of #2336, each verified on the hi3516av300 before and after: The save lock was taken on an exec-opened descriptor, which the daemon then held across its whole next sleep - an ntpd first-sync checkpoint could stall behind it for a minute, exactly the write the hook exists to persist promptly. The lock now lives in a subshell and dies with the write; a hook save alongside a running daemon returns in under a second where it previously blocked. start() returned before the daemon had written its pidfile, so a stop issued in that window found nothing to kill and the daemon survived it. The parent now writes the same pid itself before reporting OK. sysupgrade stopped syslog, klogd, ntpd and cron before the flash but would have left this daemon running - a live overlay writer during the very erase quiesce_overlay() fences off. It is now on the stop list (its stop doubles as a final checkpoint, so the camera returns from the post-flash reboot with the freshest time it can have) and on the restore list for upgrades that abort before flashing.
What this solves
A camera without a battery-backed clock boots at the firmware build stamp (
TIME_STAMPin/etc/os-release, applied byS30customizer). On a camera with no network, every session therefore starts at the same moment: recordings collide with earlier ones, log timestamps repeat, and TLS validation trips over certificate validity windows. #1886 (@danielbanar) diagnosed this and proposed a fake-hwclock package; this PR supersedes it — the redesign below is why it is a new PR rather than a rework of that branch, and Daniel is credited as co-author.Why not #1886 as it stood
/etc/fake-hwclock.dataevery 5 seconds./etclives on the overlay — jffs2 on NOR, where the partition is a handful of 64 KB erase blocks (as small as 704 KB) that jffs2 can only wear-level within. That is ~6.3M writes a year against a ~100k-cycle budget.start-stop-daemonpattern the tree removed in init scripts: match daemons by name, not by a stale pidfile #2326.Design
Surveyed OpenWrt
sysfixtime, Debian/RPifake-hwclock, systemd-timesyncd, and OpenRCswclock(mechanisms verified in their sources). This is a combination of the pieces that fit a hard-power-cut camera:Restore (boot,
S02) — forward-only, from the best of three candidates:/etc/fake-hwclock.data;/etc(OpenWrt's trick: organic config writes keep this floor advancing for free, so even a camera that never checkpoints resumes from its last config write);/sys/class/rtc/rtc0/since_epochwhere one exists — read through sysfs rather thanhwclock -s, because a stale battery-less RTC must lose to a newer checkpoint, never yank the clock backwards.The clock is only ever moved forward, so this composes with
S30customizer's build-stamp guard in either order.Checkpoint (write to flash) — cadence bounded by what the overlay actually sits on:
Overridable via
/etc/default/fake-hwclock(INTERVAL=, 0 disables). Each save also refuses to persist a clock at or below the build stamp (a clock nothing set yet would only launder garbage into the file), never moves the checkpoint backwards, and writes the RTC forward where present.Three triggers share that one guarded path:
-Shook (S49ntpdnow passes-S /usr/sbin/ntpd-script):stratum/step— the first real sync — checkpoints immediately;periodic(every 11 min while synced) is rate-limited to the medium interval;unsyncis ignored.rcK), as a bonus, never as the mechanism.Evidence (hi3516av300, NOR flash, jffs2 overlay, IMX415)
Method: the camera ran today's master build (
2.6.08.29,master+fc923b7) with the four files from this branch copied onto its overlay — they are plain shell, so the copies are exactly what the image would ship (anhi3516av300_liteimage was also built from this branch to verify installation and size, but was not flashed). NTP was made unreachable by pointing/etc/ntp.confat 192.0.2.1 (TEST-NET). The camera was returned to stock afterwards and verified over a final reboot.Hard reset with a stale RTC and no NTP — power-cut simulation (
reboot -f, so no shutdown hook ran; RTC deliberately written back to the build era first):Boot came up ~19 s behind real time — the checkpoint's age. Stock behaviour for this state is the build stamp, ~17.6 h behind.
Guards (ntpd stopped):
Backwards-set never observed; a
loadwith a correct clock is a no-op (delta 0 s), including with a deliberately stale RTC present.ntpd hook: after
S49ntpd restartwith the-Sflag, the stratum event checkpointed within 5 s of sync;periodicevents did not write again before the interval elapsed (rate-limit observed).Daemon: with
INTERVAL=5override, periodic saves observed;stopkills the pid (verified against/proc/<pid>/cmdline, not blindly) and writes a final checkpoint.Image:
hi3516av300_litebuilds clean; rootfs 7624/8192 KB. The three scripts cost ~2.8 KB comment-stripped.Not tested on NAND/ubifs or eMMC boards — the medium detection is a
/proc/mountsfstype match and falls back to the most conservative (hourly) cadence for anything unrecognised.Supersedes #1886.
Co-authored-by: Daniel Banar daniel.banar5@gmail.com