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(raw:syscall_readonly!(__NR_chdir, filename)) }
69}
70
71#[inline]
72pub(crate) fn fchdir(fd: BorrowedFd<'_>) -> io::Result<()> {
73 unsafe { ret(raw:syscall_readonly!(__NR_fchdir, fd)) }
74}
75
76#[cfg(feature = "fs")]
77#[inline]
78pub(crate) fn chroot(filename: &CStr) -> io::Result<()> {
79 unsafe { ret(raw: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: ArgReg<'_, A0>, buf_len: ArgReg<'_, A1>) = slice_mut(buf);
86 unsafe { ret_usize(raw: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(raw:syscall!(
93 __NR_membarrier,
94 c_int(membarrier_cmd::MEMBARRIER_CMD_QUERY as _),
95 c_uint(0)
96 )) {
97 Ok(query: u32) => MembarrierQuery::from_bits_retain(bits:query),
98 Err(_) => MembarrierQuery::empty(),
99 }
100 }
101}
102
103#[inline]
104pub(crate) fn membarrier(cmd: MembarrierCommand) -> io::Result<()> {
105 unsafe { ret(raw: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(raw: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: i32 = ret_c_int_infallible(raw: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: i32 = ret_c_int(raw:syscall_readonly!(__NR_getpgid, c_int(Pid::as_raw(pid))))?;
132 debug_assert!(pgid > 0);
133 Ok(Pid::from_raw_unchecked(raw:pgid))
134 }
135}
136
137#[inline]
138pub(crate) fn setpgid(pid: Option<Pid>, pgid: Option<Pid>) -> io::Result<()> {
139 unsafe {
140 ret(raw: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: i32 = ret_c_int_infallible(raw:syscall_readonly!(__NR_getpgrp));
154 debug_assert!(pgid > 0);
155 Pid::from_raw_unchecked(raw: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: usize = ret_usize(raw: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: *mut u8 = as_mut_ptr(cpuset).cast::<u8>();
180 let rest: *mut u8 = bytes.wrapping_add(count:size);
181 // Zero every byte in the cpuset not set by the kernel.
182 rest.write_bytes(val:0, count: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(raw: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(bits:ret_c_uint_infallible(raw:syscall_readonly!(__NR_umask, mode))) }
212}
213
214#[inline]
215pub(crate) fn nice(inc: i32) -> io::Result<i32> {
216 let priority: i32 = (if inc > -40 && inc < 40 {
217 inc + getpriority_process(None)?
218 } else {
219 inc
220 })
221 .clamp(min:-20, max:19);
222 setpriority_process(pid: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(raw: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(raw: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(raw: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(raw: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(raw: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(raw: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 = MaybeUninit::<rlimit64>::uninit();
301 unsafe {
302 ret_infallible(raw:syscall!(
303 __NR_prlimit64,
304 c_uint(0),
305 limit,
306 null::<c::c_void>(),
307 &mut result
308 ));
309 rlimit_from_linux(lim:result.assume_init())
310 }
311}
312
313#[inline]
314pub(crate) fn setrlimit(limit: Resource, new: Rlimit) -> io::Result<()> {
315 unsafe {
316 let lim: rlimit64 = rlimit_to_linux(lim:new.clone());
317 match ret(raw: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: Errno) => 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: rlimit64 = rlimit_to_linux(lim:new);
333 let mut result: MaybeUninit = MaybeUninit::<rlimit64>::uninit();
334 unsafe {
335 match ret(raw: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(lim:result.assume_init())),
343 Err(err: Errno) => Err(err),
344 }
345 }
346}
347
348/// Convert a Rust [`Rlimit`] to a C `rlimit64`.
349#[inline]
350fn rlimit_from_linux(lim: rlimit64) -> Rlimit {
351 let current: Option = if lim.rlim_cur == RLIM64_INFINITY as _ {
352 None
353 } else {
354 Some(lim.rlim_cur)
355 };
356 let maximum: Option = if lim.rlim_max == RLIM64_INFINITY as _ {
357 None
358 } else {
359 Some(lim.rlim_max)
360 };
361 Rlimit { current, maximum }
362}
363
364/// Convert a C `rlimit64` to a Rust `Rlimit`.
365#[inline]
366fn rlimit_to_linux(lim: Rlimit) -> rlimit64 {
367 let rlim_cur: u64 = match lim.current {
368 Some(r: u64) => r,
369 None => RLIM64_INFINITY as _,
370 };
371 let rlim_max: u64 = match lim.maximum {
372 Some(r: u64) => 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 = MaybeUninit::<u32>::uninit();
403 let pid: i32 = ret_c_int(raw: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| (pid, WaitStatus::new(status: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: Pid) => _waitid_pid(pid, options),
420 WaitId::Pgid(pid: Option) => _waitid_pgid(pgid:pid, options),
421 WaitId::PidFd(fd: BorrowedFd<'_>) => _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 = MaybeUninit::<c::siginfo_t>::zeroed();
430 unsafe {
431 ret(raw: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 = MaybeUninit::<c::siginfo_t>::zeroed();
449 unsafe {
450 ret(raw: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 = MaybeUninit::<c::siginfo_t>::zeroed();
468 unsafe {
469 ret(raw: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 = MaybeUninit::<c::siginfo_t>::zeroed();
487 unsafe {
488 ret(raw: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: siginfo = 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: i32 = ret_c_int(raw:syscall_readonly!(__NR_getsid, c_int(Pid::as_raw(pid))))?;
522 Ok(Pid::from_raw_unchecked(raw:pid))
523 }
524}
525
526#[inline]
527pub(crate) fn setsid() -> io::Result<Pid> {
528 unsafe {
529 let pid: i32 = ret_c_int(raw:syscall_readonly!(__NR_setsid))?;
530 Ok(Pid::from_raw_unchecked(raw:pid))
531 }
532}
533
534#[inline]
535pub(crate) fn kill_process(pid: Pid, sig: Signal) -> io::Result<()> {
536 unsafe { ret(raw: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(raw: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(raw: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(raw: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(raw: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(raw: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(raw: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(raw: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(raw: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 = "alloc")]
605#[inline]
606pub(crate) fn getgroups(buf: &mut [Gid]) -> io::Result<usize> {
607 let len: i32 = buf.len().try_into().map_err(|_| io::Errno::NOMEM)?;
608
609 unsafe {
610 ret_usize(raw:syscall!(
611 __NR_getgroups,
612 c_int(len),
613 slice_just_addr_mut(buf)
614 ))
615 }
616}
617