feat(builtins): add nofork on builtins

Signed-off-by: Charlotte Meyer <dev@buffet.sh>
This commit is contained in:
buffet 2022-10-26 14:04:36 +00:00
parent a5d971de35
commit c3bcdf5cd8
3 changed files with 45 additions and 22 deletions

View file

@ -9,6 +9,8 @@ use syn::{parse_macro_input, AttributeArgs, Ident, ItemFn, Signature};
struct AttrArgs { struct AttrArgs {
#[darling(default)] #[darling(default)]
description: String, description: String,
#[darling(default)]
nofork: bool,
} }
#[proc_macro_attribute] #[proc_macro_attribute]
@ -25,7 +27,10 @@ pub fn builtin(attr: TokenStream, item: TokenStream) -> TokenStream {
} = sig; } = sig;
let name = ident.to_string(); 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, Ok(args) => args,
Err(err) => return err.write_errors().into(), Err(err) => return err.write_errors().into(),
}; };
@ -46,6 +51,7 @@ pub fn builtin(attr: TokenStream, item: TokenStream) -> TokenStream {
#builtin { #builtin {
name: #name, name: #name,
description: #description, description: #description,
nofork: #nofork,
fun: #ident, fun: #ident,
} }
}; };

View file

@ -8,6 +8,7 @@ use crate::Shell;
pub struct Builtin { pub struct Builtin {
pub name: &'static str, pub name: &'static str,
pub description: &'static str, pub description: &'static str,
pub nofork: bool,
pub fun: fn(shell: &mut Shell, args: &[Cow<OsStr>]), pub fun: fn(shell: &mut Shell, args: &[Cow<OsStr>]),
} }

View file

@ -104,7 +104,7 @@ impl<'a> PreparedCommand<'a> {
} }
}; };
let child = match self.kind { match self.kind {
CommandKind::External => { CommandKind::External => {
let mut cmd = Command::new(&self.cmd); let mut cmd = Command::new(&self.cmd);
cmd.args(self.args); cmd.args(self.args);
@ -131,21 +131,30 @@ 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) {
CommandKind::Builtin(builtin) => { *pgid = child;
match unsafe { unistd::fork() }.map_err(RuntimeError::ForkFailed)? {
ForkResult::Parent { child } => child,
ForkResult::Child => {
fn redirect(old: Option<File>, new: RawFd) {
if let Some(old) = old {
let _ = unistd::dup2(old.as_raw_fd(), new);
}
} }
redirect(self.stdin, libc::STDIN_FILENO); // prevent race conditions
redirect(self.stdout, libc::STDOUT_FILENO); let _ = unistd::setpgid(child, *pgid);
redirect(self.stderr, libc::STDERR_FILENO); }
CommandKind::Builtin(builtin) => {
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();
let _ = pre_exec(); let _ = pre_exec();
(builtin.fun)(shell, &self.args); (builtin.fun)(shell, &self.args);
@ -153,16 +162,23 @@ impl<'a> PreparedCommand<'a> {
} }
} }
} }
}
}; };
if *pgid == Pid::from_raw(0) { Ok(())
*pgid = child;
} }
// prevent race conditions /// Redirect stdio.
let _ = unistd::setpgid(child, *pgid); fn do_redirects(&self) {
fn redirect(old: &Option<File>, new: RawFd) {
if let Some(old) = old {
let _ = unistd::dup2(old.as_raw_fd(), new);
}
}
Ok(()) redirect(&self.stdin, libc::STDIN_FILENO);
redirect(&self.stdout, libc::STDOUT_FILENO);
redirect(&self.stderr, libc::STDERR_FILENO);
} }
} }