rtime_clock/adjtime.rs
1//! Safe wrappers over the POSIX / Linux NTP kernel clock-adjustment APIs:
2//! `adjtimex(2)` (Linux), `clock_adjtime(2)` (Linux), and `ntp_adjtime(3)`
3//! (FreeBSD).
4//!
5//! These kernel APIs are not wrapped by `nix` or any other maintained
6//! crate. Consolidating them into this module contains every `unsafe`
7//! involved in NTP clock discipline to a single audited surface: four
8//! functions total. Every call site elsewhere in this crate can then use
9//! the wrappers without writing `unsafe` themselves.
10
11use std::io;
12
13/// A safe wrapper over `libc::timex`.
14///
15/// `Default` returns a fully zero-initialized struct, which is the
16/// documented way to prepare a `timex` before calling `adjtimex` or
17/// similar (a zero `modes` field means a read-only query).
18#[repr(transparent)]
19pub struct Timex(pub libc::timex);
20
21impl Default for Timex {
22 fn default() -> Self {
23 // SAFETY: `libc::timex` is `#[repr(C)]` and contains only integer
24 // fields and a `timeval`. An all-zero bit pattern is a valid
25 // inhabitant of every field, and is also the documented "unset"
26 // state that the kernel interprets as a read-only query.
27 Self(unsafe { std::mem::zeroed() })
28 }
29}
30
31impl Timex {
32 /// Create a fresh zero-initialized `Timex`.
33 pub fn new() -> Self {
34 Self::default()
35 }
36}
37
38/// Call `adjtimex(2)` on Linux.
39///
40/// Returns the kernel clock state on success (a non-negative integer).
41/// Callers set fields on `tx.0` before calling, and read fields from it
42/// afterwards for queries like frequency offset.
43#[cfg(target_os = "linux")]
44pub fn adjtimex(tx: &mut Timex) -> io::Result<libc::c_int> {
45 // SAFETY: `libc::adjtimex` requires a pointer to a valid, writable
46 // `libc::timex`. `tx.0` is owned by `tx` and we hold a `&mut`, so
47 // the pointer is valid, aligned, and exclusive for the duration of
48 // the call.
49 let ret = unsafe { libc::adjtimex(&mut tx.0 as *mut _) };
50 if ret < 0 {
51 return Err(io::Error::last_os_error());
52 }
53 Ok(ret)
54}
55
56/// Call `clock_adjtime(2)` on Linux for the given `clockid_t`.
57///
58/// Used for adjusting dynamic clocks (e.g. PTP hardware clocks) with the
59/// same `timex` interface as `adjtimex`.
60#[cfg(target_os = "linux")]
61pub fn clock_adjtime(clockid: libc::clockid_t, tx: &mut Timex) -> io::Result<libc::c_int> {
62 // SAFETY: `libc::clock_adjtime` requires a clockid and a pointer to
63 // a valid, writable `libc::timex`. The clockid is passed by value
64 // and `tx.0` is owned via `&mut`, so both requirements are met.
65 let ret = unsafe { libc::clock_adjtime(clockid, &mut tx.0 as *mut _) };
66 if ret < 0 {
67 return Err(io::Error::last_os_error());
68 }
69 Ok(ret)
70}
71
72/// Call `ntp_adjtime(3)` on FreeBSD (equivalent to Linux `adjtimex`).
73#[cfg(target_os = "freebsd")]
74pub fn ntp_adjtime(tx: &mut Timex) -> io::Result<libc::c_int> {
75 // SAFETY: `libc::ntp_adjtime` requires a pointer to a valid,
76 // writable `libc::timex`. `tx.0` is owned by `tx` and we hold
77 // `&mut`.
78 let ret = unsafe { libc::ntp_adjtime(&mut tx.0 as *mut _) };
79 if ret < 0 {
80 return Err(io::Error::last_os_error());
81 }
82 Ok(ret)
83}