rustix/backend/linux_raw/process/
syscalls.rs

1//! linux_raw syscalls supporting `rustix::process`.
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::c;
9#[cfg(all(feature = "alloc", feature = "fs"))]
10use crate::backend::conv::slice_mut;
11use crate::backend::conv::{
12    by_mut, by_ref, c_int, c_uint, negative_pid, pass_usize, raw_fd, ret, ret_c_int,
13    ret_c_int_infallible, ret_infallible, ret_owned_fd, zero,
14};
15use crate::fd::{AsRawFd as _, BorrowedFd, OwnedFd, RawFd};
16#[cfg(feature = "fs")]
17use crate::ffi::CStr;
18use crate::io;
19use crate::pid::RawPid;
20use crate::process::{
21    Flock, Pid, PidfdFlags, PidfdGetfdFlags, Resource, Rlimit, Uid, WaitId, WaitIdOptions,
22    WaitIdStatus, WaitOptions, WaitStatus,
23};
24use crate::signal::Signal;
25use core::mem::MaybeUninit;
26use core::ptr::{null, null_mut};
27use linux_raw_sys::general::{rlimit64, PRIO_PGRP, PRIO_PROCESS, PRIO_USER, RLIM64_INFINITY};
28#[cfg(feature = "fs")]
29use {crate::backend::conv::ret_c_uint_infallible, crate::fs::Mode};
30#[cfg(feature = "alloc")]
31use {
32    crate::backend::conv::{ret_usize, slice_just_addr_mut},
33    crate::process::Gid,
34};
35
36#[cfg(feature = "fs")]
37#[inline]
38pub(crate) fn chdir(filename: &CStr) -> io::Result<()> {
39    unsafe { ret(syscall_readonly!(__NR_chdir, filename)) }
40}
41
42#[inline]
43pub(crate) fn fchdir(fd: BorrowedFd<'_>) -> io::Result<()> {
44    unsafe { ret(syscall_readonly!(__NR_fchdir, fd)) }
45}
46
47#[cfg(feature = "fs")]
48#[inline]
49pub(crate) fn chroot(filename: &CStr) -> io::Result<()> {
50    unsafe { ret(syscall_readonly!(__NR_chroot, filename)) }
51}
52
53#[cfg(all(feature = "alloc", feature = "fs"))]
54#[inline]
55pub(crate) fn getcwd(buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
56    let (buf_addr_mut, buf_len) = slice_mut(buf);
57    unsafe { ret_usize(syscall!(__NR_getcwd, buf_addr_mut, buf_len)) }
58}
59
60#[inline]
61#[must_use]
62pub(crate) fn getppid() -> Option<Pid> {
63    unsafe {
64        let ppid = ret_c_int_infallible(syscall_readonly!(__NR_getppid));
65        Pid::from_raw(ppid)
66    }
67}
68
69#[inline]
70pub(crate) fn getpgid(pid: Option<Pid>) -> io::Result<Pid> {
71    unsafe {
72        let pgid = ret_c_int(syscall_readonly!(__NR_getpgid, c_int(Pid::as_raw(pid))))?;
73        debug_assert!(pgid > 0);
74        Ok(Pid::from_raw_unchecked(pgid))
75    }
76}
77
78#[inline]
79pub(crate) fn setpgid(pid: Option<Pid>, pgid: Option<Pid>) -> io::Result<()> {
80    unsafe {
81        ret(syscall_readonly!(
82            __NR_setpgid,
83            c_int(Pid::as_raw(pid)),
84            c_int(Pid::as_raw(pgid))
85        ))
86    }
87}
88
89#[inline]
90#[must_use]
91pub(crate) fn getpgrp() -> Pid {
92    // Use the `getpgrp` syscall if available.
93    #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
94    unsafe {
95        let pgid = ret_c_int_infallible(syscall_readonly!(__NR_getpgrp));
96        debug_assert!(pgid > 0);
97        Pid::from_raw_unchecked(pgid)
98    }
99
100    // Otherwise use `getpgrp` and pass it zero.
101    #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
102    unsafe {
103        let pgid = ret_c_int_infallible(syscall_readonly!(__NR_getpgid, c_uint(0)));
104        debug_assert!(pgid > 0);
105        Pid::from_raw_unchecked(pgid)
106    }
107}
108
109#[cfg(feature = "fs")]
110#[inline]
111pub(crate) fn umask(mode: Mode) -> Mode {
112    unsafe { Mode::from_bits_retain(ret_c_uint_infallible(syscall_readonly!(__NR_umask, mode))) }
113}
114
115#[inline]
116pub(crate) fn nice(inc: i32) -> io::Result<i32> {
117    let priority = (if inc > -40 && inc < 40 {
118        inc + getpriority_process(None)?
119    } else {
120        inc
121    })
122    .clamp(-20, 19);
123    setpriority_process(None, priority)?;
124    Ok(priority)
125}
126
127#[inline]
128pub(crate) fn getpriority_user(uid: Uid) -> io::Result<i32> {
129    unsafe {
130        Ok(20
131            - ret_c_int(syscall_readonly!(
132                __NR_getpriority,
133                c_uint(PRIO_USER),
134                c_uint(uid.as_raw())
135            ))?)
136    }
137}
138
139#[inline]
140pub(crate) fn getpriority_pgrp(pgid: Option<Pid>) -> io::Result<i32> {
141    unsafe {
142        Ok(20
143            - ret_c_int(syscall_readonly!(
144                __NR_getpriority,
145                c_uint(PRIO_PGRP),
146                c_int(Pid::as_raw(pgid))
147            ))?)
148    }
149}
150
151#[inline]
152pub(crate) fn getpriority_process(pid: Option<Pid>) -> io::Result<i32> {
153    unsafe {
154        Ok(20
155            - ret_c_int(syscall_readonly!(
156                __NR_getpriority,
157                c_uint(PRIO_PROCESS),
158                c_int(Pid::as_raw(pid))
159            ))?)
160    }
161}
162
163#[inline]
164pub(crate) fn setpriority_user(uid: Uid, priority: i32) -> io::Result<()> {
165    unsafe {
166        ret(syscall_readonly!(
167            __NR_setpriority,
168            c_uint(PRIO_USER),
169            c_uint(uid.as_raw()),
170            c_int(priority)
171        ))
172    }
173}
174
175#[inline]
176pub(crate) fn setpriority_pgrp(pgid: Option<Pid>, priority: i32) -> io::Result<()> {
177    unsafe {
178        ret(syscall_readonly!(
179            __NR_setpriority,
180            c_uint(PRIO_PGRP),
181            c_int(Pid::as_raw(pgid)),
182            c_int(priority)
183        ))
184    }
185}
186
187#[inline]
188pub(crate) fn setpriority_process(pid: Option<Pid>, priority: i32) -> io::Result<()> {
189    unsafe {
190        ret(syscall_readonly!(
191            __NR_setpriority,
192            c_uint(PRIO_PROCESS),
193            c_int(Pid::as_raw(pid)),
194            c_int(priority)
195        ))
196    }
197}
198
199#[inline]
200pub(crate) fn getrlimit(limit: Resource) -> Rlimit {
201    let mut result = MaybeUninit::<rlimit64>::uninit();
202    unsafe {
203        ret_infallible(syscall!(
204            __NR_prlimit64,
205            c_uint(0),
206            limit,
207            null::<c::c_void>(),
208            &mut result
209        ));
210        rlimit_from_linux(result.assume_init())
211    }
212}
213
214#[inline]
215pub(crate) fn setrlimit(limit: Resource, new: Rlimit) -> io::Result<()> {
216    unsafe {
217        let lim = rlimit_to_linux(new);
218        match ret(syscall_readonly!(
219            __NR_prlimit64,
220            c_uint(0),
221            limit,
222            by_ref(&lim),
223            null_mut::<c::c_void>()
224        )) {
225            Ok(()) => Ok(()),
226            Err(err) => Err(err),
227        }
228    }
229}
230
231#[inline]
232pub(crate) fn prlimit(pid: Option<Pid>, limit: Resource, new: Rlimit) -> io::Result<Rlimit> {
233    let lim = rlimit_to_linux(new);
234    let mut result = MaybeUninit::<rlimit64>::uninit();
235    unsafe {
236        match ret(syscall!(
237            __NR_prlimit64,
238            c_int(Pid::as_raw(pid)),
239            limit,
240            by_ref(&lim),
241            &mut result
242        )) {
243            Ok(()) => Ok(rlimit_from_linux(result.assume_init())),
244            Err(err) => Err(err),
245        }
246    }
247}
248
249/// Convert a C `rlimit64` to a Rust `Rlimit`.
250#[inline]
251fn rlimit_from_linux(lim: rlimit64) -> Rlimit {
252    let current = if lim.rlim_cur == RLIM64_INFINITY as u64 {
253        None
254    } else {
255        Some(lim.rlim_cur)
256    };
257    let maximum = if lim.rlim_max == RLIM64_INFINITY as u64 {
258        None
259    } else {
260        Some(lim.rlim_max)
261    };
262    Rlimit { current, maximum }
263}
264
265/// Convert a Rust [`Rlimit`] to a C `rlimit64`.
266#[inline]
267fn rlimit_to_linux(lim: Rlimit) -> rlimit64 {
268    let rlim_cur = match lim.current {
269        Some(r) => r,
270        None => RLIM64_INFINITY as _,
271    };
272    let rlim_max = match lim.maximum {
273        Some(r) => r,
274        None => RLIM64_INFINITY as _,
275    };
276    rlimit64 { rlim_cur, rlim_max }
277}
278
279#[inline]
280pub(crate) fn wait(waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> {
281    _waitpid(!0, waitopts)
282}
283
284#[inline]
285pub(crate) fn waitpid(
286    pid: Option<Pid>,
287    waitopts: WaitOptions,
288) -> io::Result<Option<(Pid, WaitStatus)>> {
289    _waitpid(Pid::as_raw(pid), waitopts)
290}
291
292#[inline]
293pub(crate) fn waitpgid(pgid: Pid, waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> {
294    _waitpid(-pgid.as_raw_nonzero().get(), waitopts)
295}
296
297#[inline]
298pub(crate) fn _waitpid(
299    pid: RawPid,
300    waitopts: WaitOptions,
301) -> io::Result<Option<(Pid, WaitStatus)>> {
302    unsafe {
303        let mut status = MaybeUninit::<i32>::uninit();
304        let pid = ret_c_int(syscall!(
305            __NR_wait4,
306            c_int(pid as _),
307            &mut status,
308            c_int(waitopts.bits() as _),
309            zero()
310        ))?;
311        Ok(Pid::from_raw(pid).map(|pid| (pid, WaitStatus::new(status.assume_init()))))
312    }
313}
314
315#[inline]
316pub(crate) fn waitid(id: WaitId<'_>, options: WaitIdOptions) -> io::Result<Option<WaitIdStatus>> {
317    // Get the id to wait on.
318    match id {
319        WaitId::All => _waitid_all(options),
320        WaitId::Pid(pid) => _waitid_pid(pid, options),
321        WaitId::Pgid(pid) => _waitid_pgid(pid, options),
322        WaitId::PidFd(fd) => _waitid_pidfd(fd, options),
323    }
324}
325
326#[inline]
327fn _waitid_all(options: WaitIdOptions) -> io::Result<Option<WaitIdStatus>> {
328    // `waitid` can return successfully without initializing the struct (no
329    // children found when using `WNOHANG`)
330    let mut status = MaybeUninit::<c::siginfo_t>::zeroed();
331    unsafe {
332        ret(syscall!(
333            __NR_waitid,
334            c_uint(c::P_ALL),
335            c_uint(0),
336            &mut status,
337            c_int(options.bits() as _),
338            zero()
339        ))?
340    };
341
342    Ok(unsafe { cvt_waitid_status(status) })
343}
344
345#[inline]
346fn _waitid_pid(pid: Pid, options: WaitIdOptions) -> io::Result<Option<WaitIdStatus>> {
347    // `waitid` can return successfully without initializing the struct (no
348    // children found when using `WNOHANG`)
349    let mut status = MaybeUninit::<c::siginfo_t>::zeroed();
350    unsafe {
351        ret(syscall!(
352            __NR_waitid,
353            c_uint(c::P_PID),
354            c_int(Pid::as_raw(Some(pid))),
355            &mut status,
356            c_int(options.bits() as _),
357            zero()
358        ))?
359    };
360
361    Ok(unsafe { cvt_waitid_status(status) })
362}
363
364#[inline]
365fn _waitid_pgid(pgid: Option<Pid>, options: WaitIdOptions) -> io::Result<Option<WaitIdStatus>> {
366    // `waitid` can return successfully without initializing the struct (no
367    // children found when using `WNOHANG`)
368    let mut status = MaybeUninit::<c::siginfo_t>::zeroed();
369    unsafe {
370        ret(syscall!(
371            __NR_waitid,
372            c_uint(c::P_PGID),
373            c_int(Pid::as_raw(pgid)),
374            &mut status,
375            c_int(options.bits() as _),
376            zero()
377        ))?
378    };
379
380    Ok(unsafe { cvt_waitid_status(status) })
381}
382
383#[inline]
384fn _waitid_pidfd(fd: BorrowedFd<'_>, options: WaitIdOptions) -> io::Result<Option<WaitIdStatus>> {
385    // `waitid` can return successfully without initializing the struct (no
386    // children found when using `WNOHANG`)
387    let mut status = MaybeUninit::<c::siginfo_t>::zeroed();
388    unsafe {
389        ret(syscall!(
390            __NR_waitid,
391            c_uint(c::P_PIDFD),
392            c_uint(fd.as_raw_fd() as _),
393            &mut status,
394            c_int(options.bits() as _),
395            zero()
396        ))?
397    };
398
399    Ok(unsafe { cvt_waitid_status(status) })
400}
401
402/// Convert a `siginfo_t` to a `WaitIdStatus`.
403///
404/// # Safety
405///
406/// The caller must ensure that `status` is initialized and that `waitid`
407/// returned successfully.
408#[inline]
409unsafe fn cvt_waitid_status(status: MaybeUninit<c::siginfo_t>) -> Option<WaitIdStatus> {
410    let status = status.assume_init();
411    if status
412        .__bindgen_anon_1
413        .__bindgen_anon_1
414        ._sifields
415        ._sigchld
416        ._pid
417        == 0
418    {
419        None
420    } else {
421        Some(WaitIdStatus(status))
422    }
423}
424
425#[inline]
426pub(crate) fn getsid(pid: Option<Pid>) -> io::Result<Pid> {
427    unsafe {
428        let pid = ret_c_int(syscall_readonly!(__NR_getsid, c_int(Pid::as_raw(pid))))?;
429        Ok(Pid::from_raw_unchecked(pid))
430    }
431}
432
433#[inline]
434pub(crate) fn setsid() -> io::Result<Pid> {
435    unsafe {
436        let pid = ret_c_int(syscall_readonly!(__NR_setsid))?;
437        Ok(Pid::from_raw_unchecked(pid))
438    }
439}
440
441#[inline]
442pub(crate) fn kill_process(pid: Pid, sig: Signal) -> io::Result<()> {
443    unsafe { ret(syscall_readonly!(__NR_kill, pid, sig)) }
444}
445
446#[inline]
447pub(crate) fn kill_process_group(pid: Pid, sig: Signal) -> io::Result<()> {
448    unsafe { ret(syscall_readonly!(__NR_kill, negative_pid(pid), sig)) }
449}
450
451#[inline]
452pub(crate) fn kill_current_process_group(sig: Signal) -> io::Result<()> {
453    unsafe { ret(syscall_readonly!(__NR_kill, pass_usize(0), sig)) }
454}
455
456#[inline]
457pub(crate) fn test_kill_process(pid: Pid) -> io::Result<()> {
458    unsafe { ret(syscall_readonly!(__NR_kill, pid, pass_usize(0))) }
459}
460
461#[inline]
462pub(crate) fn test_kill_process_group(pid: Pid) -> io::Result<()> {
463    unsafe {
464        ret(syscall_readonly!(
465            __NR_kill,
466            negative_pid(pid),
467            pass_usize(0)
468        ))
469    }
470}
471
472#[inline]
473pub(crate) fn test_kill_current_process_group() -> io::Result<()> {
474    unsafe { ret(syscall_readonly!(__NR_kill, pass_usize(0), pass_usize(0))) }
475}
476
477#[inline]
478pub(crate) fn pidfd_getfd(
479    pidfd: BorrowedFd<'_>,
480    targetfd: RawFd,
481    flags: PidfdGetfdFlags,
482) -> io::Result<OwnedFd> {
483    unsafe {
484        ret_owned_fd(syscall_readonly!(
485            __NR_pidfd_getfd,
486            pidfd,
487            raw_fd(targetfd),
488            c_int(flags.bits() as _)
489        ))
490    }
491}
492
493#[inline]
494pub(crate) fn pidfd_open(pid: Pid, flags: PidfdFlags) -> io::Result<OwnedFd> {
495    unsafe { ret_owned_fd(syscall_readonly!(__NR_pidfd_open, pid, flags)) }
496}
497
498#[inline]
499pub(crate) fn pidfd_send_signal(fd: BorrowedFd<'_>, sig: Signal) -> io::Result<()> {
500    unsafe {
501        ret(syscall_readonly!(
502            __NR_pidfd_send_signal,
503            fd,
504            sig,
505            pass_usize(0),
506            pass_usize(0)
507        ))
508    }
509}
510
511#[cfg(feature = "fs")]
512#[inline]
513pub(crate) fn pivot_root(new_root: &CStr, put_old: &CStr) -> io::Result<()> {
514    unsafe { ret(syscall_readonly!(__NR_pivot_root, new_root, put_old)) }
515}
516
517#[cfg(feature = "alloc")]
518#[inline]
519pub(crate) fn getgroups(buf: &mut [Gid]) -> io::Result<usize> {
520    let len = buf.len().try_into().map_err(|_| io::Errno::NOMEM)?;
521
522    unsafe {
523        ret_usize(syscall!(
524            __NR_getgroups,
525            c_int(len),
526            slice_just_addr_mut(buf)
527        ))
528    }
529}
530
531#[inline]
532pub(crate) fn fcntl_getlk(fd: BorrowedFd<'_>, lock: &Flock) -> io::Result<Option<Flock>> {
533    let mut curr_lock: c::flock = lock.as_raw();
534    #[cfg(target_pointer_width = "32")]
535    unsafe {
536        ret(syscall!(
537            __NR_fcntl64,
538            fd,
539            c_uint(c::F_GETLK64),
540            by_mut(&mut curr_lock)
541        ))?
542    }
543    #[cfg(target_pointer_width = "64")]
544    unsafe {
545        ret(syscall!(
546            __NR_fcntl,
547            fd,
548            c_uint(c::F_GETLK),
549            by_mut(&mut curr_lock)
550        ))?
551    }
552
553    // If no blocking lock is found, `fcntl(GETLK, ..)` sets `l_type` to
554    // `F_UNLCK`.
555    if curr_lock.l_type == c::F_UNLCK as _ {
556        Ok(None)
557    } else {
558        Ok(Some(unsafe { Flock::from_raw_unchecked(curr_lock) }))
559    }
560}