• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! linux_raw syscalls supporting `rustix::process`.
2 //!
3 //! # Safety
4 //!
5 //! See the `rustix::backend` module documentation for details.
6 #![allow(unsafe_code)]
7 #![allow(clippy::undocumented_unsafe_blocks)]
8 
9 use super::super::c;
10 use super::super::conv::{
11     by_mut, by_ref, c_int, c_uint, negative_pid, pass_usize, ret, ret_c_int, ret_c_uint,
12     ret_infallible, ret_usize, ret_usize_infallible, size_of, slice_just_addr, slice_mut, zero,
13 };
14 use super::types::{RawCpuSet, RawUname};
15 use crate::fd::BorrowedFd;
16 use crate::ffi::CStr;
17 use crate::io;
18 use crate::process::{
19     Cpuid, Gid, MembarrierCommand, MembarrierQuery, Pid, RawNonZeroPid, RawPid, Resource, Rlimit,
20     Signal, Uid, WaitOptions, WaitStatus,
21 };
22 use core::convert::TryInto;
23 use core::mem::MaybeUninit;
24 use core::num::NonZeroU32;
25 use core::ptr::{null, null_mut};
26 use linux_raw_sys::general::{
27     __kernel_gid_t, __kernel_pid_t, __kernel_uid_t, membarrier_cmd, membarrier_cmd_flag, rlimit,
28     rlimit64, PRIO_PGRP, PRIO_PROCESS, PRIO_USER, RLIM64_INFINITY, RLIM_INFINITY,
29 };
30 
31 #[inline]
chdir(filename: &CStr) -> io::Result<()>32 pub(crate) fn chdir(filename: &CStr) -> io::Result<()> {
33     unsafe { ret(syscall_readonly!(__NR_chdir, filename)) }
34 }
35 
36 #[inline]
fchdir(fd: BorrowedFd<'_>) -> io::Result<()>37 pub(crate) fn fchdir(fd: BorrowedFd<'_>) -> io::Result<()> {
38     unsafe { ret(syscall_readonly!(__NR_fchdir, fd)) }
39 }
40 
41 #[inline]
getcwd(buf: &mut [u8]) -> io::Result<usize>42 pub(crate) fn getcwd(buf: &mut [u8]) -> io::Result<usize> {
43     let (buf_addr_mut, buf_len) = slice_mut(buf);
44     unsafe { ret_usize(syscall!(__NR_getcwd, buf_addr_mut, buf_len)) }
45 }
46 
47 #[inline]
membarrier_query() -> MembarrierQuery48 pub(crate) fn membarrier_query() -> MembarrierQuery {
49     unsafe {
50         match ret_c_uint(syscall!(
51             __NR_membarrier,
52             c_int(membarrier_cmd::MEMBARRIER_CMD_QUERY as _),
53             c_uint(0)
54         )) {
55             Ok(query) => {
56                 // Safety: The safety of `from_bits_unchecked` is discussed
57                 // [here]. Our "source of truth" is Linux, and here, the
58                 // `query` value is coming from Linux, so we know it only
59                 // contains "source of truth" valid bits.
60                 //
61                 // [here]: https://github.com/bitflags/bitflags/pull/207#issuecomment-671668662
62                 MembarrierQuery::from_bits_unchecked(query)
63             }
64             Err(_) => MembarrierQuery::empty(),
65         }
66     }
67 }
68 
69 #[inline]
membarrier(cmd: MembarrierCommand) -> io::Result<()>70 pub(crate) fn membarrier(cmd: MembarrierCommand) -> io::Result<()> {
71     unsafe { ret(syscall!(__NR_membarrier, cmd, c_uint(0))) }
72 }
73 
74 #[inline]
membarrier_cpu(cmd: MembarrierCommand, cpu: Cpuid) -> io::Result<()>75 pub(crate) fn membarrier_cpu(cmd: MembarrierCommand, cpu: Cpuid) -> io::Result<()> {
76     unsafe {
77         ret(syscall!(
78             __NR_membarrier,
79             cmd,
80             c_uint(membarrier_cmd_flag::MEMBARRIER_CMD_FLAG_CPU as _),
81             cpu
82         ))
83     }
84 }
85 
86 #[inline]
getpid() -> Pid87 pub(crate) fn getpid() -> Pid {
88     unsafe {
89         let pid: i32 = ret_usize_infallible(syscall_readonly!(__NR_getpid)) as __kernel_pid_t;
90         debug_assert!(pid > 0);
91         Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pid as u32))
92     }
93 }
94 
95 #[inline]
getppid() -> Option<Pid>96 pub(crate) fn getppid() -> Option<Pid> {
97     unsafe {
98         let ppid: i32 = ret_usize_infallible(syscall_readonly!(__NR_getppid)) as __kernel_pid_t;
99         Pid::from_raw(ppid as u32)
100     }
101 }
102 
103 #[inline]
getpgid(pid: Option<Pid>) -> io::Result<Pid>104 pub(crate) fn getpgid(pid: Option<Pid>) -> io::Result<Pid> {
105     unsafe {
106         let pgid: i32 =
107             ret_usize(syscall_readonly!(__NR_getpgid, c_uint(Pid::as_raw(pid))))? as __kernel_pid_t;
108         Ok(Pid::from_raw_nonzero(NonZeroU32::new_unchecked(
109             pgid as u32,
110         )))
111     }
112 }
113 
114 #[inline]
getpgrp() -> Pid115 pub(crate) fn getpgrp() -> Pid {
116     // Use the `getpgrp` syscall if available.
117     #[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
118     unsafe {
119         let pgid: i32 = ret_usize_infallible(syscall_readonly!(__NR_getpgrp)) as __kernel_pid_t;
120         debug_assert!(pgid > 0);
121         Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pgid as u32))
122     }
123 
124     // Otherwise use `getpgrp` and pass it zero.
125     #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
126     unsafe {
127         let pgid: i32 =
128             ret_usize_infallible(syscall_readonly!(__NR_getpgid, c_uint(0))) as __kernel_pid_t;
129         debug_assert!(pgid > 0);
130         Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(pgid as u32))
131     }
132 }
133 
134 #[inline]
getgid() -> Gid135 pub(crate) fn getgid() -> Gid {
136     #[cfg(any(target_arch = "x86", target_arch = "sparc", target_arch = "arm"))]
137     unsafe {
138         let gid: i32 =
139             (ret_usize_infallible(syscall_readonly!(__NR_getgid32)) as __kernel_gid_t).into();
140         Gid::from_raw(gid as u32)
141     }
142     #[cfg(not(any(target_arch = "x86", target_arch = "sparc", target_arch = "arm")))]
143     unsafe {
144         let gid = ret_usize_infallible(syscall_readonly!(__NR_getgid)) as __kernel_gid_t;
145         Gid::from_raw(gid as u32)
146     }
147 }
148 
149 #[inline]
getegid() -> Gid150 pub(crate) fn getegid() -> Gid {
151     #[cfg(any(target_arch = "x86", target_arch = "sparc", target_arch = "arm"))]
152     unsafe {
153         let gid: i32 =
154             (ret_usize_infallible(syscall_readonly!(__NR_getegid32)) as __kernel_gid_t).into();
155         Gid::from_raw(gid as u32)
156     }
157     #[cfg(not(any(target_arch = "x86", target_arch = "sparc", target_arch = "arm")))]
158     unsafe {
159         let gid = ret_usize_infallible(syscall_readonly!(__NR_getegid)) as __kernel_gid_t;
160         Gid::from_raw(gid as u32)
161     }
162 }
163 
164 #[inline]
getuid() -> Uid165 pub(crate) fn getuid() -> Uid {
166     #[cfg(any(target_arch = "x86", target_arch = "sparc", target_arch = "arm"))]
167     unsafe {
168         let uid = (ret_usize_infallible(syscall_readonly!(__NR_getuid32)) as __kernel_uid_t).into();
169         Uid::from_raw(uid)
170     }
171     #[cfg(not(any(target_arch = "x86", target_arch = "sparc", target_arch = "arm")))]
172     unsafe {
173         let uid = ret_usize_infallible(syscall_readonly!(__NR_getuid)) as __kernel_uid_t;
174         Uid::from_raw(uid as u32)
175     }
176 }
177 
178 #[inline]
geteuid() -> Uid179 pub(crate) fn geteuid() -> Uid {
180     #[cfg(any(target_arch = "x86", target_arch = "sparc", target_arch = "arm"))]
181     unsafe {
182         let uid: i32 =
183             (ret_usize_infallible(syscall_readonly!(__NR_geteuid32)) as __kernel_uid_t).into();
184         Uid::from_raw(uid as u32)
185     }
186     #[cfg(not(any(target_arch = "x86", target_arch = "sparc", target_arch = "arm")))]
187     unsafe {
188         let uid = ret_usize_infallible(syscall_readonly!(__NR_geteuid)) as __kernel_uid_t;
189         Uid::from_raw(uid as u32)
190     }
191 }
192 
193 #[inline]
sched_getaffinity(pid: Option<Pid>, cpuset: &mut RawCpuSet) -> io::Result<()>194 pub(crate) fn sched_getaffinity(pid: Option<Pid>, cpuset: &mut RawCpuSet) -> io::Result<()> {
195     unsafe {
196         // The raw linux syscall returns the size (in bytes) of the `cpumask_t`
197         // data type that is used internally by the kernel to represent the CPU
198         // set bit mask.
199         let size = ret_usize(syscall!(
200             __NR_sched_getaffinity,
201             c_uint(Pid::as_raw(pid)),
202             size_of::<RawCpuSet, _>(),
203             by_mut(&mut cpuset.bits)
204         ))?;
205         let bytes = (cpuset as *mut RawCpuSet).cast::<u8>();
206         let rest = bytes.wrapping_add(size);
207         // Zero every byte in the cpuset not set by the kernel.
208         rest.write_bytes(0, core::mem::size_of::<RawCpuSet>() - size);
209         Ok(())
210     }
211 }
212 
213 #[inline]
sched_setaffinity(pid: Option<Pid>, cpuset: &RawCpuSet) -> io::Result<()>214 pub(crate) fn sched_setaffinity(pid: Option<Pid>, cpuset: &RawCpuSet) -> io::Result<()> {
215     unsafe {
216         ret(syscall_readonly!(
217             __NR_sched_setaffinity,
218             c_uint(Pid::as_raw(pid)),
219             size_of::<RawCpuSet, _>(),
220             slice_just_addr(&cpuset.bits)
221         ))
222     }
223 }
224 
225 #[inline]
sched_yield()226 pub(crate) fn sched_yield() {
227     unsafe {
228         // See the documentation for [`crate::process::sched_yield`] for why
229         // errors are ignored.
230         syscall_readonly!(__NR_sched_yield).decode_void();
231     }
232 }
233 
234 #[inline]
uname() -> RawUname235 pub(crate) fn uname() -> RawUname {
236     let mut uname = MaybeUninit::<RawUname>::uninit();
237     unsafe {
238         ret(syscall!(__NR_uname, &mut uname)).unwrap();
239         uname.assume_init()
240     }
241 }
242 
243 #[inline]
nice(inc: i32) -> io::Result<i32>244 pub(crate) fn nice(inc: i32) -> io::Result<i32> {
245     let priority = if inc > -40 && inc < 40 {
246         inc + getpriority_process(None)?
247     } else {
248         inc
249     }
250     // TODO: With Rust 1.50, use `.clamp` instead of `.min` and `.max`.
251     //.clamp(-20, 19);
252     .min(19)
253     .max(-20);
254     setpriority_process(None, priority)?;
255     Ok(priority)
256 }
257 
258 #[inline]
getpriority_user(uid: Uid) -> io::Result<i32>259 pub(crate) fn getpriority_user(uid: Uid) -> io::Result<i32> {
260     unsafe {
261         Ok(20
262             - ret_c_int(syscall_readonly!(
263                 __NR_getpriority,
264                 c_uint(PRIO_USER),
265                 c_uint(uid.as_raw())
266             ))?)
267     }
268 }
269 
270 #[inline]
getpriority_pgrp(pgid: Option<Pid>) -> io::Result<i32>271 pub(crate) fn getpriority_pgrp(pgid: Option<Pid>) -> io::Result<i32> {
272     unsafe {
273         Ok(20
274             - ret_c_int(syscall_readonly!(
275                 __NR_getpriority,
276                 c_uint(PRIO_PGRP),
277                 c_uint(Pid::as_raw(pgid))
278             ))?)
279     }
280 }
281 
282 #[inline]
getpriority_process(pid: Option<Pid>) -> io::Result<i32>283 pub(crate) fn getpriority_process(pid: Option<Pid>) -> io::Result<i32> {
284     unsafe {
285         Ok(20
286             - ret_c_int(syscall_readonly!(
287                 __NR_getpriority,
288                 c_uint(PRIO_PROCESS),
289                 c_uint(Pid::as_raw(pid))
290             ))?)
291     }
292 }
293 
294 #[inline]
setpriority_user(uid: Uid, priority: i32) -> io::Result<()>295 pub(crate) fn setpriority_user(uid: Uid, priority: i32) -> io::Result<()> {
296     unsafe {
297         ret(syscall_readonly!(
298             __NR_setpriority,
299             c_uint(PRIO_USER),
300             c_uint(uid.as_raw()),
301             c_int(priority)
302         ))
303     }
304 }
305 
306 #[inline]
setpriority_pgrp(pgid: Option<Pid>, priority: i32) -> io::Result<()>307 pub(crate) fn setpriority_pgrp(pgid: Option<Pid>, priority: i32) -> io::Result<()> {
308     unsafe {
309         ret(syscall_readonly!(
310             __NR_setpriority,
311             c_uint(PRIO_PGRP),
312             c_uint(Pid::as_raw(pgid)),
313             c_int(priority)
314         ))
315     }
316 }
317 
318 #[inline]
setpriority_process(pid: Option<Pid>, priority: i32) -> io::Result<()>319 pub(crate) fn setpriority_process(pid: Option<Pid>, priority: i32) -> io::Result<()> {
320     unsafe {
321         ret(syscall_readonly!(
322             __NR_setpriority,
323             c_uint(PRIO_PROCESS),
324             c_uint(Pid::as_raw(pid)),
325             c_int(priority)
326         ))
327     }
328 }
329 
330 #[inline]
getrlimit(limit: Resource) -> Rlimit331 pub(crate) fn getrlimit(limit: Resource) -> Rlimit {
332     let mut result = MaybeUninit::<rlimit64>::uninit();
333     unsafe {
334         match ret(syscall!(
335             __NR_prlimit64,
336             c_uint(0),
337             limit,
338             null::<c::c_void>(),
339             &mut result
340         )) {
341             Ok(()) => rlimit_from_linux(result.assume_init()),
342             Err(err) => {
343                 debug_assert_eq!(err, io::Errno::NOSYS);
344                 getrlimit_old(limit)
345             }
346         }
347     }
348 }
349 
350 /// The old 32-bit-only `getrlimit` syscall, for when we lack the new
351 /// `prlimit64`.
getrlimit_old(limit: Resource) -> Rlimit352 unsafe fn getrlimit_old(limit: Resource) -> Rlimit {
353     let mut result = MaybeUninit::<rlimit>::uninit();
354 
355     // On these platforms, `__NR_getrlimit` is called `__NR_ugetrlimit`.
356     #[cfg(any(
357         target_arch = "arm",
358         target_arch = "powerpc",
359         target_arch = "powerpc64",
360         target_arch = "x86",
361     ))]
362     {
363         ret_infallible(syscall!(__NR_ugetrlimit, limit, &mut result));
364     }
365 
366     // On these platforms, it's just `__NR_getrlimit`.
367     #[cfg(not(any(
368         target_arch = "arm",
369         target_arch = "powerpc",
370         target_arch = "powerpc64",
371         target_arch = "x86",
372     )))]
373     {
374         ret_infallible(syscall!(__NR_getrlimit, limit, &mut result));
375     }
376 
377     rlimit_from_linux_old(result.assume_init())
378 }
379 
380 #[inline]
setrlimit(limit: Resource, new: Rlimit) -> io::Result<()>381 pub(crate) fn setrlimit(limit: Resource, new: Rlimit) -> io::Result<()> {
382     unsafe {
383         let lim = rlimit_to_linux(new.clone());
384         match ret(syscall_readonly!(
385             __NR_prlimit64,
386             c_uint(0),
387             limit,
388             by_ref(&lim),
389             null_mut::<c::c_void>()
390         )) {
391             Ok(()) => Ok(()),
392             Err(io::Errno::NOSYS) => setrlimit_old(limit, new),
393             Err(err) => Err(err),
394         }
395     }
396 }
397 
398 /// The old 32-bit-only `setrlimit` syscall, for when we lack the new
399 /// `prlimit64`.
setrlimit_old(limit: Resource, new: Rlimit) -> io::Result<()>400 unsafe fn setrlimit_old(limit: Resource, new: Rlimit) -> io::Result<()> {
401     let lim = rlimit_to_linux_old(new)?;
402     ret(syscall_readonly!(__NR_setrlimit, limit, by_ref(&lim)))
403 }
404 
405 #[inline]
prlimit(pid: Option<Pid>, limit: Resource, new: Rlimit) -> io::Result<Rlimit>406 pub(crate) fn prlimit(pid: Option<Pid>, limit: Resource, new: Rlimit) -> io::Result<Rlimit> {
407     let lim = rlimit_to_linux(new);
408     let mut result = MaybeUninit::<rlimit64>::uninit();
409     unsafe {
410         match ret(syscall!(
411             __NR_prlimit64,
412             c_uint(Pid::as_raw(pid)),
413             limit,
414             by_ref(&lim),
415             &mut result
416         )) {
417             Ok(()) => Ok(rlimit_from_linux(result.assume_init())),
418             Err(err) => Err(err),
419         }
420     }
421 }
422 
423 /// Convert a Rust [`Rlimit`] to a C `rlimit64`.
424 #[inline]
rlimit_from_linux(lim: rlimit64) -> Rlimit425 fn rlimit_from_linux(lim: rlimit64) -> Rlimit {
426     let current = if lim.rlim_cur == RLIM64_INFINITY as _ {
427         None
428     } else {
429         Some(lim.rlim_cur)
430     };
431     let maximum = if lim.rlim_max == RLIM64_INFINITY as _ {
432         None
433     } else {
434         Some(lim.rlim_max)
435     };
436     Rlimit { current, maximum }
437 }
438 
439 /// Convert a C `rlimit64` to a Rust `Rlimit`.
440 #[inline]
rlimit_to_linux(lim: Rlimit) -> rlimit64441 fn rlimit_to_linux(lim: Rlimit) -> rlimit64 {
442     let rlim_cur = match lim.current {
443         Some(r) => r,
444         None => RLIM64_INFINITY as _,
445     };
446     let rlim_max = match lim.maximum {
447         Some(r) => r,
448         None => RLIM64_INFINITY as _,
449     };
450     rlimit64 { rlim_cur, rlim_max }
451 }
452 
453 /// Like `rlimit_from_linux` but uses Linux's old 32-bit `rlimit`.
454 #[allow(clippy::useless_conversion)]
rlimit_from_linux_old(lim: rlimit) -> Rlimit455 fn rlimit_from_linux_old(lim: rlimit) -> Rlimit {
456     let current = if lim.rlim_cur == RLIM_INFINITY as _ {
457         None
458     } else {
459         Some(lim.rlim_cur.into())
460     };
461     let maximum = if lim.rlim_max == RLIM_INFINITY as _ {
462         None
463     } else {
464         Some(lim.rlim_max.into())
465     };
466     Rlimit { current, maximum }
467 }
468 
469 /// Like `rlimit_to_linux` but uses Linux's old 32-bit `rlimit`.
470 #[allow(clippy::useless_conversion)]
rlimit_to_linux_old(lim: Rlimit) -> io::Result<rlimit>471 fn rlimit_to_linux_old(lim: Rlimit) -> io::Result<rlimit> {
472     let rlim_cur = match lim.current {
473         Some(r) => r.try_into().map_err(|_e| io::Errno::INVAL)?,
474         None => RLIM_INFINITY as _,
475     };
476     let rlim_max = match lim.maximum {
477         Some(r) => r.try_into().map_err(|_e| io::Errno::INVAL)?,
478         None => RLIM_INFINITY as _,
479     };
480     Ok(rlimit { rlim_cur, rlim_max })
481 }
482 
483 #[inline]
wait(waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>>484 pub(crate) fn wait(waitopts: WaitOptions) -> io::Result<Option<(Pid, WaitStatus)>> {
485     _waitpid(!0, waitopts)
486 }
487 
488 #[inline]
waitpid( pid: Option<Pid>, waitopts: WaitOptions, ) -> io::Result<Option<(Pid, WaitStatus)>>489 pub(crate) fn waitpid(
490     pid: Option<Pid>,
491     waitopts: WaitOptions,
492 ) -> io::Result<Option<(Pid, WaitStatus)>> {
493     _waitpid(Pid::as_raw(pid), waitopts)
494 }
495 
496 #[inline]
_waitpid( pid: RawPid, waitopts: WaitOptions, ) -> io::Result<Option<(Pid, WaitStatus)>>497 pub(crate) fn _waitpid(
498     pid: RawPid,
499     waitopts: WaitOptions,
500 ) -> io::Result<Option<(Pid, WaitStatus)>> {
501     unsafe {
502         let mut status = MaybeUninit::<u32>::uninit();
503         let pid = ret_c_uint(syscall!(
504             __NR_wait4,
505             c_int(pid as _),
506             &mut status,
507             c_int(waitopts.bits() as _),
508             zero()
509         ))?;
510         Ok(RawNonZeroPid::new(pid).map(|non_zero| {
511             (
512                 Pid::from_raw_nonzero(non_zero),
513                 WaitStatus::new(status.assume_init()),
514             )
515         }))
516     }
517 }
518 
519 #[cfg(feature = "runtime")]
520 #[inline]
exit_group(code: c::c_int) -> !521 pub(crate) fn exit_group(code: c::c_int) -> ! {
522     unsafe { syscall_noreturn!(__NR_exit_group, c_int(code)) }
523 }
524 
525 #[inline]
setsid() -> io::Result<Pid>526 pub(crate) fn setsid() -> io::Result<Pid> {
527     unsafe {
528         let pid = ret_usize(syscall_readonly!(__NR_setsid))?;
529         debug_assert!(pid > 0);
530         Ok(Pid::from_raw_nonzero(RawNonZeroPid::new_unchecked(
531             pid as u32,
532         )))
533     }
534 }
535 
536 #[inline]
kill_process(pid: Pid, sig: Signal) -> io::Result<()>537 pub(crate) fn kill_process(pid: Pid, sig: Signal) -> io::Result<()> {
538     unsafe { ret(syscall_readonly!(__NR_kill, pid, sig)) }
539 }
540 
541 #[inline]
kill_process_group(pid: Pid, sig: Signal) -> io::Result<()>542 pub(crate) fn kill_process_group(pid: Pid, sig: Signal) -> io::Result<()> {
543     unsafe { ret(syscall_readonly!(__NR_kill, negative_pid(pid), sig)) }
544 }
545 
546 #[inline]
kill_current_process_group(sig: Signal) -> io::Result<()>547 pub(crate) fn kill_current_process_group(sig: Signal) -> io::Result<()> {
548     unsafe { ret(syscall_readonly!(__NR_kill, pass_usize(0), sig)) }
549 }
550 
551 #[inline]
prctl( option: c::c_int, arg2: *mut c::c_void, arg3: *mut c::c_void, arg4: *mut c::c_void, arg5: *mut c::c_void, ) -> io::Result<c::c_int>552 pub(crate) unsafe fn prctl(
553     option: c::c_int,
554     arg2: *mut c::c_void,
555     arg3: *mut c::c_void,
556     arg4: *mut c::c_void,
557     arg5: *mut c::c_void,
558 ) -> io::Result<c::c_int> {
559     ret_c_int(syscall!(__NR_prctl, c_int(option), arg2, arg3, arg4, arg5))
560 }
561