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 {
#[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,
}
};

View file

@ -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<OsStr>]),
}

View file

@ -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<File>, 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<File>, 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);
}
}