1//! libc syscalls supporting `rustix::thread`.
2
3use super::super::c;
4use super::super::conv::ret;
5#[cfg(any(target_os = "android", target_os = "linux"))]
6use super::super::conv::{borrowed_fd, ret_c_int};
7use super::super::time::types::LibcTimespec;
8#[cfg(any(target_os = "android", target_os = "linux"))]
9use crate::fd::BorrowedFd;
10use crate::io;
11#[cfg(any(target_os = "android", target_os = "linux"))]
12use crate::process::{Pid, RawNonZeroPid};
13#[cfg(not(target_os = "redox"))]
14use crate::thread::{NanosleepRelativeResult, Timespec};
15use core::mem::MaybeUninit;
16#[cfg(not(any(
17    target_os = "dragonfly",
18    target_os = "emscripten",
19    target_os = "freebsd",
20    target_os = "haiku",
21    target_os = "ios",
22    target_os = "macos",
23    target_os = "openbsd",
24    target_os = "redox",
25    target_os = "wasi",
26)))]
27use {crate::thread::ClockId, core::ptr::null_mut};
28
29#[cfg(all(
30    any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
31    target_env = "gnu",
32))]
33weak!(fn __clock_nanosleep_time64(c::clockid_t, c::c_int, *const LibcTimespec, *mut LibcTimespec) -> c::c_int);
34#[cfg(all(
35    any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
36    target_env = "gnu",
37))]
38weak!(fn __nanosleep64(*const LibcTimespec, *mut LibcTimespec) -> c::c_int);
39
40#[cfg(not(any(
41    target_os = "dragonfly",
42    target_os = "emscripten",
43    target_os = "freebsd", // FreeBSD 12 has clock_nanosleep, but libc targets FreeBSD 11.
44    target_os = "haiku",
45    target_os = "ios",
46    target_os = "macos",
47    target_os = "openbsd",
48    target_os = "redox",
49    target_os = "wasi",
50)))]
51#[inline]
52pub(crate) fn clock_nanosleep_relative(id: ClockId, request: &Timespec) -> NanosleepRelativeResult {
53    let mut remain = MaybeUninit::<LibcTimespec>::uninit();
54    let flags = 0;
55
56    // 32-bit gnu version: libc has `clock_nanosleep` but it is not y2038 safe by
57    // default.
58    #[cfg(all(
59        any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
60        target_env = "gnu",
61    ))]
62    unsafe {
63        if let Some(libc_clock_nanosleep) = __clock_nanosleep_time64.get() {
64            match libc_clock_nanosleep(
65                id as c::clockid_t,
66                flags,
67                &request.clone().into(),
68                remain.as_mut_ptr(),
69            ) {
70                0 => NanosleepRelativeResult::Ok,
71                err if err == io::Errno::INTR.0 => {
72                    NanosleepRelativeResult::Interrupted(remain.assume_init().into())
73                }
74                err => NanosleepRelativeResult::Err(io::Errno(err)),
75            }
76        } else {
77            clock_nanosleep_relative_old(id, request)
78        }
79    }
80
81    // Main version: libc is y2038 safe and has `clock_nanosleep`.
82    #[cfg(not(all(
83        any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
84        target_env = "gnu",
85    )))]
86    unsafe {
87        match c::clock_nanosleep(id as c::clockid_t, flags, request, remain.as_mut_ptr()) {
88            0 => NanosleepRelativeResult::Ok,
89            err if err == io::Errno::INTR.0 => {
90                NanosleepRelativeResult::Interrupted(remain.assume_init())
91            }
92            err => NanosleepRelativeResult::Err(io::Errno(err)),
93        }
94    }
95}
96
97#[cfg(all(
98    any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
99    target_env = "gnu",
100))]
101unsafe fn clock_nanosleep_relative_old(id: ClockId, request: &Timespec) -> NanosleepRelativeResult {
102    use core::convert::TryInto;
103    let tv_sec = match request.tv_sec.try_into() {
104        Ok(tv_sec) => tv_sec,
105        Err(_) => return NanosleepRelativeResult::Err(io::Errno::OVERFLOW),
106    };
107    let tv_nsec = match request.tv_nsec.try_into() {
108        Ok(tv_nsec) => tv_nsec,
109        Err(_) => return NanosleepRelativeResult::Err(io::Errno::INVAL),
110    };
111    let old_request = c::timespec { tv_sec, tv_nsec };
112    let mut old_remain = MaybeUninit::<c::timespec>::uninit();
113    let flags = 0;
114
115    match c::clock_nanosleep(
116        id as c::clockid_t,
117        flags,
118        &old_request,
119        old_remain.as_mut_ptr(),
120    ) {
121        0 => NanosleepRelativeResult::Ok,
122        err if err == io::Errno::INTR.0 => {
123            let old_remain = old_remain.assume_init();
124            let remain = Timespec {
125                tv_sec: old_remain.tv_sec.into(),
126                tv_nsec: old_remain.tv_nsec.into(),
127            };
128            NanosleepRelativeResult::Interrupted(remain)
129        }
130        err => NanosleepRelativeResult::Err(io::Errno(err)),
131    }
132}
133
134#[cfg(not(any(
135    target_os = "dragonfly",
136    target_os = "emscripten",
137    target_os = "freebsd", // FreeBSD 12 has clock_nanosleep, but libc targets FreeBSD 11.
138    target_os = "haiku",
139    target_os = "ios",
140    target_os = "macos",
141    target_os = "openbsd",
142    target_os = "redox",
143    target_os = "wasi",
144)))]
145#[inline]
146pub(crate) fn clock_nanosleep_absolute(id: ClockId, request: &Timespec) -> io::Result<()> {
147    let flags = c::TIMER_ABSTIME;
148
149    // 32-bit gnu version: libc has `clock_nanosleep` but it is not y2038 safe by
150    // default.
151    #[cfg(all(
152        any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
153        target_env = "gnu",
154    ))]
155    {
156        if let Some(libc_clock_nanosleep) = __clock_nanosleep_time64.get() {
157            match unsafe {
158                libc_clock_nanosleep(
159                    id as c::clockid_t,
160                    flags,
161                    &request.clone().into(),
162                    null_mut(),
163                )
164            } {
165                0 => Ok(()),
166                err => Err(io::Errno(err)),
167            }
168        } else {
169            clock_nanosleep_absolute_old(id, request)
170        }
171    }
172
173    // Main version: libc is y2038 safe and has `clock_nanosleep`.
174    #[cfg(not(all(
175        any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
176        target_env = "gnu",
177    )))]
178    match unsafe { c::clock_nanosleep(id as c::clockid_t, flags, request, null_mut()) } {
179        0 => Ok(()),
180        err => Err(io::Errno(err)),
181    }
182}
183
184#[cfg(all(
185    any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
186    target_env = "gnu",
187))]
188fn clock_nanosleep_absolute_old(id: ClockId, request: &Timespec) -> io::Result<()> {
189    use core::convert::TryInto;
190
191    let flags = c::TIMER_ABSTIME;
192
193    let old_request = c::timespec {
194        tv_sec: request.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?,
195        tv_nsec: request.tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
196    };
197    match unsafe { c::clock_nanosleep(id as c::clockid_t, flags, &old_request, null_mut()) } {
198        0 => Ok(()),
199        err => Err(io::Errno(err)),
200    }
201}
202
203#[cfg(not(target_os = "redox"))]
204#[inline]
205pub(crate) fn nanosleep(request: &Timespec) -> NanosleepRelativeResult {
206    let mut remain = MaybeUninit::<LibcTimespec>::uninit();
207
208    // 32-bit gnu version: libc has `nanosleep` but it is not y2038 safe by
209    // default.
210    #[cfg(all(
211        any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
212        target_env = "gnu",
213    ))]
214    unsafe {
215        if let Some(libc_nanosleep) = __nanosleep64.get() {
216            match ret(libc_nanosleep(&request.clone().into(), remain.as_mut_ptr())) {
217                Ok(()) => NanosleepRelativeResult::Ok,
218                Err(io::Errno::INTR) => {
219                    NanosleepRelativeResult::Interrupted(remain.assume_init().into())
220                }
221                Err(err) => NanosleepRelativeResult::Err(err),
222            }
223        } else {
224            nanosleep_old(request)
225        }
226    }
227
228    // Main version: libc is y2038 safe and has `nanosleep`.
229    #[cfg(not(all(
230        any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
231        target_env = "gnu",
232    )))]
233    unsafe {
234        match ret(c::nanosleep(request, remain.as_mut_ptr())) {
235            Ok(()) => NanosleepRelativeResult::Ok,
236            Err(io::Errno::INTR) => NanosleepRelativeResult::Interrupted(remain.assume_init()),
237            Err(err) => NanosleepRelativeResult::Err(err),
238        }
239    }
240}
241
242#[cfg(all(
243    any(target_arch = "arm", target_arch = "mips", target_arch = "x86"),
244    target_env = "gnu",
245))]
246unsafe fn nanosleep_old(request: &Timespec) -> NanosleepRelativeResult {
247    use core::convert::TryInto;
248    let tv_sec = match request.tv_sec.try_into() {
249        Ok(tv_sec) => tv_sec,
250        Err(_) => return NanosleepRelativeResult::Err(io::Errno::OVERFLOW),
251    };
252    let tv_nsec = match request.tv_nsec.try_into() {
253        Ok(tv_nsec) => tv_nsec,
254        Err(_) => return NanosleepRelativeResult::Err(io::Errno::INVAL),
255    };
256    let old_request = c::timespec { tv_sec, tv_nsec };
257    let mut old_remain = MaybeUninit::<c::timespec>::uninit();
258
259    match ret(c::nanosleep(&old_request, old_remain.as_mut_ptr())) {
260        Ok(()) => NanosleepRelativeResult::Ok,
261        Err(io::Errno::INTR) => {
262            let old_remain = old_remain.assume_init();
263            let remain = Timespec {
264                tv_sec: old_remain.tv_sec.into(),
265                tv_nsec: old_remain.tv_nsec.into(),
266            };
267            NanosleepRelativeResult::Interrupted(remain)
268        }
269        Err(err) => NanosleepRelativeResult::Err(err),
270    }
271}
272
273#[cfg(any(target_os = "android", target_os = "linux"))]
274#[inline]
275#[must_use]
276pub(crate) fn gettid() -> Pid {
277    // `gettid` wasn't supported in glibc until 2.30, and musl until 1.2.2,
278    // so use `syscall`.
279    // <https://sourceware.org/bugzilla/show_bug.cgi?id=6399#c62>
280    weak_or_syscall! {
281        fn gettid() via SYS_gettid -> c::pid_t
282    }
283
284    unsafe {
285        let tid = gettid();
286        debug_assert_ne!(tid, 0);
287        Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(tid))
288    }
289}
290
291#[cfg(any(target_os = "android", target_os = "linux"))]
292#[inline]
293pub(crate) fn setns(fd: BorrowedFd, nstype: c::c_int) -> io::Result<c::c_int> {
294    unsafe { ret_c_int(c::setns(borrowed_fd(fd), nstype)) }
295}
296
297#[cfg(any(target_os = "android", target_os = "linux"))]
298#[inline]
299pub(crate) fn unshare(flags: crate::thread::UnshareFlags) -> io::Result<()> {
300    unsafe { ret(c::unshare(flags.bits() as i32)) }
301}
302