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