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, ×pec))
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