1pub use crate::data::{CmdChain, BasicCmd, CmdChainBuilder, BasicCmdBuilder, Builder, ProcessState};
26pub use crate::pipe::Pipe;
28
29mod libc_util;
30mod data;
31mod pipe;
32
33
34pub fn execute_piped_cmd_chain(cmds: &CmdChain) -> Vec<ProcessState> {
37 let mut pids: Vec<libc::pid_t> = vec![];
38
39 let mut pipe_to_current: Option<Pipe> = Option::None;
40 let mut pipe_to_next: Option<Pipe> = Option::None;
41 for i in 0..cmds.length() {
42 let cmd = &cmds.cmds()[i];
43
44 if pipe_to_next.is_some() {
45 pipe_to_current.replace(
46 pipe_to_next.take().unwrap()
47 );
48 }
49
50 if !cmd.is_last() {
51 pipe_to_next.replace(Pipe::new());
52 }
53
54 let pid = unsafe { libc::fork() };
55 if pid == -1 {
56 panic!("Fork failed! {}", errno::errno());
57 }
58
59 if pid > 0 {
61 pids.push(pid);
62
63 if pipe_to_current.is_some() {
65 pipe_to_current.as_mut().unwrap().parent_close_all();
66 }
67 }
68 else {
70 if cmd.is_first() && cmd.in_red_path().is_some() {
72 initial_ir(cmd);
73 }
74 if cmd.is_last() && cmd.out_red_path().is_some() {
76 final_or(cmd);
77 }
78
79 if pipe_to_current.is_some() {
80 pipe_to_current.as_mut().unwrap().as_read_end();
81 }
82 if pipe_to_next.is_some() {
83 pipe_to_next.as_mut().unwrap().as_write_end();
84 }
85
86 let _res = unsafe {
87 libc::execvp(
88 cmd.executable_cstring().as_ptr(),
89 cmd.args_to_c_argv()
90 )
91 };
92 panic!("Exec failed! {}", errno::errno());
93 }
94 }
95
96 let mut i = 0;
97 let mut process_states: Vec<ProcessState> = pids.into_iter()
98 .map(|pid| {
99 let executable = cmds.cmds()[i].executable().to_owned();
100 i += 1;
101 ProcessState::new(executable, pid)
102 })
103 .collect();
104
105 update_process_states(&mut process_states, cmds.background());
106
107 process_states
108}
109
110pub fn update_process_states(states: &mut Vec<ProcessState>, wnohang: bool) -> bool {
116 let wait_flags: libc::c_int = if wnohang { libc::WNOHANG } else { 0 };
118 let mut all_finished = true;
119
120 states.into_iter()
123 .filter(|state| !state.finished())
124 .for_each(|state| {
125 let mut status_code: libc::c_int = 0;
126 let status_code_ptr = &mut status_code as * mut libc::c_int;
127
128 let res = unsafe { libc::waitpid(state.pid(), status_code_ptr, wait_flags) };
129
130 let exited_normally: bool = unsafe { libc::WIFEXITED(status_code) };
133
134 if wait_flags == libc::WNOHANG && res == 0 {
135 all_finished = false;
136 } else if res == -1 {
138 panic!("Failure during waitpid! {}", errno::errno());
139 } else {
140 if !exited_normally {
141 eprintln!("Process did not exited normally! {:#?}", state);
142 }
143 let exit_code: libc::c_int = unsafe { libc::WEXITSTATUS(status_code) };
145
146 state.finish(exit_code);
147 println!("Process {} finished with status code {}", state.pid(), status_code);
148 }
149 });
150 all_finished
151}
152
153fn initial_ir(cmd: &BasicCmd) {
155 let fd = unsafe {
156 libc::open(
157 cmd.in_red_path_cstring().unwrap().as_ptr(),
158 libc::O_RDONLY,
159 )
160 };
161 if fd == -1 {
162 panic!("Input redirect path {} can't be opened/read! {}", cmd.in_red_path().as_ref().unwrap(), errno::errno());
163 }
164 let ret = unsafe { libc::dup2(fd, libc::STDIN_FILENO) };
165 if ret == -1 {
166 panic!("Error dup2() input redirect! {}", errno::errno());
167 }
168}
169
170fn final_or(cmd: &BasicCmd) {
172 let fd = unsafe {
173 let file = libc::fopen(
185 cmd.out_red_path_cstring().unwrap().as_ptr(),
186 "w".as_ptr() as * const i8
187 );
188 libc::fileno(file)
190 };
191 if fd == -1 {
192 panic!("Output redirect path {} can't be opened/written! {}", cmd.out_red_path().as_ref().unwrap(), errno::errno());
193 }
194 let ret = unsafe { libc::dup2(fd, libc::STDOUT_FILENO) };
195 if ret == -1 {
196 panic!("Error dup2() output redirect! {}", errno::errno());
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use crate::data::{CmdChainBuilder, BasicCmdBuilder, Builder};
203 use crate::execute_piped_cmd_chain;
204
205 #[test]
206 fn test_execute_chain() {
207 let cmd_chain = CmdChainBuilder::new()
210 .add_cmd(
211 BasicCmdBuilder::new()
212 .set_executable("echo")
213 .add_arg("echo")
214 .add_arg("Hallo\nAbc\n123\nAbc123")
215 ).add_cmd(
216 BasicCmdBuilder::new()
217 .set_executable("grep")
218 .add_arg("grep")
219 .add_arg("-i")
220 .add_arg("abc"))
221 .add_cmd(
222 BasicCmdBuilder::new()
223 .set_executable("wc")
224 .add_arg("wc")
225 .add_arg("-l")
226 ).build();
227
228 execute_piped_cmd_chain(&cmd_chain);
229 }
230}