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