1use rtime_core::clock::{Clock, ClockError};
2use rtime_core::timestamp::{NtpDuration, NtpTimestamp};
3
4use crate::adjtime::Timex;
5
6#[cfg(target_os = "freebsd")]
8const MOD_FREQUENCY: u32 = 0x0002;
9
10pub struct UnixClock {
12 adjustable: bool,
13}
14
15impl UnixClock {
16 pub fn new() -> Self {
18 let adjustable = Self::probe_adjustable();
19 Self { adjustable }
20 }
21
22 fn read_clock() -> Result<nix::sys::time::TimeSpec, ClockError> {
23 nix::time::clock_gettime(nix::time::ClockId::CLOCK_REALTIME)
24 .map_err(|e| ClockError::Os(e.into()))
25 }
26
27 #[cfg(target_os = "linux")]
30 fn probe_adjustable() -> bool {
31 let mut tx = Timex::new(); crate::adjtime::adjtimex(&mut tx).is_ok()
33 }
34
35 #[cfg(target_os = "freebsd")]
36 fn probe_adjustable() -> bool {
37 let mut tx = Timex::new(); crate::adjtime::ntp_adjtime(&mut tx).is_ok()
39 }
40
41 #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
42 fn probe_adjustable() -> bool {
43 false
44 }
45
46 #[cfg(target_os = "linux")]
49 fn step_impl(&self, offset: NtpDuration) -> Result<(), ClockError> {
50 let nanos = offset.to_nanos();
51 let mut tx = Timex::new();
52 tx.0.modes = libc::ADJ_SETOFFSET | libc::ADJ_NANO;
53 tx.0.time.tv_sec = nanos / 1_000_000_000;
54 tx.0.time.tv_usec = nanos % 1_000_000_000;
55
56 crate::adjtime::adjtimex(&mut tx).map_err(|err| {
57 if err.raw_os_error() == Some(libc::EPERM) {
58 ClockError::PermissionDenied
59 } else {
60 ClockError::Os(err)
61 }
62 })?;
63 Ok(())
64 }
65
66 #[cfg(target_os = "freebsd")]
67 fn step_impl(&self, offset: NtpDuration) -> Result<(), ClockError> {
68 use nix::sys::time::TimeSpec;
69 let ts = nix::time::clock_gettime(nix::time::ClockId::CLOCK_REALTIME)
71 .map_err(|e| ClockError::Os(e.into()))?;
72
73 let nanos = offset.to_nanos();
74 let mut tv_sec = ts.tv_sec() + nanos / 1_000_000_000;
75 let mut tv_nsec = ts.tv_nsec() + (nanos % 1_000_000_000) as libc::c_long;
76
77 while tv_nsec >= 1_000_000_000 {
79 tv_sec += 1;
80 tv_nsec -= 1_000_000_000;
81 }
82 while tv_nsec < 0 {
83 tv_sec -= 1;
84 tv_nsec += 1_000_000_000;
85 }
86
87 let new_ts = TimeSpec::new(tv_sec, tv_nsec);
88 nix::time::clock_settime(nix::time::ClockId::CLOCK_REALTIME, new_ts).map_err(|e| {
89 let err: std::io::Error = e.into();
90 if err.raw_os_error() == Some(libc::EPERM) {
91 ClockError::PermissionDenied
92 } else {
93 ClockError::Os(err)
94 }
95 })
96 }
97
98 #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
99 fn step_impl(&self, _offset: NtpDuration) -> Result<(), ClockError> {
100 Err(ClockError::NotSupported)
101 }
102
103 #[cfg(target_os = "linux")]
106 fn adjust_frequency_impl(&self, ppm: f64) -> Result<(), ClockError> {
107 let freq = (ppm * 65536.0) as i64;
109
110 let mut tx = Timex::new();
111 tx.0.modes = libc::ADJ_FREQUENCY;
112 tx.0.freq = freq;
113
114 crate::adjtime::adjtimex(&mut tx).map_err(|err| {
115 if err.raw_os_error() == Some(libc::EPERM) {
116 ClockError::PermissionDenied
117 } else {
118 ClockError::Os(err)
119 }
120 })?;
121 Ok(())
122 }
123
124 #[cfg(target_os = "freebsd")]
125 fn adjust_frequency_impl(&self, ppm: f64) -> Result<(), ClockError> {
126 let freq = (ppm * 65536.0) as i64;
127
128 let mut tx = Timex::new();
129 tx.0.modes = MOD_FREQUENCY as u32;
130 tx.0.freq = freq;
131
132 crate::adjtime::ntp_adjtime(&mut tx).map_err(|err| {
133 if err.raw_os_error() == Some(libc::EPERM) {
134 ClockError::PermissionDenied
135 } else {
136 ClockError::Os(err)
137 }
138 })?;
139 Ok(())
140 }
141
142 #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
143 fn adjust_frequency_impl(&self, _ppm: f64) -> Result<(), ClockError> {
144 Err(ClockError::NotSupported)
145 }
146
147 #[cfg(target_os = "linux")]
150 fn frequency_offset_impl(&self) -> Result<f64, ClockError> {
151 let mut tx = Timex::new(); crate::adjtime::adjtimex(&mut tx).map_err(ClockError::Os)?;
153 Ok(tx.0.freq as f64 / 65536.0)
154 }
155
156 #[cfg(target_os = "freebsd")]
157 fn frequency_offset_impl(&self) -> Result<f64, ClockError> {
158 let mut tx = Timex::new(); crate::adjtime::ntp_adjtime(&mut tx).map_err(ClockError::Os)?;
160 Ok(tx.0.freq as f64 / 65536.0)
161 }
162
163 #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
164 fn frequency_offset_impl(&self) -> Result<f64, ClockError> {
165 Err(ClockError::NotSupported)
166 }
167}
168
169impl Default for UnixClock {
170 fn default() -> Self {
171 Self::new()
172 }
173}
174
175impl Clock for UnixClock {
176 fn now(&self) -> Result<NtpTimestamp, ClockError> {
177 let ts = Self::read_clock()?;
178 let st = std::time::UNIX_EPOCH
179 + std::time::Duration::new(ts.tv_sec() as u64, ts.tv_nsec() as u32);
180 Ok(NtpTimestamp::from_system_time(st))
181 }
182
183 fn step(&self, offset: NtpDuration) -> Result<(), ClockError> {
184 if !self.adjustable {
185 return Err(ClockError::PermissionDenied);
186 }
187 self.step_impl(offset)
188 }
189
190 fn adjust_frequency(&self, ppm: f64) -> Result<(), ClockError> {
191 if !self.adjustable {
192 return Err(ClockError::PermissionDenied);
193 }
194 self.adjust_frequency_impl(ppm)
195 }
196
197 fn frequency_offset(&self) -> Result<f64, ClockError> {
198 self.frequency_offset_impl()
199 }
200
201 fn resolution(&self) -> NtpDuration {
202 NtpDuration::from_nanos(1)
204 }
205
206 fn max_frequency_adjustment(&self) -> f64 {
207 500.0
209 }
210
211 fn is_adjustable(&self) -> bool {
212 self.adjustable
213 }
214}