1use crate::env::builtin::{BuiltinEnvironment, BuiltinUtility};
2use crate::env::{
3 AsyncIoEnvironment, EnvRestorer, ExecutableData, ExecutableEnvironment,
4 ExportedVariableEnvironment, FileDescEnvironment, FileDescOpener, FunctionEnvironment,
5 FunctionFrameEnvironment, RedirectEnvRestorer, SetArgumentsEnvironment,
6 UnsetVariableEnvironment, VarEnvRestorer, WorkingDirectoryEnvironment,
7};
8use crate::error::{CommandError, RedirectionError};
9use crate::eval::{
10 eval_redirects_or_cmd_words_with_restorer, eval_redirects_or_var_assignments_with_restorer,
11 EvalRedirectOrCmdWordError, EvalRedirectOrVarAssigError, RedirectEval, RedirectOrCmdWord,
12 RedirectOrVarAssig, WordEval,
13};
14use crate::io::FileDescWrapper;
15use crate::spawn::{function_body, Spawn};
16use crate::{
17 ExitStatus, EXIT_CMD_NOT_EXECUTABLE, EXIT_CMD_NOT_FOUND, EXIT_ERROR, EXIT_SUCCESS,
18 STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO,
19};
20use futures_core::future::BoxFuture;
21use std::borrow::Borrow;
22use std::collections::VecDeque;
23use std::error::Error;
24use std::ffi::OsStr;
25
26pub async fn simple_command<'a, R, V, W, IV, IW, S, E>(
29 vars: IV,
30 words: IW,
31 env: &'a mut E,
32) -> Result<BoxFuture<'static, ExitStatus>, S::Error>
33where
34 IV: Iterator<Item = RedirectOrVarAssig<R, V, W>>,
35 IW: Iterator<Item = RedirectOrCmdWord<R, W>>,
36 R: RedirectEval<E, Handle = E::FileHandle>,
37 R::Error: 'static + Error + From<RedirectionError>,
38 W: WordEval<E>,
39 W::Error: 'static + Error,
40 E: ?Sized
41 + Send
42 + Sync
43 + AsyncIoEnvironment
44 + BuiltinEnvironment<BuiltinName = <E as FunctionEnvironment>::FnName>
45 + ExecutableEnvironment
46 + ExportedVariableEnvironment
47 + FileDescEnvironment
48 + FileDescOpener
49 + FunctionEnvironment<Fn = S>
50 + FunctionFrameEnvironment
51 + SetArgumentsEnvironment
52 + UnsetVariableEnvironment
53 + WorkingDirectoryEnvironment,
54 E::Builtin: BuiltinUtility<'a, Vec<W::EvalResult>, EnvRestorer<'a, E>, E>,
55 E::Arg: From<W::EvalResult>,
56 E::Args: From<VecDeque<E::Arg>>,
57 E::FileHandle: Send + Sync + Clone + FileDescWrapper + From<E::OpenedFileHandle>,
58 E::FnName: From<W::EvalResult>,
59 E::IoHandle: Send + Sync + From<E::FileHandle>,
60 E::VarName: Send + Sync + Clone + Borrow<String> + From<V>,
61 E::Var: Send + Sync + Clone + Borrow<String> + From<W::EvalResult>,
62 S: Spawn<E> + Clone,
63 S::Error: From<R::Error> + From<W::Error> + From<CommandError> + From<RedirectionError>,
64{
65 simple_command_with_restorer(vars, words, &mut EnvRestorer::new(env)).await
66}
67
68pub async fn simple_command_with_restorer<'a, R, V, W, IV, IW, RR, S, E>(
71 vars: IV,
72 words: IW,
73 restorer: &mut RR,
74) -> Result<BoxFuture<'static, ExitStatus>, S::Error>
75where
76 IV: Iterator<Item = RedirectOrVarAssig<R, V, W>>,
77 IW: Iterator<Item = RedirectOrCmdWord<R, W>>,
78 R: RedirectEval<E, Handle = E::FileHandle>,
79 R::Error: 'static + Error + From<RedirectionError>,
80 W: WordEval<E>,
81 W::Error: 'static + Error,
82 RR: ?Sized
83 + Send
84 + Sync
85 + AsyncIoEnvironment
86 + FileDescOpener
87 + ExportedVariableEnvironment
88 + RedirectEnvRestorer<'a, E>
89 + VarEnvRestorer<'a, E>,
90 RR::FileHandle: From<RR::OpenedFileHandle>,
91 RR::IoHandle: Send + From<RR::FileHandle>,
92 E: 'a
93 + ?Sized
94 + Send
95 + Sync
96 + BuiltinEnvironment<BuiltinName = <E as FunctionEnvironment>::FnName>
97 + ExecutableEnvironment
98 + ExportedVariableEnvironment
99 + FileDescEnvironment
100 + FunctionEnvironment<Fn = S>
101 + FunctionFrameEnvironment
102 + SetArgumentsEnvironment
103 + WorkingDirectoryEnvironment,
104 E::Builtin: BuiltinUtility<'a, Vec<W::EvalResult>, RR, E>,
105 E::Arg: From<W::EvalResult>,
106 E::Args: From<VecDeque<E::Arg>>,
107 E::FileHandle: Clone + FileDescWrapper,
108 E::FnName: From<W::EvalResult>,
109 E::VarName: Borrow<String> + From<V>,
110 E::Var: Borrow<String> + From<W::EvalResult>,
111 S: Spawn<E> + Clone,
112 S::Error: From<R::Error> + From<W::Error> + From<CommandError> + From<RedirectionError>,
113{
114 let ret = do_simple_command_with_restorer(vars, words, restorer).await;
115 restorer.restore_vars();
116 restorer.restore_redirects();
117 ret
118}
119
120async fn do_simple_command_with_restorer<'a, R, V, W, IV, IW, RR, S, E>(
121 vars: IV,
122 mut words: IW,
123 restorer: &mut RR,
124) -> Result<BoxFuture<'static, ExitStatus>, S::Error>
125where
126 IV: Iterator<Item = RedirectOrVarAssig<R, V, W>>,
127 IW: Iterator<Item = RedirectOrCmdWord<R, W>>,
128 R: RedirectEval<E, Handle = E::FileHandle>,
129 R::Error: 'static + Error + From<RedirectionError>,
130 W: WordEval<E>,
131 W::Error: 'static + Error,
132 RR: ?Sized
133 + Send
134 + Sync
135 + AsyncIoEnvironment
136 + FileDescOpener
137 + ExportedVariableEnvironment
138 + RedirectEnvRestorer<'a, E>
139 + VarEnvRestorer<'a, E>,
140 RR::FileHandle: From<RR::OpenedFileHandle>,
141 RR::IoHandle: Send + From<RR::FileHandle>,
142 E: 'a
143 + ?Sized
144 + Send
145 + Sync
146 + BuiltinEnvironment<BuiltinName = <E as FunctionEnvironment>::FnName>
147 + ExecutableEnvironment
148 + ExportedVariableEnvironment
149 + FileDescEnvironment
150 + FunctionEnvironment<Fn = S>
151 + FunctionFrameEnvironment
152 + SetArgumentsEnvironment
153 + WorkingDirectoryEnvironment,
154 E::Builtin: BuiltinUtility<'a, Vec<W::EvalResult>, RR, E>,
155 E::Arg: From<W::EvalResult>,
156 E::Args: From<VecDeque<E::Arg>>,
157 E::FileHandle: Clone + FileDescWrapper,
158 E::FnName: From<W::EvalResult>,
159 E::VarName: Borrow<String> + From<V>,
160 E::Var: Borrow<String> + From<W::EvalResult>,
161 S: Spawn<E> + Clone,
162 S::Error: From<R::Error> + From<W::Error> + From<CommandError> + From<RedirectionError>,
163{
164 let mut other_redirects = Vec::new();
166 let mut first_word = None;
167
168 while let Some(w) = words.next() {
169 match w {
170 w @ RedirectOrCmdWord::CmdWord(_) => {
171 first_word = Some(w);
172 break;
173 }
174 RedirectOrCmdWord::Redirect(r) => {
175 other_redirects.push(RedirectOrVarAssig::Redirect(r));
176 }
177 }
178 }
179
180 let export_vars = first_word.as_ref().map(|_| true);
185
186 let vars = vars.chain(other_redirects.into_iter());
187 let words = first_word.into_iter().chain(words);
188
189 eval_redirects_or_var_assignments_with_restorer(export_vars, vars, restorer)
190 .await
191 .map_err(|e| match e {
192 EvalRedirectOrVarAssigError::Redirect(e) => S::Error::from(e),
193 EvalRedirectOrVarAssigError::VarAssig(e) => S::Error::from(e),
194 })?;
195
196 let mut words = eval_redirects_or_cmd_words_with_restorer(restorer, words)
197 .await
198 .map_err(|e| match e {
199 EvalRedirectOrCmdWordError::Redirect(e) => S::Error::from(e),
200 EvalRedirectOrCmdWordError::CmdWord(e) => S::Error::from(e),
201 })?;
202
203 let cmd_name = if words.is_empty() {
204 restorer.clear_vars();
208 return Ok(Box::pin(async { EXIT_SUCCESS }));
209 } else {
210 words.remove(0)
211 };
212
213 {
214 let cmd_name = cmd_name.clone().into();
215 let env = restorer.get_mut();
216
217 if let Some(func) = env.function(&cmd_name).cloned() {
218 let args = words.into_iter().map(Into::into).collect();
219 return Ok(function_body(func, args, env).await?);
220 } else if let Some(builtin) = env.builtin(&cmd_name) {
221 return Ok(builtin.spawn_builtin(words, restorer).await);
222 }
223 }
224
225 let (stdin, stdout, stderr) = {
227 let env = restorer.get();
228 (
229 env.file_desc(STDIN_FILENO).map(|(fdes, _)| fdes).cloned(),
230 env.file_desc(STDOUT_FILENO).map(|(fdes, _)| fdes).cloned(),
231 env.file_desc(STDERR_FILENO).map(|(fdes, _)| fdes).cloned(),
232 )
233 };
234
235 restorer.restore_redirects();
238
239 let get_io = move |fd, fdes: Option<E::FileHandle>| match fdes {
244 None => Ok(None),
245 Some(fdes_wrapper) => match fdes_wrapper.try_unwrap() {
246 Ok(fdes) => Ok(Some(fdes)),
247 Err(err) => {
248 let msg = format!("file descriptor {}", fd);
249 Err(RedirectionError::Io(err, Some(msg)))
250 }
251 },
252 };
253
254 let env = restorer.get();
255 let args = words
256 .iter()
257 .map(|a| OsStr::new(a.borrow()))
258 .collect::<Vec<_>>();
259 let env_vars = env
260 .env_vars()
261 .iter()
262 .map(|&(ref key, ref val)| {
263 let key = OsStr::new((*key).borrow());
264 let val = OsStr::new((*val).borrow());
265 (key, val)
266 })
267 .collect::<Vec<_>>();
268
269 let cur_dir = env.current_working_dir().to_path_buf();
270
271 let data = ExecutableData {
272 name: OsStr::new(cmd_name.borrow()),
273 args: &args,
274 env_vars: &env_vars,
275 current_dir: &cur_dir,
276 stdin: get_io(STDIN_FILENO, stdin)?,
277 stdout: get_io(STDOUT_FILENO, stdout)?,
278 stderr: get_io(STDERR_FILENO, stderr)?,
279 };
280
281 let child = env.spawn_executable(data);
282
283 restorer.restore_vars();
286
287 match child {
288 Ok(ret) => Ok(ret),
289 Err(e) => {
290 if let Some(e) = find_root_cause(&e).downcast_ref::<CommandError>() {
291 let status = match e {
292 CommandError::NotExecutable(_) => EXIT_CMD_NOT_EXECUTABLE,
293 CommandError::NotFound(_) => EXIT_CMD_NOT_FOUND,
294 CommandError::Io(_, _) => EXIT_ERROR,
295 };
296
297 Ok(Box::pin(async move { status }))
298 } else {
299 Err(S::Error::from(e))
300 }
301 }
302 }
303}
304
305fn find_root_cause<'a>(mut err: &'a (dyn Error + 'static)) -> &'a (dyn Error + 'static) {
306 while let Some(e) = err.source() {
307 err = e;
308 }
309
310 err
311}