Skip to main content

coreshift_core/
process.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/
4
5//! Low-level process management primitives.
6//!
7//! Safe wrappers around `fork`, `setsid`, `setpgid`, `dup2`, `prctl`,
8//! and fd-range close — building blocks for double-fork supervisor patterns.
9
10use crate::CoreError;
11use crate::error::syscall_ret;
12
13/// Result of a [`fork`] call.
14pub enum ForkResult {
15    /// Returned in the parent with the child's PID.
16    Parent(i32),
17    /// Returned in the child (PID = 0).
18    Child,
19}
20
21/// Fork the current process.
22///
23/// # Safety
24/// After `fork`, only async-signal-safe operations are safe in the child
25/// before `exec`. Rust's allocator is not async-signal-safe; use this only
26/// in the narrow pattern of fork → exec or fork → immediate `_exit`.
27pub unsafe fn fork() -> Result<ForkResult, CoreError> {
28    let pid = unsafe { libc::fork() };
29    if pid < 0 {
30        return Err(CoreError::sys(
31            std::io::Error::last_os_error().raw_os_error().unwrap_or(-1),
32            "fork",
33        ));
34    }
35    if pid == 0 {
36        Ok(ForkResult::Child)
37    } else {
38        Ok(ForkResult::Parent(pid))
39    }
40}
41
42/// Create a new session and set the calling process as leader.
43pub fn setsid() -> Result<(), CoreError> {
44    let ret = unsafe { libc::setsid() };
45    if ret < 0 {
46        return Err(CoreError::sys(
47            std::io::Error::last_os_error().raw_os_error().unwrap_or(-1),
48            "setsid",
49        ));
50    }
51    Ok(())
52}
53
54/// Set the process group ID of `pid` to `pgid` (use 0 for self).
55pub fn setpgid(pid: i32, pgid: i32) -> Result<(), CoreError> {
56    syscall_ret(unsafe { libc::setpgid(pid, pgid) }, "setpgid")
57}
58
59/// Redirect stdin, stdout, and stderr to `/dev/null`.
60///
61/// # Safety
62/// Uses `dup2` on file descriptors 0/1/2.
63pub unsafe fn redirect_stdio_to_devnull() -> Result<(), CoreError> {
64    let fd = unsafe {
65        libc::open(b"/dev/null\0".as_ptr() as *const libc::c_char,
66                   libc::O_RDWR)
67    };
68    if fd < 0 {
69        return Err(CoreError::sys(
70            std::io::Error::last_os_error().raw_os_error().unwrap_or(-1),
71            "open:/dev/null",
72        ));
73    }
74    unsafe {
75        libc::dup2(fd, 0);
76        libc::dup2(fd, 1);
77        libc::dup2(fd, 2);
78        if fd > 2 { libc::close(fd); }
79    }
80    Ok(())
81}
82
83/// Set the signal sent to this process when its parent dies (`PR_SET_PDEATHSIG`).
84pub fn set_pdeathsig(sig: i32) -> Result<(), CoreError> {
85    syscall_ret(
86        unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, sig as libc::c_ulong, 0, 0, 0) },
87        "prctl:PR_SET_PDEATHSIG",
88    )
89}
90
91/// Drop process privileges to the given UID (`setresuid`).
92///
93/// Sets real, effective, and saved UID to `uid`.
94pub fn setuid(uid: u32) -> Result<(), CoreError> {
95    syscall_ret(
96        unsafe { libc::setresuid(uid, uid, uid) },
97        "setresuid",
98    )
99}
100
101/// Drop process privileges to the given GID (`setresgid`).
102///
103/// Sets real, effective, and saved GID to `gid`.
104pub fn setgid(gid: u32) -> Result<(), CoreError> {
105    syscall_ret(
106        unsafe { libc::setresgid(gid, gid, gid) },
107        "setresgid",
108    )
109}
110
111/// Close all file descriptors >= `start`.
112///
113/// Silently ignores `EBADF` (already closed or never open).
114pub fn close_fds_from(start: i32) {
115    for fd in start..1024 {
116        unsafe { libc::close(fd) };
117    }
118}