1//! libc syscalls supporting `rustix::process`.
2
3use super::super::c;
4#[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))]
5use super::super::conv::borrowed_fd;
6#[cfg(not(target_os = "wasi"))]
7use super::super::conv::ret_pid_t;
8use super::super::conv::{c_str, ret, ret_c_int, ret_discarded_char_ptr};
9#[cfg(any(target_os = "android", target_os = "linux"))]
10use super::super::conv::{syscall_ret, syscall_ret_u32};
11#[cfg(any(
12    target_os = "android",
13    target_os = "dragonfly",
14    target_os = "fuchsia",
15    target_os = "linux",
16))]
17use super::types::RawCpuSet;
18#[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))]
19use crate::fd::BorrowedFd;
20use crate::ffi::CStr;
21use crate::io;
22use core::mem::MaybeUninit;
23#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
24use {
25    super::super::conv::ret_infallible,
26    super::super::offset::{libc_getrlimit, libc_rlimit, libc_setrlimit, LIBC_RLIM_INFINITY},
27    crate::process::{Resource, Rlimit},
28    core::convert::TryInto,
29};
30#[cfg(any(target_os = "android", target_os = "linux"))]
31use {
32    super::super::offset::libc_prlimit,
33    crate::process::{Cpuid, MembarrierCommand, MembarrierQuery},
34};
35#[cfg(not(target_os = "wasi"))]
36use {
37    super::types::RawUname,
38    crate::process::{Gid, Pid, RawNonZeroPid, RawPid, Signal, Uid, WaitOptions, WaitStatus},
39};
40
41#[cfg(not(target_os = "wasi"))]
42pub(crate) fn chdir(path: &CStr) -> io::Result<()> {
43    unsafe { ret(c::chdir(c_str(path))) }
44}
45
46#[cfg(not(any(target_os = "wasi", target_os = "fuchsia")))]
47pub(crate) fn fchdir(dirfd: BorrowedFd<'_>) -> io::Result<()> {
48    unsafe { ret(c::fchdir(borrowed_fd(dirfd))) }
49}
50
51#[cfg(not(target_os = "wasi"))]
52pub(crate) fn getcwd(buf: &mut [u8]) -> io::Result<()> {
53    unsafe { ret_discarded_char_ptr(c::getcwd(buf.as_mut_ptr().cast(), buf.len())) }
54}
55
56#[cfg(any(target_os = "android", target_os = "linux"))]
57pub(crate) fn membarrier_query() -> MembarrierQuery {
58    // GLIBC does not have a wrapper for `membarrier`; [the documentation]
59    // says to use `syscall`.
60    //
61    // [the documentation]: https://man7.org/linux/man-pages/man2/membarrier.2.html#NOTES
62    const MEMBARRIER_CMD_QUERY: u32 = 0;
63    unsafe {
64        match syscall_ret_u32(c::syscall(c::SYS_membarrier, MEMBARRIER_CMD_QUERY, 0)) {
65            Ok(query) => MembarrierQuery::from_bits_unchecked(query),
66            Err(_) => MembarrierQuery::empty(),
67        }
68    }
69}
70
71#[cfg(any(target_os = "android", target_os = "linux"))]
72pub(crate) fn membarrier(cmd: MembarrierCommand) -> io::Result<()> {
73    unsafe { syscall_ret(c::syscall(c::SYS_membarrier, cmd as u32, 0)) }
74}
75
76#[cfg(any(target_os = "android", target_os = "linux"))]
77pub(crate) fn membarrier_cpu(cmd: MembarrierCommand, cpu: Cpuid) -> io::Result<()> {
78    const MEMBARRIER_CMD_FLAG_CPU: u32 = 1;
79    unsafe {
80        syscall_ret(c::syscall(
81            c::SYS_membarrier,
82            cmd as u32,
83            MEMBARRIER_CMD_FLAG_CPU,
84            cpu.as_raw(),
85        ))
86    }
87}
88
89#[cfg(not(target_os = "wasi"))]
90#[inline]
91#[must_use]
92pub(crate) fn getuid() -> Uid {
93    unsafe {
94        let uid = c::getuid();
95        Uid::from_raw(uid)
96    }
97}
98
99#[cfg(not(target_os = "wasi"))]
100#[inline]
101#[must_use]
102pub(crate) fn geteuid() -> Uid {
103    unsafe {
104        let uid = c::geteuid();
105        Uid::from_raw(uid)
106    }
107}
108
109#[cfg(not(target_os = "wasi"))]
110#[inline]
111#[must_use]
112pub(crate) fn getgid() -> Gid {
113    unsafe {
114        let gid = c::getgid();
115        Gid::from_raw(gid)
116    }
117}
118
119#[cfg(not(target_os = "wasi"))]
120#[inline]
121#[must_use]
122pub(crate) fn getegid() -> Gid {
123    unsafe {
124        let gid = c::getegid();
125        Gid::from_raw(gid)
126    }
127}
128
129#[cfg(not(target_os = "wasi"))]
130#[inline]
131#[must_use]
132pub(crate) fn getpid() -> Pid {
133    unsafe {
134        let pid = c::getpid();
135        debug_assert_ne!(pid, 0);
136        Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pid))
137    }
138}
139
140#[cfg(not(target_os = "wasi"))]
141#[inline]
142#[must_use]
143pub(crate) fn getppid() -> Option<Pid> {
144    unsafe {
145        let pid: i32 = c::getppid();
146        Pid::from_raw(pid)
147    }
148}
149
150#[cfg(not(target_os = "wasi"))]
151#[inline]
152pub(crate) fn getpgid(pid: Option<Pid>) -> io::Result<Pid> {
153    unsafe {
154        let pgid = ret_pid_t(c::getpgid(Pid::as_raw(pid) as _))?;
155        debug_assert_ne!(pgid, 0);
156        Ok(Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pgid)))
157    }
158}
159
160#[cfg(not(target_os = "wasi"))]
161#[inline]
162#[must_use]
163pub(crate) fn getpgrp() -> Pid {
164    unsafe {
165        let pgid = c::getpgrp();
166        debug_assert_ne!(pgid, 0);
167        Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pgid))
168    }
169}
170
171#[cfg(any(
172    target_os = "android",
173    target_os = "dragonfly",
174    target_os = "fuchsia",
175    target_os = "linux",
176))]
177#[inline]
178pub(crate) fn sched_getaffinity(pid: Option<Pid>, cpuset: &mut RawCpuSet) -> io::Result<()> {
179    unsafe {
180        ret(c::sched_getaffinity(
181            Pid::as_raw(pid) as _,
182            core::mem::size_of::<RawCpuSet>(),
183            cpuset,
184        ))
185    }
186}
187
188#[cfg(any(
189    target_os = "android",
190    target_os = "dragonfly",
191    target_os = "fuchsia",
192    target_os = "linux",
193))]
194#[inline]
195pub(crate) fn sched_setaffinity(pid: Option<Pid>, cpuset: &RawCpuSet) -> io::Result<()> {
196    unsafe {
197        ret(c::sched_setaffinity(
198            Pid::as_raw(pid) as _,
199            core::mem::size_of::<RawCpuSet>(),
200            cpuset,
201        ))
202    }
203}
204
205#[inline]
206pub(crate) fn sched_yield() {
207    unsafe {
208        let _ = c::sched_yield();
209    }
210}
211
212#[cfg(not(target_os = "wasi"))]
213#[inline]
214pub(crate) fn uname() -> RawUname {
215    let mut uname = MaybeUninit::<RawUname>::uninit();
216    unsafe {
217        ret(c::uname(uname.as_mut_ptr())).unwrap();
218        uname.assume_init()
219    }
220}
221
222#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))]
223#[inline]
224pub(crate) fn nice(inc: i32) -> io::Result<i32> {
225    libc_errno::set_errno(libc_errno::Errno(0));
226    let r = unsafe { c::nice(inc) };
227    if libc_errno::errno().0 != 0 {
228        ret_c_int(r)
229    } else {
230        Ok(r)
231    }
232}
233
234#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
235#[inline]
236pub(crate) fn getpriority_user(uid: Uid) -> io::Result<i32> {
237    libc_errno::set_errno(libc_errno::Errno(0));
238    let r = unsafe { c::getpriority(c::PRIO_USER, uid.as_raw() as _) };
239    if libc_errno::errno().0 != 0 {
240        ret_c_int(r)
241    } else {
242        Ok(r)
243    }
244}
245
246#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
247#[inline]
248pub(crate) fn getpriority_pgrp(pgid: Option<Pid>) -> io::Result<i32> {
249    libc_errno::set_errno(libc_errno::Errno(0));
250    let r = unsafe { c::getpriority(c::PRIO_PGRP, Pid::as_raw(pgid) as _) };
251    if libc_errno::errno().0 != 0 {
252        ret_c_int(r)
253    } else {
254        Ok(r)
255    }
256}
257
258#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
259#[inline]
260pub(crate) fn getpriority_process(pid: Option<Pid>) -> io::Result<i32> {
261    libc_errno::set_errno(libc_errno::Errno(0));
262    let r = unsafe { c::getpriority(c::PRIO_PROCESS, Pid::as_raw(pid) as _) };
263    if libc_errno::errno().0 != 0 {
264        ret_c_int(r)
265    } else {
266        Ok(r)
267    }
268}
269
270#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
271#[inline]
272pub(crate) fn setpriority_user(uid: Uid, priority: i32) -> io::Result<()> {
273    unsafe { ret(c::setpriority(c::PRIO_USER, uid.as_raw() as _, priority)) }
274}
275
276#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
277#[inline]
278pub(crate) fn setpriority_pgrp(pgid: Option<Pid>, priority: i32) -> io::Result<()> {
279    unsafe {
280        ret(c::setpriority(
281            c::PRIO_PGRP,
282            Pid::as_raw(pgid) as _,
283            priority,
284        ))
285    }
286}
287
288#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
289#[inline]
290pub(crate) fn setpriority_process(pid: Option<Pid>, priority: i32) -> io::Result<()> {
291    unsafe {
292        ret(c::setpriority(
293            c::PRIO_PROCESS,
294            Pid::as_raw(pid) as _,
295            priority,
296        ))
297    }
298}
299
300#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
301#[inline]
302pub(crate) fn getrlimit(limit: Resource) -> Rlimit {
303    let mut result = MaybeUninit::<libc_rlimit>::uninit();
304    unsafe {
305        ret_infallible(libc_getrlimit(limit as _, result.as_mut_ptr()));
306        rlimit_from_libc(result.assume_init())
307    }
308}
309
310#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
311#[inline]
312pub(crate) fn setrlimit(limit: Resource, new: Rlimit) -> io::Result<()> {
313    let lim = rlimit_to_libc(new)?;
314    unsafe { ret(libc_setrlimit(limit as _, &lim)) }
315}
316
317#[cfg(any(target_os = "android", target_os = "linux"))]
318#[inline]
319pub(crate) fn prlimit(pid: Option<Pid>, limit: Resource, new: Rlimit) -> io::Result<Rlimit> {
320    let lim = rlimit_to_libc(new)?;
321    let mut result = MaybeUninit::<libc_rlimit>::uninit();
322    unsafe {
323        ret(libc_prlimit(
324            Pid::as_raw(pid),
325            limit as _,
326            &lim,
327            result.as_mut_ptr(),
328        ))?;
329        Ok(rlimit_from_libc(result.assume_init()))
330    }
331}
332
333/// Convert a Rust [`Rlimit`] to a C `libc_rlimit`.
334#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
335fn rlimit_from_libc(lim: libc_rlimit) -> Rlimit {
336    let current = if lim.rlim_cur == LIBC_RLIM_INFINITY {
337        None
338    } else {
339        Some(lim.rlim_cur.try_into().unwrap())
340    };
341    let maximum = if lim.rlim_max == LIBC_RLIM_INFINITY {
342        None
343    } else {
344        Some(lim.rlim_max.try_into().unwrap())
345    };
346    Rlimit { current, maximum }
347}
348
349/// Convert a C `libc_rlimit` to a Rust `Rlimit`.
350#[cfg(not(any(target_os = "fuchsia", target_os = "redox", target_os = "wasi")))]
351fn rlimit_to_libc(lim: Rlimit) -> io::Result<libc_rlimit> {
352    let Rlimit { current, maximum } = lim;
353    let rlim_cur = match current {
354        Some(r) => r.try_into().map_err(|_e| io::Errno::INVAL)?,
355        None => LIBC_RLIM_INFINITY as _,
356    };
357    let rlim_max = match maximum {
358        Some(r) => r.try_into().map_err(|_e| io::Errno::INVAL)?,
359        None => LIBC_RLIM_INFINITY as _,
360    };
361    Ok(libc_rlimit { rlim_cur, rlim_max })
362}
363
364#[cfg(not(target_os = "wasi"))]
365#[inline]
366pub(crate) fn wait(waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> {
367    _waitpid(!0, waitopts)
368}
369
370#[cfg(not(target_os = "wasi"))]
371#[inline]
372pub(crate) fn waitpid(
373    pid: Option<Pid>,
374    waitopts: WaitOptions,
375) -> io::Result<Option<(Pid, WaitStatus)>> {
376    _waitpid(Pid::as_raw(pid), waitopts)
377}
378
379#[cfg(not(target_os = "wasi"))]
380#[inline]
381pub(crate) fn _waitpid(
382    pid: RawPid,
383    waitopts: WaitOptions,
384) -> io::Result<Option<(Pid, WaitStatus)>> {
385    unsafe {
386        let mut status: c::c_int = 0;
387        let pid = ret_c_int(c::waitpid(pid as _, &mut status, waitopts.bits() as _))?;
388        Ok(RawNonZeroPid::new(pid).map(|non_zero| {
389            (
390                Pid::from_raw_nonzero(non_zero),
391                WaitStatus::new(status as _),
392            )
393        }))
394    }
395}
396
397#[inline]
398pub(crate) fn exit_group(code: c::c_int) -> ! {
399    // `_exit` and `_Exit` are the same; it's just a matter of which ones
400    // the libc bindings expose.
401    #[cfg(any(target_os = "wasi", target_os = "solid"))]
402    unsafe {
403        c::_Exit(code)
404    }
405    #[cfg(unix)]
406    unsafe {
407        c::_exit(code)
408    }
409}
410
411#[cfg(not(target_os = "wasi"))]
412#[inline]
413pub(crate) fn setsid() -> io::Result<Pid> {
414    unsafe {
415        let pid = ret_c_int(c::setsid())?;
416        debug_assert_ne!(pid, 0);
417        Ok(Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pid)))
418    }
419}
420
421#[cfg(not(target_os = "wasi"))]
422#[inline]
423pub(crate) fn kill_process(pid: Pid, sig: Signal) -> io::Result<()> {
424    unsafe { ret(c::kill(pid.as_raw_nonzero().get(), sig as i32)) }
425}
426
427#[cfg(not(target_os = "wasi"))]
428#[inline]
429pub(crate) fn kill_process_group(pid: Pid, sig: Signal) -> io::Result<()> {
430    unsafe {
431        ret(c::kill(
432            pid.as_raw_nonzero().get().wrapping_neg(),
433            sig as i32,
434        ))
435    }
436}
437
438#[cfg(not(target_os = "wasi"))]
439#[inline]
440pub(crate) fn kill_current_process_group(sig: Signal) -> io::Result<()> {
441    unsafe { ret(c::kill(0, sig as i32)) }
442}
443
444#[cfg(any(target_os = "android", target_os = "linux"))]
445#[inline]
446pub(crate) unsafe fn prctl(
447    option: c::c_int,
448    arg2: *mut c::c_void,
449    arg3: *mut c::c_void,
450    arg4: *mut c::c_void,
451    arg5: *mut c::c_void,
452) -> io::Result<c::c_int> {
453    ret_c_int(c::prctl(option, arg2, arg3, arg4, arg5))
454}
455
456#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
457#[inline]
458pub(crate) unsafe fn procctl(
459    idtype: c::idtype_t,
460    id: c::id_t,
461    option: c::c_int,
462    data: *mut c::c_void,
463) -> io::Result<()> {
464    ret(c::procctl(idtype, id, option, data))
465}
466