diff --git a/crates/oyster_builtin_proc/src/lib.rs b/crates/oyster_builtin_proc/src/lib.rs index 8c403ed..75a41ac 100644 --- a/crates/oyster_builtin_proc/src/lib.rs +++ b/crates/oyster_builtin_proc/src/lib.rs @@ -9,6 +9,8 @@ use syn::{parse_macro_input, AttributeArgs, Ident, ItemFn, Signature}; struct AttrArgs { #[darling(default)] description: String, + #[darling(default)] + nofork: bool, } #[proc_macro_attribute] @@ -25,7 +27,10 @@ pub fn builtin(attr: TokenStream, item: TokenStream) -> TokenStream { } = sig; let name = ident.to_string(); - let AttrArgs { description } = match AttrArgs::from_list(&attrs) { + let AttrArgs { + description, + nofork, + } = match AttrArgs::from_list(&attrs) { Ok(args) => args, Err(err) => return err.write_errors().into(), }; @@ -46,6 +51,7 @@ pub fn builtin(attr: TokenStream, item: TokenStream) -> TokenStream { #builtin { name: #name, description: #description, + nofork: #nofork, fun: #ident, } }; diff --git a/crates/oyster_runtime/src/builtins/mod.rs b/crates/oyster_runtime/src/builtins/mod.rs index a831302..c8fec4e 100644 --- a/crates/oyster_runtime/src/builtins/mod.rs +++ b/crates/oyster_runtime/src/builtins/mod.rs @@ -8,6 +8,7 @@ use crate::Shell; pub struct Builtin { pub name: &'static str, pub description: &'static str, + pub nofork: bool, pub fun: fn(shell: &mut Shell, args: &[Cow]), } diff --git a/crates/oyster_runtime/src/pipeline.rs b/crates/oyster_runtime/src/pipeline.rs index ead050a..4cfa54b 100644 --- a/crates/oyster_runtime/src/pipeline.rs +++ b/crates/oyster_runtime/src/pipeline.rs @@ -104,7 +104,7 @@ impl<'a> PreparedCommand<'a> { } }; - let child = match self.kind { + match self.kind { CommandKind::External => { let mut cmd = Command::new(&self.cmd); cmd.args(self.args); @@ -131,38 +131,54 @@ impl<'a> PreparedCommand<'a> { } }; - Pid::from_raw(child.id() as i32) + let child = Pid::from_raw(child.id() as i32); + if *pgid == Pid::from_raw(0) { + *pgid = child; + } + + // prevent race conditions + let _ = unistd::setpgid(child, *pgid); } CommandKind::Builtin(builtin) => { - match unsafe { unistd::fork() }.map_err(RuntimeError::ForkFailed)? { - ForkResult::Parent { child } => child, - ForkResult::Child => { - fn redirect(old: Option, new: RawFd) { - if let Some(old) = old { - let _ = unistd::dup2(old.as_raw_fd(), new); + if builtin.nofork { + self.do_redirects(); + (builtin.fun)(shell, &self.args); + } else { + match unsafe { unistd::fork() }.map_err(RuntimeError::ForkFailed)? { + ForkResult::Parent { child } => { + if *pgid == Pid::from_raw(0) { + *pgid = child; } + + // prevent race conditions + let _ = unistd::setpgid(child, *pgid); } + ForkResult::Child => { + self.do_redirects(); - redirect(self.stdin, libc::STDIN_FILENO); - redirect(self.stdout, libc::STDOUT_FILENO); - redirect(self.stderr, libc::STDERR_FILENO); - - let _ = pre_exec(); - (builtin.fun)(shell, &self.args); - process::exit(0); + let _ = pre_exec(); + (builtin.fun)(shell, &self.args); + process::exit(0); + } } } } }; - if *pgid == Pid::from_raw(0) { - *pgid = child; + Ok(()) + } + + /// Redirect stdio. + fn do_redirects(&self) { + fn redirect(old: &Option, new: RawFd) { + if let Some(old) = old { + let _ = unistd::dup2(old.as_raw_fd(), new); + } } - // prevent race conditions - let _ = unistd::setpgid(child, *pgid); - - Ok(()) + redirect(&self.stdin, libc::STDIN_FILENO); + redirect(&self.stdout, libc::STDOUT_FILENO); + redirect(&self.stderr, libc::STDERR_FILENO); } }