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