1 //! libc syscalls supporting `rustix::time`.
2
3 use super::super::c;
4 use super::super::conv::ret;
5 #[cfg(feature = "time")]
6 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
7 use super::super::time::types::LibcItimerspec;
8 use super::super::time::types::LibcTimespec;
9 use super::types::Timespec;
10 #[cfg(not(target_os = "wasi"))]
11 use super::types::{ClockId, DynamicClockId};
12 use crate::io;
13 use core::mem::MaybeUninit;
14 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
15 #[cfg(feature = "time")]
16 use {
17 super::super::conv::{borrowed_fd, ret_owned_fd},
18 crate::fd::{BorrowedFd, OwnedFd},
19 crate::time::{Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags},
20 };
21
22 #[cfg(all(
23 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
24 target_env = "gnu",
25 ))]
26 weak!(fn __clock_gettime64(c::clockid_t, *mut LibcTimespec) -> c::c_int);
27 #[cfg(all(
28 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
29 target_env = "gnu",
30 ))]
31 weak!(fn __clock_getres64(c::clockid_t, *mut LibcTimespec) -> c::c_int);
32 #[cfg(all(
33 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
34 target_env = "gnu",
35 ))]
36 #[cfg(feature = "time")]
37 weak!(fn __timerfd_gettime64(c::c_int, *mut LibcItimerspec) -> c::c_int);
38 #[cfg(all(
39 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
40 target_env = "gnu",
41 ))]
42 #[cfg(feature = "time")]
43 weak!(fn __timerfd_settime64(c::c_int, c::c_int, *const LibcItimerspec, *mut LibcItimerspec) -> c::c_int);
44
45 #[cfg(not(any(target_os = "redox", target_os = "wasi")))]
46 #[inline]
47 #[must_use]
clock_getres(id: ClockId) -> Timespec48 pub(crate) fn clock_getres(id: ClockId) -> Timespec {
49 let mut timespec = MaybeUninit::<LibcTimespec>::uninit();
50
51 // 32-bit gnu version: libc has `clock_getres` but it is not y2038 safe by
52 // default.
53 #[cfg(all(
54 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
55 target_env = "gnu",
56 ))]
57 unsafe {
58 if let Some(libc_clock_getres) = __clock_getres64.get() {
59 ret(libc_clock_getres(id as c::clockid_t, timespec.as_mut_ptr())).unwrap();
60 timespec.assume_init().into()
61 } else {
62 clock_getres_old(id)
63 }
64 }
65
66 // Main version: libc is y2038 safe and has `clock_getres`.
67 #[cfg(not(all(
68 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
69 target_env = "gnu",
70 )))]
71 unsafe {
72 let _ = c::clock_getres(id as c::clockid_t, timespec.as_mut_ptr());
73 timespec.assume_init()
74 }
75 }
76
77 #[cfg(all(
78 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
79 target_env = "gnu",
80 ))]
81 #[must_use]
clock_getres_old(id: ClockId) -> Timespec82 unsafe fn clock_getres_old(id: ClockId) -> Timespec {
83 let mut old_timespec = MaybeUninit::<c::timespec>::uninit();
84 ret(c::clock_getres(
85 id as c::clockid_t,
86 old_timespec.as_mut_ptr(),
87 ))
88 .unwrap();
89 let old_timespec = old_timespec.assume_init();
90 Timespec {
91 tv_sec: old_timespec.tv_sec.into(),
92 tv_nsec: old_timespec.tv_nsec.into(),
93 }
94 }
95
96 #[cfg(not(target_os = "wasi"))]
97 #[inline]
98 #[must_use]
clock_gettime(id: ClockId) -> Timespec99 pub(crate) fn clock_gettime(id: ClockId) -> Timespec {
100 let mut timespec = MaybeUninit::<LibcTimespec>::uninit();
101
102 #[cfg(all(
103 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
104 target_env = "gnu",
105 ))]
106 unsafe {
107 if let Some(libc_clock_gettime) = __clock_gettime64.get() {
108 ret(libc_clock_gettime(
109 id as c::clockid_t,
110 timespec.as_mut_ptr(),
111 ))
112 .unwrap();
113 timespec.assume_init().into()
114 } else {
115 clock_gettime_old(id)
116 }
117 }
118
119 // Use `unwrap()` here because `clock_getres` can fail if the clock itself
120 // overflows a number of seconds, but if that happens, the monotonic clocks
121 // can't maintain their invariants, or the realtime clocks aren't properly
122 // configured.
123 #[cfg(not(all(
124 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
125 target_env = "gnu",
126 )))]
127 unsafe {
128 ret(c::clock_gettime(id as c::clockid_t, timespec.as_mut_ptr())).unwrap();
129 timespec.assume_init()
130 }
131 }
132
133 #[cfg(all(
134 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
135 target_env = "gnu",
136 ))]
137 #[must_use]
clock_gettime_old(id: ClockId) -> Timespec138 unsafe fn clock_gettime_old(id: ClockId) -> Timespec {
139 let mut old_timespec = MaybeUninit::<c::timespec>::uninit();
140 ret(c::clock_gettime(
141 id as c::clockid_t,
142 old_timespec.as_mut_ptr(),
143 ))
144 .unwrap();
145 let old_timespec = old_timespec.assume_init();
146 Timespec {
147 tv_sec: old_timespec.tv_sec.into(),
148 tv_nsec: old_timespec.tv_nsec.into(),
149 }
150 }
151
152 #[cfg(not(target_os = "wasi"))]
153 #[inline]
clock_gettime_dynamic(id: DynamicClockId<'_>) -> io::Result<Timespec>154 pub(crate) fn clock_gettime_dynamic(id: DynamicClockId<'_>) -> io::Result<Timespec> {
155 let mut timespec = MaybeUninit::<LibcTimespec>::uninit();
156 unsafe {
157 let id: c::clockid_t = match id {
158 DynamicClockId::Known(id) => id as c::clockid_t,
159
160 #[cfg(any(target_os = "android", target_os = "linux"))]
161 DynamicClockId::Dynamic(fd) => {
162 use crate::fd::AsRawFd;
163 const CLOCKFD: i32 = 3;
164 (!fd.as_raw_fd() << 3) | CLOCKFD
165 }
166
167 #[cfg(not(any(target_os = "android", target_os = "linux")))]
168 DynamicClockId::Dynamic(_fd) => {
169 // Dynamic clocks are not supported on this platform.
170 return Err(io::Errno::INVAL);
171 }
172
173 #[cfg(any(target_os = "android", target_os = "linux"))]
174 DynamicClockId::RealtimeAlarm => c::CLOCK_REALTIME_ALARM,
175
176 #[cfg(any(target_os = "android", target_os = "linux"))]
177 DynamicClockId::Tai => c::CLOCK_TAI,
178
179 #[cfg(any(target_os = "android", target_os = "linux"))]
180 DynamicClockId::Boottime => c::CLOCK_BOOTTIME,
181
182 #[cfg(any(target_os = "android", target_os = "linux"))]
183 DynamicClockId::BoottimeAlarm => c::CLOCK_BOOTTIME_ALARM,
184 };
185
186 #[cfg(all(
187 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
188 target_env = "gnu",
189 ))]
190 {
191 if let Some(libc_clock_gettime) = __clock_gettime64.get() {
192 ret(libc_clock_gettime(
193 id as c::clockid_t,
194 timespec.as_mut_ptr(),
195 ))?;
196
197 Ok(timespec.assume_init().into())
198 } else {
199 clock_gettime_dynamic_old(id)
200 }
201 }
202
203 #[cfg(not(all(
204 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
205 target_env = "gnu",
206 )))]
207 {
208 ret(c::clock_gettime(id as c::clockid_t, timespec.as_mut_ptr()))?;
209
210 Ok(timespec.assume_init())
211 }
212 }
213 }
214
215 #[cfg(all(
216 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
217 target_env = "gnu",
218 ))]
219 #[inline]
clock_gettime_dynamic_old(id: c::clockid_t) -> io::Result<Timespec>220 unsafe fn clock_gettime_dynamic_old(id: c::clockid_t) -> io::Result<Timespec> {
221 let mut old_timespec = MaybeUninit::<c::timespec>::uninit();
222
223 ret(c::clock_gettime(
224 id as c::clockid_t,
225 old_timespec.as_mut_ptr(),
226 ))?;
227
228 let old_timespec = old_timespec.assume_init();
229 Ok(Timespec {
230 tv_sec: old_timespec.tv_sec.into(),
231 tv_nsec: old_timespec.tv_nsec.into(),
232 })
233 }
234
235 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
236 #[cfg(feature = "time")]
timerfd_create(id: TimerfdClockId, flags: TimerfdFlags) -> io::Result<OwnedFd>237 pub(crate) fn timerfd_create(id: TimerfdClockId, flags: TimerfdFlags) -> io::Result<OwnedFd> {
238 unsafe { ret_owned_fd(c::timerfd_create(id as c::clockid_t, flags.bits())) }
239 }
240
241 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
242 #[cfg(feature = "time")]
timerfd_settime( fd: BorrowedFd<'_>, flags: TimerfdTimerFlags, new_value: &Itimerspec, ) -> io::Result<Itimerspec>243 pub(crate) fn timerfd_settime(
244 fd: BorrowedFd<'_>,
245 flags: TimerfdTimerFlags,
246 new_value: &Itimerspec,
247 ) -> io::Result<Itimerspec> {
248 let mut result = MaybeUninit::<LibcItimerspec>::uninit();
249
250 #[cfg(all(
251 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
252 target_env = "gnu",
253 ))]
254 unsafe {
255 if let Some(libc_timerfd_settime) = __timerfd_settime64.get() {
256 ret(libc_timerfd_settime(
257 borrowed_fd(fd),
258 flags.bits(),
259 &new_value.clone().into(),
260 result.as_mut_ptr(),
261 ))?;
262 Ok(result.assume_init().into())
263 } else {
264 timerfd_settime_old(fd, flags, new_value)
265 }
266 }
267
268 #[cfg(not(all(
269 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
270 target_env = "gnu",
271 )))]
272 unsafe {
273 ret(c::timerfd_settime(
274 borrowed_fd(fd),
275 flags.bits(),
276 new_value,
277 result.as_mut_ptr(),
278 ))?;
279 Ok(result.assume_init())
280 }
281 }
282
283 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
284 #[cfg(all(
285 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
286 target_env = "gnu",
287 ))]
288 #[cfg(feature = "time")]
timerfd_settime_old( fd: BorrowedFd<'_>, flags: TimerfdTimerFlags, new_value: &Itimerspec, ) -> io::Result<Itimerspec>289 unsafe fn timerfd_settime_old(
290 fd: BorrowedFd<'_>,
291 flags: TimerfdTimerFlags,
292 new_value: &Itimerspec,
293 ) -> io::Result<Itimerspec> {
294 use core::convert::TryInto;
295
296 let mut old_result = MaybeUninit::<c::itimerspec>::uninit();
297
298 // Convert `new_value` to the old `itimerspec` format.
299 let old_new_value = c::itimerspec {
300 it_interval: c::timespec {
301 tv_sec: new_value
302 .it_interval
303 .tv_sec
304 .try_into()
305 .map_err(|_| io::Errno::OVERFLOW)?,
306 tv_nsec: new_value
307 .it_interval
308 .tv_nsec
309 .try_into()
310 .map_err(|_| io::Errno::INVAL)?,
311 },
312 it_value: c::timespec {
313 tv_sec: new_value
314 .it_value
315 .tv_sec
316 .try_into()
317 .map_err(|_| io::Errno::OVERFLOW)?,
318 tv_nsec: new_value
319 .it_value
320 .tv_nsec
321 .try_into()
322 .map_err(|_| io::Errno::INVAL)?,
323 },
324 };
325
326 ret(c::timerfd_settime(
327 borrowed_fd(fd),
328 flags.bits(),
329 &old_new_value,
330 old_result.as_mut_ptr(),
331 ))?;
332
333 let old_result = old_result.assume_init();
334 Ok(Itimerspec {
335 it_interval: Timespec {
336 tv_sec: old_result
337 .it_interval
338 .tv_sec
339 .try_into()
340 .map_err(|_| io::Errno::OVERFLOW)?,
341 tv_nsec: old_result.it_interval.tv_nsec as _,
342 },
343 it_value: Timespec {
344 tv_sec: old_result
345 .it_interval
346 .tv_sec
347 .try_into()
348 .map_err(|_| io::Errno::OVERFLOW)?,
349 tv_nsec: old_result.it_interval.tv_nsec as _,
350 },
351 })
352 }
353
354 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
355 #[cfg(feature = "time")]
timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result<Itimerspec>356 pub(crate) fn timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> {
357 let mut result = MaybeUninit::<LibcItimerspec>::uninit();
358
359 #[cfg(all(
360 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
361 target_env = "gnu",
362 ))]
363 unsafe {
364 if let Some(libc_timerfd_gettime) = __timerfd_gettime64.get() {
365 ret(libc_timerfd_gettime(borrowed_fd(fd), result.as_mut_ptr()))?;
366 Ok(result.assume_init().into())
367 } else {
368 timerfd_gettime_old(fd)
369 }
370 }
371
372 #[cfg(not(all(
373 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
374 target_env = "gnu",
375 )))]
376 unsafe {
377 ret(c::timerfd_gettime(borrowed_fd(fd), result.as_mut_ptr()))?;
378 Ok(result.assume_init())
379 }
380 }
381
382 #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
383 #[cfg(all(
384 any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
385 target_env = "gnu",
386 ))]
387 #[cfg(feature = "time")]
timerfd_gettime_old(fd: BorrowedFd<'_>) -> io::Result<Itimerspec>388 unsafe fn timerfd_gettime_old(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> {
389 use core::convert::TryInto;
390
391 let mut old_result = MaybeUninit::<c::itimerspec>::uninit();
392
393 ret(c::timerfd_gettime(borrowed_fd(fd), old_result.as_mut_ptr()))?;
394
395 let old_result = old_result.assume_init();
396 Ok(Itimerspec {
397 it_interval: Timespec {
398 tv_sec: old_result
399 .it_interval
400 .tv_sec
401 .try_into()
402 .map_err(|_| io::Errno::OVERFLOW)?,
403 tv_nsec: old_result.it_interval.tv_nsec as _,
404 },
405 it_value: Timespec {
406 tv_sec: old_result
407 .it_interval
408 .tv_sec
409 .try_into()
410 .map_err(|_| io::Errno::OVERFLOW)?,
411 tv_nsec: old_result.it_interval.tv_nsec as _,
412 },
413 })
414 }
415