1use std::ffi::CString;
8use std::os::fd::{IntoRawFd, RawFd};
9
10use nix::fcntl::OFlag;
11use nix::sys::stat::Mode;
12use nix::sys::wait::{self, WaitPidFlag, WaitStatus};
13use nix::unistd::{self, ForkResult, Pid};
14
15pub struct Pipe {
19 pub read: RawFd,
20 pub write: RawFd,
21}
22
23pub fn pipe() -> Result<Pipe, nix::errno::Errno> {
25 let (rd, wr) = unistd::pipe()?;
26 Ok(Pipe {
27 read: rd.into_raw_fd(),
28 write: wr.into_raw_fd(),
29 })
30}
31
32pub fn dup2(src: RawFd, dst: RawFd) -> Result<RawFd, nix::errno::Errno> {
36 unistd::dup2(src, dst)
37}
38
39pub fn close(fd: RawFd) -> Result<(), nix::errno::Errno> {
41 unistd::close(fd)
42}
43
44pub fn open(path: &std::ffi::CStr, flags: OFlag, mode: Mode) -> Result<RawFd, nix::errno::Errno> {
46 let owned = nix::fcntl::open(path, flags, mode)?;
47 Ok(owned.into_raw_fd())
48}
49
50pub fn dup2_and_close(src: RawFd, dst: RawFd) -> Result<(), nix::errno::Errno> {
52 if src != dst {
53 dup2(src, dst)?;
54 close(src)?;
55 }
56 Ok(())
57}
58
59pub enum ForkOutcome {
63 Child,
64 Parent { child_pid: Pid },
65}
66
67pub unsafe fn fork() -> Result<ForkOutcome, nix::errno::Errno> {
74 match unsafe { unistd::fork() }? {
75 ForkResult::Child => Ok(ForkOutcome::Child),
76 ForkResult::Parent { child } => Ok(ForkOutcome::Parent { child_pid: child }),
77 }
78}
79
80pub fn exec(argv: &[CString], envp: &[CString]) -> nix::errno::Errno {
93 let Some(cmd) = argv.first() else {
94 return nix::errno::Errno::ENOENT;
95 };
96
97 if cmd.as_bytes().contains(&b'/') {
98 match unistd::execve(cmd, argv, envp) {
100 Ok(infallible) => match infallible {},
101 Err(e) => e,
102 }
103 } else {
104 let path_val = envp
106 .iter()
107 .find_map(|entry| {
108 let bytes = entry.as_bytes();
109 bytes
110 .starts_with(b"PATH=")
111 .then(|| String::from_utf8_lossy(&bytes[5..]).into_owned())
112 })
113 .unwrap_or_else(|| "/usr/bin:/bin".into());
114
115 let cmd_str = cmd.to_string_lossy();
116 let mut last_err = nix::errno::Errno::ENOENT;
117
118 for dir in path_val.split(':') {
119 let Ok(full_path) = CString::new(format!("{dir}/{cmd_str}")) else {
120 continue;
121 };
122 match unistd::execve(&full_path, argv, envp) {
123 Ok(infallible) => match infallible {},
124 Err(nix::errno::Errno::ENOENT | nix::errno::Errno::EACCES) => continue,
125 Err(e) => {
126 last_err = e;
127 break;
128 }
129 }
130 }
131 last_err
132 }
133}
134
135pub enum ChildStatus {
139 Exited(i32),
141 Signaled(i32),
143 Stopped,
145 Running,
147}
148
149pub fn wait_pid(pid: Pid) -> Result<ChildStatus, nix::errno::Errno> {
151 match wait::waitpid(pid, None)? {
152 WaitStatus::Exited(_, code) => Ok(ChildStatus::Exited(code)),
153 WaitStatus::Signaled(_, sig, _) => Ok(ChildStatus::Signaled(128 + sig as i32)),
154 WaitStatus::Stopped(_, _) => Ok(ChildStatus::Stopped),
155 _ => Ok(ChildStatus::Exited(0)),
156 }
157}
158
159pub fn try_wait_pid(pid: Pid) -> Result<ChildStatus, nix::errno::Errno> {
161 match wait::waitpid(pid, Some(WaitPidFlag::WNOHANG | WaitPidFlag::WUNTRACED))? {
162 WaitStatus::Exited(_, code) => Ok(ChildStatus::Exited(code)),
163 WaitStatus::Signaled(_, sig, _) => Ok(ChildStatus::Signaled(128 + sig as i32)),
164 WaitStatus::Stopped(_, _) => Ok(ChildStatus::Stopped),
165 WaitStatus::StillAlive => Ok(ChildStatus::Running),
166 _ => Ok(ChildStatus::Running),
167 }
168}