1#![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 #[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 #[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#[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#[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 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 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 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 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 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#[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 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}