rustix/backend/libc/time/
syscalls.rs

1//! libc syscalls supporting `rustix::time`.
2
3use crate::backend::c;
4use crate::backend::conv::ret;
5#[cfg(any(
6    linux_kernel,
7    target_os = "freebsd",
8    target_os = "fuchsia",
9    target_os = "illumos",
10    target_os = "netbsd"
11))]
12#[cfg(any(all(target_env = "gnu", fix_y2038), not(fix_y2038)))]
13use crate::backend::time::types::LibcItimerspec;
14#[cfg(not(target_os = "wasi"))]
15use crate::clockid::{ClockId, DynamicClockId};
16use crate::io;
17#[cfg(not(fix_y2038))]
18use crate::timespec::as_libc_timespec_mut_ptr;
19#[cfg(not(fix_y2038))]
20#[cfg(not(any(
21    target_os = "redox",
22    target_os = "wasi",
23    all(apple, not(target_os = "macos"))
24)))]
25use crate::timespec::as_libc_timespec_ptr;
26#[cfg(all(target_env = "gnu", fix_y2038))]
27use crate::timespec::LibcTimespec;
28use crate::timespec::Timespec;
29use core::mem::MaybeUninit;
30#[cfg(any(
31    linux_kernel,
32    target_os = "freebsd",
33    target_os = "fuchsia",
34    target_os = "illumos",
35    target_os = "netbsd"
36))]
37use {
38    crate::backend::conv::{borrowed_fd, ret_owned_fd},
39    crate::fd::{BorrowedFd, OwnedFd},
40    crate::time::{Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags},
41};
42
43#[cfg(all(target_env = "gnu", fix_y2038))]
44weak!(fn __clock_gettime64(c::clockid_t, *mut LibcTimespec) -> c::c_int);
45#[cfg(all(target_env = "gnu", fix_y2038))]
46weak!(fn __clock_settime64(c::clockid_t, *const LibcTimespec) -> c::c_int);
47#[cfg(all(target_env = "gnu", fix_y2038))]
48weak!(fn __clock_getres64(c::clockid_t, *mut LibcTimespec) -> c::c_int);
49#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))]
50#[cfg(all(target_env = "gnu", fix_y2038))]
51weak!(fn __timerfd_gettime64(c::c_int, *mut LibcItimerspec) -> c::c_int);
52#[cfg(any(linux_kernel, target_os = "freebsd", target_os = "fuchsia"))]
53#[cfg(all(target_env = "gnu", fix_y2038))]
54weak!(fn __timerfd_settime64(c::c_int, c::c_int, *const LibcItimerspec, *mut LibcItimerspec) -> c::c_int);
55
56#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
57#[inline]
58#[must_use]
59pub(crate) fn clock_getres(id: ClockId) -> Timespec {
60    // Old 32-bit version: libc has `clock_getres` but it is not y2038 safe by
61    // default. But there may be a `__clock_getres64` we can use.
62    #[cfg(fix_y2038)]
63    {
64        #[cfg(target_env = "gnu")]
65        if let Some(libc_clock_getres) = __clock_getres64.get() {
66            let mut timespec = MaybeUninit::<LibcTimespec>::uninit();
67            unsafe {
68                ret(libc_clock_getres(id as c::clockid_t, timespec.as_mut_ptr())).unwrap();
69                return timespec.assume_init().into();
70            }
71        }
72
73        clock_getres_old(id)
74    }
75
76    // Main version: libc is y2038 safe and has `clock_getres`.
77    #[cfg(not(fix_y2038))]
78    unsafe {
79        let mut timespec = MaybeUninit::<Timespec>::uninit();
80        let _ = c::clock_getres(id as c::clockid_t, as_libc_timespec_mut_ptr(&mut timespec));
81        timespec.assume_init()
82    }
83}
84
85#[cfg(fix_y2038)]
86#[must_use]
87fn clock_getres_old(id: ClockId) -> Timespec {
88    let mut old_timespec = MaybeUninit::<c::timespec>::uninit();
89
90    let old_timespec = unsafe {
91        ret(c::clock_getres(
92            id as c::clockid_t,
93            old_timespec.as_mut_ptr(),
94        ))
95        .unwrap();
96        old_timespec.assume_init()
97    };
98
99    Timespec {
100        tv_sec: old_timespec.tv_sec.into(),
101        tv_nsec: old_timespec.tv_nsec.into(),
102    }
103}
104
105#[cfg(not(target_os = "wasi"))]
106#[inline]
107#[must_use]
108pub(crate) fn clock_gettime(id: ClockId) -> Timespec {
109    // Old 32-bit version: libc has `clock_gettime` but it is not y2038 safe by
110    // default. But there may be a `__clock_gettime64` we can use.
111    #[cfg(fix_y2038)]
112    {
113        #[cfg(target_env = "gnu")]
114        if let Some(libc_clock_gettime) = __clock_gettime64.get() {
115            let mut timespec = MaybeUninit::<LibcTimespec>::uninit();
116            unsafe {
117                ret(libc_clock_gettime(
118                    id as c::clockid_t,
119                    timespec.as_mut_ptr(),
120                ))
121                .unwrap();
122                return timespec.assume_init().into();
123            }
124        }
125
126        clock_gettime_old(id)
127    }
128
129    // Use `.unwrap()` here because `clock_getres` can fail if the clock itself
130    // overflows a number of seconds, but if that happens, the monotonic clocks
131    // can't maintain their invariants, or the realtime clocks aren't properly
132    // configured.
133    #[cfg(not(fix_y2038))]
134    unsafe {
135        let mut timespec = MaybeUninit::<Timespec>::uninit();
136        ret(c::clock_gettime(
137            id as c::clockid_t,
138            as_libc_timespec_mut_ptr(&mut timespec),
139        ))
140        .unwrap();
141        let timespec = timespec.assume_init();
142        #[cfg(apple)]
143        let timespec = fix_negative_timespec_nsecs(timespec);
144        timespec
145    }
146}
147
148#[cfg(fix_y2038)]
149#[must_use]
150fn clock_gettime_old(id: ClockId) -> Timespec {
151    let mut old_timespec = MaybeUninit::<c::timespec>::uninit();
152
153    let old_timespec = unsafe {
154        ret(c::clock_gettime(
155            id as c::clockid_t,
156            old_timespec.as_mut_ptr(),
157        ))
158        .unwrap();
159        old_timespec.assume_init()
160    };
161
162    Timespec {
163        tv_sec: old_timespec.tv_sec.into(),
164        tv_nsec: old_timespec.tv_nsec.into(),
165    }
166}
167
168#[cfg(not(target_os = "wasi"))]
169#[inline]
170pub(crate) fn clock_gettime_dynamic(id: DynamicClockId<'_>) -> io::Result<Timespec> {
171    let id: c::clockid_t = match id {
172        DynamicClockId::Known(id) => id as c::clockid_t,
173
174        #[cfg(linux_kernel)]
175        DynamicClockId::Dynamic(fd) => {
176            use crate::fd::AsRawFd as _;
177            const CLOCKFD: i32 = 3;
178            (!fd.as_raw_fd() << 3) | CLOCKFD
179        }
180
181        #[cfg(not(linux_kernel))]
182        DynamicClockId::Dynamic(_fd) => {
183            // Dynamic clocks are not supported on this platform.
184            return Err(io::Errno::INVAL);
185        }
186
187        #[cfg(any(linux_kernel, target_os = "fuchsia"))]
188        DynamicClockId::RealtimeAlarm => c::CLOCK_REALTIME_ALARM,
189
190        #[cfg(linux_kernel)]
191        DynamicClockId::Tai => c::CLOCK_TAI,
192
193        #[cfg(any(
194            linux_kernel,
195            target_os = "freebsd",
196            target_os = "fuchsia",
197            target_os = "openbsd"
198        ))]
199        DynamicClockId::Boottime => c::CLOCK_BOOTTIME,
200
201        #[cfg(any(linux_kernel, target_os = "fuchsia"))]
202        DynamicClockId::BoottimeAlarm => c::CLOCK_BOOTTIME_ALARM,
203    };
204
205    // Old 32-bit version: libc has `clock_gettime` but it is not y2038
206    // safe by default. But there may be a `__clock_gettime64` we can use.
207    #[cfg(fix_y2038)]
208    {
209        #[cfg(target_env = "gnu")]
210        if let Some(libc_clock_gettime) = __clock_gettime64.get() {
211            let mut timespec = MaybeUninit::<LibcTimespec>::uninit();
212            unsafe {
213                ret(libc_clock_gettime(
214                    id as c::clockid_t,
215                    timespec.as_mut_ptr(),
216                ))?;
217
218                return Ok(timespec.assume_init().into());
219            }
220        }
221
222        clock_gettime_dynamic_old(id)
223    }
224
225    // Main version: libc is y2038 safe and has `clock_gettime`.
226    #[cfg(not(fix_y2038))]
227    unsafe {
228        let mut timespec = MaybeUninit::<Timespec>::uninit();
229
230        ret(c::clock_gettime(
231            id as c::clockid_t,
232            as_libc_timespec_mut_ptr(&mut timespec),
233        ))?;
234        let timespec = timespec.assume_init();
235        #[cfg(apple)]
236        let timespec = fix_negative_timespec_nsecs(timespec);
237        Ok(timespec)
238    }
239}
240
241#[cfg(fix_y2038)]
242#[inline]
243fn clock_gettime_dynamic_old(id: c::clockid_t) -> io::Result<Timespec> {
244    let mut old_timespec = MaybeUninit::<c::timespec>::uninit();
245
246    let old_timespec = unsafe {
247        ret(c::clock_gettime(
248            id as c::clockid_t,
249            old_timespec.as_mut_ptr(),
250        ))?;
251
252        old_timespec.assume_init()
253    };
254
255    Ok(Timespec {
256        tv_sec: old_timespec.tv_sec.into(),
257        tv_nsec: old_timespec.tv_nsec.into(),
258    })
259}
260
261#[cfg(not(any(
262    target_os = "redox",
263    target_os = "wasi",
264    all(apple, not(target_os = "macos"))
265)))]
266#[inline]
267pub(crate) fn clock_settime(id: ClockId, timespec: Timespec) -> io::Result<()> {
268    // Old 32-bit version: libc has `clock_gettime` but it is not y2038 safe by
269    // default. But there may be a `__clock_settime64` we can use.
270    #[cfg(fix_y2038)]
271    {
272        #[cfg(target_env = "gnu")]
273        if let Some(libc_clock_settime) = __clock_settime64.get() {
274            unsafe {
275                let mut new_timespec = core::mem::zeroed::<LibcTimespec>();
276                new_timespec.tv_sec = timespec.tv_sec;
277                new_timespec.tv_nsec = timespec.tv_nsec as _;
278                return ret(libc_clock_settime(id as c::clockid_t, &new_timespec));
279            }
280        }
281
282        clock_settime_old(id, timespec)
283    }
284
285    // Main version: libc is y2038 safe and has `clock_settime`.
286    #[cfg(not(fix_y2038))]
287    unsafe {
288        ret(c::clock_settime(
289            id as c::clockid_t,
290            as_libc_timespec_ptr(&timespec),
291        ))
292    }
293}
294
295#[cfg(not(any(
296    target_os = "redox",
297    target_os = "wasi",
298    all(apple, not(target_os = "macos"))
299)))]
300#[cfg(fix_y2038)]
301fn clock_settime_old(id: ClockId, timespec: Timespec) -> io::Result<()> {
302    let old_timespec = c::timespec {
303        tv_sec: timespec
304            .tv_sec
305            .try_into()
306            .map_err(|_| io::Errno::OVERFLOW)?,
307        tv_nsec: timespec.tv_nsec as _,
308    };
309
310    unsafe { ret(c::clock_settime(id as c::clockid_t, &old_timespec)) }
311}
312
313#[cfg(any(
314    linux_kernel,
315    target_os = "freebsd",
316    target_os = "fuchsia",
317    target_os = "illumos",
318    target_os = "netbsd"
319))]
320pub(crate) fn timerfd_create(id: TimerfdClockId, flags: TimerfdFlags) -> io::Result<OwnedFd> {
321    unsafe { ret_owned_fd(c::timerfd_create(id as c::clockid_t, bitflags_bits!(flags))) }
322}
323
324#[cfg(any(
325    linux_kernel,
326    target_os = "freebsd",
327    target_os = "fuchsia",
328    target_os = "illumos",
329    target_os = "netbsd"
330))]
331pub(crate) fn timerfd_settime(
332    fd: BorrowedFd<'_>,
333    flags: TimerfdTimerFlags,
334    new_value: &Itimerspec,
335) -> io::Result<Itimerspec> {
336    // Old 32-bit version: libc has `timerfd_settime` but it is not y2038 safe
337    // by default. But there may be a `__timerfd_settime64` we can use.
338    #[cfg(fix_y2038)]
339    {
340        #[cfg(target_env = "gnu")]
341        if let Some(libc_timerfd_settime) = __timerfd_settime64.get() {
342            let mut result = MaybeUninit::<LibcItimerspec>::uninit();
343            unsafe {
344                ret(libc_timerfd_settime(
345                    borrowed_fd(fd),
346                    bitflags_bits!(flags),
347                    &new_value.clone().into(),
348                    result.as_mut_ptr(),
349                ))?;
350                return Ok(result.assume_init().into());
351            }
352        }
353
354        timerfd_settime_old(fd, flags, new_value)
355    }
356
357    #[cfg(not(fix_y2038))]
358    unsafe {
359        use crate::backend::time::types::{as_libc_itimerspec_mut_ptr, as_libc_itimerspec_ptr};
360
361        let mut result = MaybeUninit::<LibcItimerspec>::uninit();
362        ret(c::timerfd_settime(
363            borrowed_fd(fd),
364            bitflags_bits!(flags),
365            as_libc_itimerspec_ptr(new_value),
366            as_libc_itimerspec_mut_ptr(&mut result),
367        ))?;
368        Ok(result.assume_init())
369    }
370}
371
372#[cfg(any(
373    linux_kernel,
374    target_os = "freebsd",
375    target_os = "fuchsia",
376    target_os = "illumos",
377    target_os = "netbsd"
378))]
379#[cfg(fix_y2038)]
380fn timerfd_settime_old(
381    fd: BorrowedFd<'_>,
382    flags: TimerfdTimerFlags,
383    new_value: &Itimerspec,
384) -> io::Result<Itimerspec> {
385    let mut old_result = MaybeUninit::<c::itimerspec>::uninit();
386
387    // Convert `new_value` to the old `itimerspec` format.
388    let old_new_value = c::itimerspec {
389        it_interval: c::timespec {
390            tv_sec: new_value
391                .it_interval
392                .tv_sec
393                .try_into()
394                .map_err(|_| io::Errno::OVERFLOW)?,
395            tv_nsec: new_value
396                .it_interval
397                .tv_nsec
398                .try_into()
399                .map_err(|_| io::Errno::INVAL)?,
400        },
401        it_value: c::timespec {
402            tv_sec: new_value
403                .it_value
404                .tv_sec
405                .try_into()
406                .map_err(|_| io::Errno::OVERFLOW)?,
407            tv_nsec: new_value
408                .it_value
409                .tv_nsec
410                .try_into()
411                .map_err(|_| io::Errno::INVAL)?,
412        },
413    };
414
415    let old_result = unsafe {
416        ret(c::timerfd_settime(
417            borrowed_fd(fd),
418            bitflags_bits!(flags),
419            &old_new_value,
420            old_result.as_mut_ptr(),
421        ))?;
422        old_result.assume_init()
423    };
424
425    Ok(Itimerspec {
426        it_interval: Timespec {
427            tv_sec: old_result
428                .it_interval
429                .tv_sec
430                .try_into()
431                .map_err(|_| io::Errno::OVERFLOW)?,
432            tv_nsec: old_result.it_interval.tv_nsec as _,
433        },
434        it_value: Timespec {
435            tv_sec: old_result
436                .it_interval
437                .tv_sec
438                .try_into()
439                .map_err(|_| io::Errno::OVERFLOW)?,
440            tv_nsec: old_result.it_interval.tv_nsec as _,
441        },
442    })
443}
444
445#[cfg(any(
446    linux_kernel,
447    target_os = "freebsd",
448    target_os = "fuchsia",
449    target_os = "illumos",
450    target_os = "netbsd"
451))]
452pub(crate) fn timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> {
453    // Old 32-bit version: libc has `timerfd_gettime` but it is not y2038 safe
454    // by default. But there may be a `__timerfd_gettime64` we can use.
455    #[cfg(fix_y2038)]
456    {
457        #[cfg(target_env = "gnu")]
458        if let Some(libc_timerfd_gettime) = __timerfd_gettime64.get() {
459            let mut result = MaybeUninit::<LibcItimerspec>::uninit();
460            unsafe {
461                ret(libc_timerfd_gettime(borrowed_fd(fd), result.as_mut_ptr()))?;
462                return Ok(result.assume_init().into());
463            }
464        }
465
466        timerfd_gettime_old(fd)
467    }
468
469    #[cfg(not(fix_y2038))]
470    unsafe {
471        use crate::backend::time::types::as_libc_itimerspec_mut_ptr;
472
473        let mut result = MaybeUninit::<LibcItimerspec>::uninit();
474        ret(c::timerfd_gettime(
475            borrowed_fd(fd),
476            as_libc_itimerspec_mut_ptr(&mut result),
477        ))?;
478        Ok(result.assume_init())
479    }
480}
481
482#[cfg(any(
483    linux_kernel,
484    target_os = "freebsd",
485    target_os = "fuchsia",
486    target_os = "illumos",
487    target_os = "netbsd"
488))]
489#[cfg(fix_y2038)]
490fn timerfd_gettime_old(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> {
491    let mut old_result = MaybeUninit::<c::itimerspec>::uninit();
492
493    let old_result = unsafe {
494        ret(c::timerfd_gettime(borrowed_fd(fd), old_result.as_mut_ptr()))?;
495        old_result.assume_init()
496    };
497
498    Ok(Itimerspec {
499        it_interval: Timespec {
500            tv_sec: old_result
501                .it_interval
502                .tv_sec
503                .try_into()
504                .map_err(|_| io::Errno::OVERFLOW)?,
505            tv_nsec: old_result.it_interval.tv_nsec as _,
506        },
507        it_value: Timespec {
508            tv_sec: old_result
509                .it_interval
510                .tv_sec
511                .try_into()
512                .map_err(|_| io::Errno::OVERFLOW)?,
513            tv_nsec: old_result.it_interval.tv_nsec as _,
514        },
515    })
516}
517
518/// See [`crate::timespec::fix_negative_nsecs`] for details.
519#[cfg(apple)]
520#[cfg(not(fix_y2038))]
521fn fix_negative_timespec_nsecs(mut ts: Timespec) -> Timespec {
522    let (sec, nsec) = crate::timespec::fix_negative_nsecs(ts.tv_sec as _, ts.tv_nsec as _);
523    ts.tv_sec = sec as _;
524    ts.tv_nsec = nsec as _;
525    ts
526}