1//! libc syscalls supporting `rustix::time`.
2
3use super::super::c;
4use super::super::conv::ret;
5#[cfg(feature = "time")]
6#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
7use super::super::time::types::LibcItimerspec;
8use super::super::time::types::LibcTimespec;
9use super::types::Timespec;
10#[cfg(not(target_os = "wasi"))]
11use super::types::{ClockId, DynamicClockId};
12use crate::io;
13use core::mem::MaybeUninit;
14#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
15#[cfg(feature = "time")]
16use {
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))]
26weak!(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))]
31weak!(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")]
37weak!(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")]
43weak!(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]
48pub(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]
82unsafe 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]
99pub(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]
138unsafe 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]
154pub(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]
220unsafe 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")]
237pub(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")]
243pub(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")]
289unsafe 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")]
356pub(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")]
388unsafe 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