feat(runtime): set foreground job properly
This commit is contained in:
parent
0bfa8b3c8f
commit
f0698457ed
5 changed files with 64 additions and 27 deletions
|
@ -5,7 +5,7 @@ use oyster_parser::ast::Code;
|
|||
use oyster_runtime::{Shell, Status};
|
||||
|
||||
fn main() {
|
||||
let mut shell = Shell::default();
|
||||
let mut shell = Shell::new().unwrap();
|
||||
let mut exit_code = Status::SUCCESS;
|
||||
|
||||
loop {
|
||||
|
|
|
@ -13,7 +13,7 @@ thiserror = "1.0.35"
|
|||
[dependencies.nix]
|
||||
version = "~0.25.0"
|
||||
default-features = false
|
||||
features = [ "fs", "ioctl", "process", "term" ]
|
||||
features = [ "fs", "ioctl", "process", "signal", "term" ]
|
||||
|
||||
[dev-dependencies]
|
||||
insta = "^1.15.0"
|
||||
|
|
|
@ -5,6 +5,10 @@ mod pipeline;
|
|||
|
||||
use std::io;
|
||||
|
||||
use nix::sys::{
|
||||
signal::{self, SaFlags, SigAction, SigHandler, Signal},
|
||||
signalfd::SigSet,
|
||||
};
|
||||
use oyster_parser::ast;
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -33,12 +37,25 @@ pub enum RuntimeError {
|
|||
|
||||
#[error("failed to spawn process: {0}")]
|
||||
SpawnFailed(#[source] io::Error),
|
||||
|
||||
#[error("waitpid error: {0}")]
|
||||
WaidPid(nix::Error),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Shell;
|
||||
|
||||
impl Shell {
|
||||
pub fn new() -> Result<Shell, nix::Error> {
|
||||
let ignore = SigAction::new(SigHandler::SigIgn, SaFlags::empty(), SigSet::empty());
|
||||
|
||||
unsafe {
|
||||
signal::sigaction(Signal::SIGTSTP, &ignore)?;
|
||||
signal::sigaction(Signal::SIGTTOU, &ignore)?;
|
||||
}
|
||||
|
||||
Ok(Shell)
|
||||
}
|
||||
|
||||
pub fn run<'a>(&mut self, code: &'a ast::Code) -> Result<Status, RuntimeError> {
|
||||
let mut last_status = Status::SUCCESS;
|
||||
|
||||
|
|
|
@ -3,16 +3,16 @@ use std::{
|
|||
ffi::{OsStr, OsString},
|
||||
fs::File,
|
||||
io,
|
||||
os::unix::{
|
||||
io::FromRawFd,
|
||||
process::{CommandExt, ExitStatusExt},
|
||||
},
|
||||
os::unix::{io::FromRawFd, process::CommandExt},
|
||||
process::{Child, Command, Stdio},
|
||||
slice::Iter,
|
||||
};
|
||||
|
||||
use nix::{
|
||||
errno::Errno,
|
||||
fcntl::OFlag,
|
||||
libc,
|
||||
sys::wait::{self, WaitPidFlag, WaitStatus},
|
||||
unistd::{self, Pid},
|
||||
};
|
||||
use oyster_parser::ast::{self, Redirect};
|
||||
|
@ -44,6 +44,15 @@ impl<'a> PreparedCommand<'a> {
|
|||
cmd.stderr(self.stderr.map_or(Stdio::inherit(), Stdio::from));
|
||||
cmd.process_group(pgid.as_raw());
|
||||
|
||||
if *pgid == Pid::from_raw(0) {
|
||||
unsafe {
|
||||
cmd.pre_exec(move || {
|
||||
let _ = unistd::tcsetpgrp(libc::STDIN_FILENO, unistd::getpid());
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let child = match cmd.spawn() {
|
||||
Ok(child) => child,
|
||||
Err(err) => {
|
||||
|
@ -92,7 +101,6 @@ impl Shell {
|
|||
&mut self,
|
||||
pipeline: &ast::Pipeline,
|
||||
) -> Result<Status, RuntimeError> {
|
||||
let mut children = Vec::with_capacity(pipeline.0.len());
|
||||
let mut cmds = pipeline.0.iter().map(PreparedCommand::from);
|
||||
let mut pgid = Pid::from_raw(0);
|
||||
|
||||
|
@ -106,28 +114,40 @@ impl Shell {
|
|||
Redirect::Stdout => last_cmd.stdout = Some(input),
|
||||
}
|
||||
|
||||
children.push(last_cmd.spawn(&mut pgid)?);
|
||||
last_cmd.spawn(&mut pgid)?;
|
||||
|
||||
last_cmd = cmd;
|
||||
}
|
||||
|
||||
children.push(last_cmd.spawn(&mut pgid)?);
|
||||
last_cmd.spawn(&mut pgid)?;
|
||||
|
||||
// TODO: kill children if error occured
|
||||
// TODO: set foreground group then wait for foreground group
|
||||
|
||||
let mut last_status = Status::SUCCESS;
|
||||
for mut c in children {
|
||||
// TODO: handle error
|
||||
let status = c.wait().unwrap();
|
||||
last_status = Status(
|
||||
status
|
||||
.code()
|
||||
.unwrap_or_else(|| Status::SIG_BASE.0 + status.signal().unwrap()),
|
||||
);
|
||||
let status = wait_pgid(pgid)?;
|
||||
let _ = unistd::tcsetpgrp(libc::STDIN_FILENO, unistd::getpgid(None).unwrap());
|
||||
|
||||
Ok(status)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(last_status)
|
||||
/// Wait for all processes of the specified job to terminate.
|
||||
fn wait_pgid(pgid: Pid) -> Result<Status, RuntimeError> {
|
||||
let pgid = Pid::from_raw(-pgid.as_raw());
|
||||
let mut last_status = Status::SUCCESS;
|
||||
|
||||
loop {
|
||||
match wait::waitpid(pgid, Some(WaitPidFlag::WUNTRACED)) {
|
||||
Ok(WaitStatus::Exited(_, code)) => last_status = Status(code),
|
||||
Ok(WaitStatus::Signaled(_, signal, _)) => {
|
||||
last_status = Status(Status::SIG_BASE.0 + signal as i32)
|
||||
}
|
||||
Ok(WaitStatus::Stopped(_, _)) => todo!("put job into background job list"),
|
||||
Ok(_) => (),
|
||||
Err(err) => match err {
|
||||
Errno::ECHILD => break Ok(last_status), // no more children
|
||||
_ => break Err(RuntimeError::WaidPid(err)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ fn simple_command() {
|
|||
};
|
||||
|
||||
let actual = collect_output(|| {
|
||||
let mut shell = Shell::default();
|
||||
let mut shell = Shell::new().unwrap();
|
||||
shell.run(&ast).unwrap();
|
||||
});
|
||||
|
||||
|
@ -107,7 +107,7 @@ fn pipeline() {
|
|||
};
|
||||
|
||||
let actual = collect_output(|| {
|
||||
let mut shell = Shell::default();
|
||||
let mut shell = Shell::new().unwrap();
|
||||
shell.run(&ast).unwrap();
|
||||
});
|
||||
|
||||
|
@ -126,7 +126,7 @@ fn command_not_found() {
|
|||
};
|
||||
|
||||
// XXX: this relies on the command actually not existing, as unsetting PATH is rather complex
|
||||
let mut shell = Shell::default();
|
||||
let mut shell = Shell::new().unwrap();
|
||||
let actual = shell.run(&ast);
|
||||
|
||||
assert_snapshot!(actual);
|
||||
|
@ -143,7 +143,7 @@ fn permission_denied() {
|
|||
)]))])
|
||||
};
|
||||
|
||||
let mut shell = Shell::default();
|
||||
let mut shell = Shell::new().unwrap();
|
||||
let actual = shell.run(&ast);
|
||||
|
||||
assert_snapshot!(actual);
|
||||
|
@ -164,7 +164,7 @@ fn multipart_word() {
|
|||
};
|
||||
|
||||
let actual = collect_output(|| {
|
||||
let mut shell = Shell::default();
|
||||
let mut shell = Shell::new().unwrap();
|
||||
shell.run(&ast).unwrap();
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue