1use libc::{
2 __errno_location, _exit, c_int, c_void, execl, fclose, fdopen, pipe, pipe2, read, socketpair,
3 AF_UNIX, FILE, O_NONBLOCK, SOCK_STREAM, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO,
4};
5use std::ffi::{CString, NulError};
6
7use crate::{
8 create_pipe, create_pipe2, dup::DupError, wait::Wait, Close, Dup, Fork, ForkPid,
9 SocketPairError,
10};
11
12#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
13pub struct Popen {
14 pub arg: String,
15 pub stdin: *mut FILE,
16 pub stdout: *mut FILE,
17 pub stderr: *mut FILE,
18 pub pid: Option<c_int>,
19}
20
21#[derive(Clone, Debug, Eq, PartialEq)]
22pub enum PopenError {
23 PipeCreateFailed,
24 ExecArgFailed(c_int),
25 ForkFailed,
26 PipeRedirectFailed(c_int),
27 Dup2Errno(DupError),
28 FdOpenErrno(c_int),
29 CloseError(Close),
30 SocketPairError(SocketPairError),
31 CreateRedirectError(c_int),
32 CStringParesError(NulError),
33}
34
35impl PopenError {
36 fn to_string(&self) -> String {
37 match self {
38 Self::PipeCreateFailed => "create pipe failed!".to_string(),
39 Self::ExecArgFailed(code) => std::format!("exec arg failed! exit code: {}", code),
40 Self::ForkFailed => "fork failed!".to_string(),
41 Self::PipeRedirectFailed(v) => {
42 std::format!("redirect std(in|out|err) to pipe failed! code: {}", v)
43 }
44 PopenError::Dup2Errno(v) => std::format!("dup2 old fd to new fd failed! {}", v),
45 Self::FdOpenErrno(v) => {
46 std::format!("open file descriptor as file* stream failed! errno: {}", v)
47 }
48 Self::CloseError(v) => {
49 std::format!("{}", v)
50 }
51 Self::SocketPairError(v) => {
52 std::format!("socket pair {}", v)
53 }
54 Self::CreateRedirectError(v) => {
55 std::format!(
56 "create socket pair and stderr pipe both failed! errno: {}",
57 v
58 )
59 }
60 Self::CStringParesError(n) => {
61 std::format!("parse {:<.20} failed!", n.to_string())
62 }
63 }
64 }
65}
66
67fn socket_pipe() -> Result<[[c_int; 2]; 2], PopenError> {
80 let mut sv = [0 as c_int; 2];
81 let mut fd = [0 as c_int; 2];
82 match unsafe {
83 (
84 socketpair(AF_UNIX, SOCK_STREAM, 0, sv.as_mut_ptr()),
85 pipe2(fd.as_mut_ptr(), O_NONBLOCK),
86 )
87 } {
88 (-1, 0) => Err(PopenError::SocketPairError(SocketPairError::SocketErrno(
89 unsafe { *__errno_location() },
90 ))),
91 (0, -1) => Err(PopenError::PipeCreateFailed),
92 (-1, -1) => Err(PopenError::CreateRedirectError(unsafe {
93 *__errno_location()
94 })),
95 (0, 0) => Ok([sv, fd]),
96 _ => panic!("this should reached!"),
97 }
98}
99
100#[deprecated(note = "do not use!")]
102impl Popen {
103 pub fn arg(arg: &str) -> Box<Popen> {
104 Box::new(Popen {
105 arg: String::from(arg),
106 stdin: 0 as *mut FILE,
107 stdout: 0 as *mut FILE,
108 stderr: 0 as *mut FILE,
109 pid: None,
110 })
111 }
112 pub fn exec(mut self: Box<Popen>) -> Result<Box<Popen>, PopenError> {
113 let [stdout, stdin] = create_pipe!(2).ok_or(PopenError::PipeCreateFailed)?;
115 let [stderr] = create_pipe2!(1, [O_NONBLOCK]).ok_or(PopenError::PipeCreateFailed)?;
116 match Fork::fork() {
117 ForkPid::Parent((_, children)) => {
118 println!("{}", self.as_ref() as *const Popen as *const i32 as i32);
119 self.pid = Some(children);
120 Close::close(&[stdin[0], stdout[1], stderr[1]])
121 .or_else(|x| Err(PopenError::CloseError(x)))?;
122 let r = CString::new("r").or_else(|x| Err(PopenError::CStringParesError(x)))?;
123 let w = CString::new("w").or_else(|x| Err(PopenError::CStringParesError(x)))?;
124 self.stdin = unsafe { fdopen(stdin[1], w.as_ptr()) };
125 self.stdout = unsafe { fdopen(stdout[0], r.as_ptr()) };
126 self.stderr = unsafe { fdopen(stderr[0], r.as_ptr()) };
127 Ok(self)
128 }
129 ForkPid::Children(_) => {
131 println!("{}", self.as_ref() as *const Popen as *const i32 as i32);
132 Dup::dup2s(
133 &[stdout[1], stderr[1], STDIN_FILENO],
134 &[STDOUT_FILENO, STDERR_FILENO, stdin[0]],
135 )
136 .unwrap();
137 Close::close(&[
138 stdout[0], stdout[1], stdin[0], stdin[1], stderr[0], stderr[1],
139 ])
140 .unwrap();
141 let path = CString::new("/bin/sh").unwrap();
142 let sh = CString::new("sh").unwrap();
143 let exec = CString::new("-c").unwrap();
144 let zsh = CString::new("zsh").unwrap();
145 let arg = CString::new(self.arg.clone()).unwrap();
146 unsafe {
147 _exit(execl(
148 path.as_ptr(),
149 sh.as_ptr(),
150 exec.as_ptr(),
151 zsh.as_ptr(),
152 exec.as_ptr(),
153 arg.as_ptr(),
154 0,
155 ))
156 };
157 }
158 ForkPid::None => Err(PopenError::ForkFailed),
159 }
160 }
161}
162
163impl Drop for Popen {
164 fn drop(&mut self) {
165 let null = std::ptr::null_mut::<FILE>();
166 for i in &[self.stdin, self.stdout, self.stderr][..] {
167 if *i != null {
168 unsafe { fclose(*i) };
169 }
170 }
171 if let Some(pid) = self.pid {
172 while {
174 match Wait::children_with(pid, 0) {
175 Err(Wait::WaitFailure(libc::EINTR)) => true,
176 _ => false,
177 }
178 } {}
179 } else {
180 eprintln!("pid is missed!");
181 }
182 }
183}
184
185#[cfg(test)]
186mod popen {
187 use libc::{
188 __errno_location, fclose, fgets, perror, socketpair, strlen, FILE, PF_UNIX, SOCK_CLOEXEC,
189 SOCK_DGRAM,
190 };
191
192 use crate::{popen::popen, Close, Popen, Wait};
193
194 #[test]
195 fn test_libc_popen() {
197 unsafe {
198 let stream = libc::popen(
200 "echo hello\0".as_ptr() as *mut i8,
201 "r\0".as_ptr() as *mut i8,
202 );
203 assert!(stream != std::ptr::null_mut::<libc::FILE>());
204 let mut buf: [libc::c_char; 4096] = [0; 4096];
205 while {
206 let ptr = libc::fgets(buf.as_mut_ptr(), 4096, stream);
207 ptr != std::ptr::null_mut::<libc::c_char>() && *ptr != libc::EOF as i8
208 } {
209 let len = libc::strlen(buf.as_ptr());
210 assert!(len != 0);
211 let str = "hello\0";
212 for i in 0..len - 1 {
213 let x = &str[i..i + 1];
214 assert!(x.as_bytes()[0] == buf[i] as u8);
215 }
216 assert!(libc::strcmp(buf.as_ptr(), "hello\0".as_ptr() as *const i8) > 0);
217 }
218 assert!(libc::fclose(stream) != -1);
219 }
220 }
221 #[test]
222 fn test_popen_date() {
223 Popen::arg("date").exec().unwrap();
224 }
225
226 #[test]
227 fn socketpair_redirect() {
228 unsafe {
229 let mut fd: [i32; 4096] = [0; 4096];
230 socketpair(PF_UNIX, SOCK_DGRAM, 0, fd.as_mut_ptr());
231 Close::close(&[fd[0]]).unwrap();
232 }
233 }
234
235 #[test]
236 fn test_out_err() {
237 unsafe {
238 let popen = Popen::arg("time").exec().unwrap();
239 let mut buf = [0 as u8; 4096];
240 let mut p;
241 while {
242 p = fgets(buf.as_mut_ptr() as *mut i8, 4096, popen.stdout);
243 p != std::ptr::null_mut::<i8>() && *p != '\0' as i8
244 } {
245 assert!(strlen(p) != 0);
246 }
247 }
248 }
249
250 #[test]
251 fn test_write_out() {
252 let o = Popen::arg(
253 r#"
254while true;
255do
256 cat /proc/stat;
257 sleep 1;
258done;
259"#,
260 )
261 .exec()
262 .unwrap();
263 let mut buf: [i8; 4096] = [0 as i8; 4096];
264 let mut p;
265 while unsafe {
266 p = fgets(buf.as_mut_ptr(), 4096, o.stderr);
267 p != std::ptr::null_mut::<i8>()
268 } {
269 assert!(unsafe { strlen(p) } != 0);
270 }
271 }
272
273 #[test]
274 fn tty_shell() {}
275}