1//! linux_raw syscalls supporting `rustix::time`.
2//!
3//! # Safety
4//!
5//! See the `rustix::backend` module documentation for details.
6#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)]
7
8use crate::backend::conv::{ret, ret_infallible};
9use crate::clockid::ClockId;
10use crate::io;
11use crate::timespec::Timespec;
12use core::mem::MaybeUninit;
13#[cfg(all(feature = "time", target_pointer_width = "32"))]
14use linux_raw_sys::general::itimerspec as __kernel_old_itimerspec;
15#[cfg(target_pointer_width = "32")]
16use linux_raw_sys::general::timespec as __kernel_old_timespec;
17#[cfg(feature = "time")]
18use {
19 crate::backend::conv::{by_ref, ret_owned_fd},
20 crate::fd::BorrowedFd,
21 crate::fd::OwnedFd,
22 crate::time::{Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags},
23};
24
25// `clock_gettime` has special optimizations via the vDSO.
26#[cfg(feature = "time")]
27pub(crate) use crate::backend::vdso_wrappers::{clock_gettime, clock_gettime_dynamic};
28
29#[inline]
30pub(crate) fn clock_getres(which_clock: ClockId) -> Timespec {
31 #[cfg(target_pointer_width = "32")]
32 unsafe {
33 let mut result = MaybeUninit::<Timespec>::uninit();
34 if let Err(err) = ret(syscall!(__NR_clock_getres_time64, which_clock, &mut result)) {
35 // See the comments in `rustix_clock_gettime_via_syscall` about
36 // emulation.
37 debug_assert_eq!(err, io::Errno::NOSYS);
38 clock_getres_old(which_clock, &mut result);
39 }
40 result.assume_init()
41 }
42 #[cfg(target_pointer_width = "64")]
43 unsafe {
44 let mut result: MaybeUninit<__kernel_timespec> = MaybeUninit::<Timespec>::uninit();
45 ret_infallible(raw:syscall!(__NR_clock_getres, which_clock, &mut result));
46 result.assume_init()
47 }
48}
49
50#[cfg(target_pointer_width = "32")]
51unsafe fn clock_getres_old(which_clock: ClockId, result: &mut MaybeUninit<Timespec>) {
52 let mut old_result = MaybeUninit::<__kernel_old_timespec>::uninit();
53 ret_infallible(syscall!(__NR_clock_getres, which_clock, &mut old_result));
54 let old_result = old_result.assume_init();
55 result.write(Timespec {
56 tv_sec: old_result.tv_sec.into(),
57 tv_nsec: old_result.tv_nsec.into(),
58 });
59}
60
61#[cfg(feature = "time")]
62#[inline]
63pub(crate) fn clock_settime(which_clock: ClockId, timespec: Timespec) -> io::Result<()> {
64 // `clock_settime64` was introduced in Linux 5.1. The old `clock_settime`
65 // syscall is not y2038-compatible on 32-bit architectures.
66 #[cfg(target_pointer_width = "32")]
67 unsafe {
68 match ret(syscall_readonly!(
69 __NR_clock_settime64,
70 which_clock,
71 by_ref(&timespec)
72 )) {
73 Err(io::Errno::NOSYS) => clock_settime_old(which_clock, timespec),
74 otherwise => otherwise,
75 }
76 }
77 #[cfg(target_pointer_width = "64")]
78 unsafe {
79 ret(raw:syscall_readonly!(
80 __NR_clock_settime,
81 which_clock,
82 by_ref(&timespec)
83 ))
84 }
85}
86
87#[cfg(feature = "time")]
88#[cfg(target_pointer_width = "32")]
89unsafe fn clock_settime_old(which_clock: ClockId, timespec: Timespec) -> io::Result<()> {
90 let old_timespec = __kernel_old_timespec {
91 tv_sec: timespec
92 .tv_sec
93 .try_into()
94 .map_err(|_| io::Errno::OVERFLOW)?,
95 tv_nsec: timespec.tv_nsec as _,
96 };
97 ret(syscall_readonly!(
98 __NR_clock_settime,
99 which_clock,
100 by_ref(&old_timespec)
101 ))
102}
103
104#[cfg(feature = "time")]
105#[inline]
106pub(crate) fn timerfd_create(clockid: TimerfdClockId, flags: TimerfdFlags) -> io::Result<OwnedFd> {
107 unsafe { ret_owned_fd(raw:syscall_readonly!(__NR_timerfd_create, clockid, flags)) }
108}
109
110#[cfg(feature = "time")]
111#[inline]
112pub(crate) fn timerfd_settime(
113 fd: BorrowedFd<'_>,
114 flags: TimerfdTimerFlags,
115 new_value: &Itimerspec,
116) -> io::Result<Itimerspec> {
117 let mut result = MaybeUninit::<Itimerspec>::uninit();
118
119 #[cfg(target_pointer_width = "64")]
120 unsafe {
121 ret(syscall!(
122 __NR_timerfd_settime,
123 fd,
124 flags,
125 by_ref(new_value),
126 &mut result
127 ))?;
128 Ok(result.assume_init())
129 }
130
131 #[cfg(target_pointer_width = "32")]
132 unsafe {
133 ret(syscall!(
134 __NR_timerfd_settime64,
135 fd,
136 flags,
137 by_ref(new_value),
138 &mut result
139 ))
140 .or_else(|err| {
141 // See the comments in `rustix_clock_gettime_via_syscall` about
142 // emulation.
143 if err == io::Errno::NOSYS {
144 timerfd_settime_old(fd, flags, new_value, &mut result)
145 } else {
146 Err(err)
147 }
148 })?;
149 Ok(result.assume_init())
150 }
151}
152
153#[cfg(feature = "time")]
154#[cfg(target_pointer_width = "32")]
155unsafe fn timerfd_settime_old(
156 fd: BorrowedFd<'_>,
157 flags: TimerfdTimerFlags,
158 new_value: &Itimerspec,
159 result: &mut MaybeUninit<Itimerspec>,
160) -> io::Result<()> {
161 let mut old_result = MaybeUninit::<__kernel_old_itimerspec>::uninit();
162
163 // Convert `new_value` to the old `__kernel_old_itimerspec` format.
164 let old_new_value = __kernel_old_itimerspec {
165 it_interval: __kernel_old_timespec {
166 tv_sec: new_value
167 .it_interval
168 .tv_sec
169 .try_into()
170 .map_err(|_| io::Errno::OVERFLOW)?,
171 tv_nsec: new_value
172 .it_interval
173 .tv_nsec
174 .try_into()
175 .map_err(|_| io::Errno::INVAL)?,
176 },
177 it_value: __kernel_old_timespec {
178 tv_sec: new_value
179 .it_value
180 .tv_sec
181 .try_into()
182 .map_err(|_| io::Errno::OVERFLOW)?,
183 tv_nsec: new_value
184 .it_value
185 .tv_nsec
186 .try_into()
187 .map_err(|_| io::Errno::INVAL)?,
188 },
189 };
190 ret(syscall!(
191 __NR_timerfd_settime,
192 fd,
193 flags,
194 by_ref(&old_new_value),
195 &mut old_result
196 ))?;
197 let old_result = old_result.assume_init();
198 result.write(Itimerspec {
199 it_interval: Timespec {
200 tv_sec: old_result.it_interval.tv_sec.into(),
201 tv_nsec: old_result.it_interval.tv_nsec.into(),
202 },
203 it_value: Timespec {
204 tv_sec: old_result.it_value.tv_sec.into(),
205 tv_nsec: old_result.it_value.tv_nsec.into(),
206 },
207 });
208 Ok(())
209}
210
211#[cfg(feature = "time")]
212#[inline]
213pub(crate) fn timerfd_gettime(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> {
214 let mut result: MaybeUninit<__kernel_itimerspec> = MaybeUninit::<Itimerspec>::uninit();
215
216 #[cfg(target_pointer_width = "64")]
217 unsafe {
218 ret(raw:syscall!(__NR_timerfd_gettime, fd, &mut result))?;
219 Ok(result.assume_init())
220 }
221
222 #[cfg(target_pointer_width = "32")]
223 unsafe {
224 ret(syscall!(__NR_timerfd_gettime64, fd, &mut result)).or_else(|err| {
225 // See the comments in `rustix_clock_gettime_via_syscall` about
226 // emulation.
227 if err == io::Errno::NOSYS {
228 timerfd_gettime_old(fd, &mut result)
229 } else {
230 Err(err)
231 }
232 })?;
233 Ok(result.assume_init())
234 }
235}
236
237#[cfg(feature = "time")]
238#[cfg(target_pointer_width = "32")]
239unsafe fn timerfd_gettime_old(
240 fd: BorrowedFd<'_>,
241 result: &mut MaybeUninit<Itimerspec>,
242) -> io::Result<()> {
243 let mut old_result = MaybeUninit::<__kernel_old_itimerspec>::uninit();
244 ret(syscall!(__NR_timerfd_gettime, fd, &mut old_result))?;
245 let old_result = old_result.assume_init();
246 result.write(Itimerspec {
247 it_interval: Timespec {
248 tv_sec: old_result.it_interval.tv_sec.into(),
249 tv_nsec: old_result.it_interval.tv_nsec.into(),
250 },
251 it_value: Timespec {
252 tv_sec: old_result.it_value.tv_sec.into(),
253 tv_nsec: old_result.it_value.tv_nsec.into(),
254 },
255 });
256 Ok(())
257}
258