Repository navigation
Expand file tree
/
Copy pathtimesync.py
More file actions
54 lines (44 loc) · 1.95 KB
/
Copy pathtimesync.py
File metadata and controls
54 lines (44 loc) · 1.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
"""Whether the system clock can be trusted yet.
The Pi has no RTC, so at boot the clock is wrong until systemd-timesyncd
reaches an NTP server over Wi-Fi. We ask systemd directly rather than guessing.
"""
from __future__ import annotations
import subprocess
import time
from pathlib import Path
from typing import Callable
#: Touched by systemd-timesyncd on every successful NTP exchange. Lives on
#: tmpfs, so it also does not exist before the first sync of the current boot.
SYNC_STAMP = Path("/run/systemd/timesync/synchronized")
def is_ntp_synced() -> bool:
"""Return True once systemd reports the clock as NTP-synchronized.
Any error (timedatectl missing, e.g. on a dev machine) is treated as
not-synced, which keeps the display showing dashes rather than a bad time.
"""
try:
result = subprocess.run(
["timedatectl", "show", "-p", "NTPSynchronized", "--value"],
capture_output=True,
text=True,
timeout=5,
)
except (OSError, subprocess.SubprocessError):
return False
return result.returncode == 0 and result.stdout.strip() == "yes"
def last_sync_age(
stamp: Path = SYNC_STAMP, *, now: Callable[[], float] = time.time
) -> float | None:
"""Seconds since timesyncd's last *successful* NTP sync, or None if unknown.
NTPSynchronized answers "is the clock still trustworthy" with hours of
hysteresis (the kernel's maxerror estimate takes ~9 h to expire), which is
right for the dashes but useless for noticing that syncs have stopped. The
stamp file's mtime tracks NTP reachability directly: it stops advancing the
moment sync responses stop arriving. A single stat(), so it is cheap enough
to check on every sync poll. None (no timesyncd, e.g. a dev machine) tells
the caller to fall back to is_ntp_synced() alone.
"""
try:
mtime = stamp.stat().st_mtime
except OSError:
return None
return max(0.0, now() - mtime)