unix_exec_output_catcher/
child.rs1use crate::error::UECOError;
4use crate::exec::exec;
5use crate::libc_util::{libc_ret_to_result, LibcSyscall};
6use crate::pipe::Pipe;
7use std::fmt::Debug;
8use std::sync::{Arc, Mutex};
9
10#[derive(Debug, PartialEq, Copy, Clone)]
12pub enum ProcessState {
13 Ready,
15 Running,
17 FinishedSuccess,
19 FinishedError(i32),
21}
22
23pub struct ChildProcess {
25 executable: String,
28 args: Vec<String>,
31 pid: Option<libc::pid_t>,
34 exit_code: Option<i32>,
36 state: ProcessState,
38 stdout_pipe: Arc<Mutex<Pipe>>,
40 stderr_pipe: Arc<Mutex<Pipe>>,
42 child_after_dispatch_before_exec_fn: Box<dyn Send + FnMut() -> Result<(), UECOError>>,
44 parent_after_dispatch_fn: Box<dyn Send + FnMut() -> Result<(), UECOError>>,
46}
47
48impl ChildProcess {
49 pub fn new(
57 executable: &str,
58 args: Vec<&str>,
59 child_after_dispatch_before_exec_fn: Box<dyn Send + FnMut() -> Result<(), UECOError>>,
60 parent_after_dispatch_fn: Box<dyn Send + FnMut() -> Result<(), UECOError>>,
61 stdout_pipe: Arc<Mutex<Pipe>>,
62 stderr_pipe: Arc<Mutex<Pipe>>,
63 ) -> Self {
64 ChildProcess {
65 executable: executable.to_string(),
66 args: args.iter().map(|s| s.to_string()).collect::<Vec<String>>(),
67 pid: None,
68 exit_code: None,
69 state: ProcessState::Ready,
70 child_after_dispatch_before_exec_fn,
71 parent_after_dispatch_fn,
72 stdout_pipe,
73 stderr_pipe,
74 }
75 }
76
77 pub fn dispatch(&mut self) -> Result<libc::pid_t, UECOError> {
80 self.state = ProcessState::Running;
81 let pid = unsafe { libc::fork() };
82 libc_ret_to_result(pid, LibcSyscall::Fork)?;
84
85 trace!("forked successfully");
86
87 if pid == 0 {
88 trace!("Hello from Child!");
90 let res: Result<(), UECOError> = (self.child_after_dispatch_before_exec_fn)();
91 res?;
92 exec(
93 &self.executable,
94 self.args.iter().map(|s| s.as_str()).collect::<Vec<&str>>(),
95 )?;
96 Err(UECOError::Unknown)
100 } else {
101 trace!("Hello from parent!");
103 self.pid.replace(pid);
104 let res: Result<(), UECOError> = (self.parent_after_dispatch_fn)();
105 res?;
106 Ok(pid)
107 }
108 }
109
110 pub fn check_state_nbl(&mut self) -> ProcessState {
112 if self.state != ProcessState::Running {
113 return self.state;
114 }
115
116 let wait_flags = libc::WNOHANG;
117 let mut status_code: libc::c_int = 0;
118 let status_code_ptr = &mut status_code as *mut libc::c_int;
119
120 let ret = unsafe { libc::waitpid(self.pid.unwrap(), status_code_ptr, wait_flags) };
121 libc_ret_to_result(ret, LibcSyscall::Waitpid).unwrap();
122
123 if ret == 0 {
132 trace!("Child process not started yet");
133 return self.state; } else if ret == self.pid.unwrap() {
135 trace!("Child process started");
136 }
137
138 let exited_normally: bool = libc::WIFEXITED(status_code);
140 let exited_by_signal: bool = libc::WIFSIGNALED(status_code);
142 let exit_code: libc::c_int = libc::WEXITSTATUS(status_code);
144
145 if exited_normally || exited_by_signal {
146 self.exit_code.replace(exit_code);
147 if exit_code == 0 {
148 self.state = ProcessState::FinishedSuccess;
149 } else {
150 self.state = ProcessState::FinishedError(exit_code);
151 }
152 }
153
154 self.state
155 }
156
157 pub fn exit_code(&self) -> Option<i32> {
159 self.exit_code
160 }
161 pub fn stdout_pipe(&self) -> &Arc<Mutex<Pipe>> {
163 &self.stdout_pipe
164 }
165 pub fn stderr_pipe(&self) -> &Arc<Mutex<Pipe>> {
167 &self.stderr_pipe
168 }
169}