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