• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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