115 lines
2.8 KiB
Rust
115 lines
2.8 KiB
Rust
use std::env;
|
|
|
|
use jobs::Job;
|
|
|
|
use crate::prelude::*;
|
|
|
|
pub mod jobs;
|
|
pub mod logic;
|
|
pub mod exec_ctx;
|
|
pub mod meta;
|
|
pub mod shenv;
|
|
pub mod vars;
|
|
pub mod input;
|
|
|
|
/// Calls attach_tty() on the shell's process group to retake control of the terminal
|
|
pub fn take_term() -> ShResult<()> {
|
|
attach_tty(getpgrp())?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn disable_reaping() -> ShResult<()> {
|
|
log!(TRACE, "Disabling reaping");
|
|
unsafe { signal(Signal::SIGCHLD, SigHandler::Handler(crate::signal::ignore_sigchld)) }?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Waits on the current foreground job and updates the shell's last status code
|
|
pub fn wait_fg(job: Job, shenv: &mut ShEnv) -> ShResult<()> {
|
|
log!(TRACE, "Waiting on foreground job");
|
|
let mut code = 0;
|
|
attach_tty(job.pgid())?;
|
|
disable_reaping()?;
|
|
let statuses = write_jobs(|j| j.new_fg(job))?;
|
|
for status in statuses {
|
|
match status {
|
|
WtStat::Exited(_, exit_code) => {
|
|
code = exit_code;
|
|
}
|
|
WtStat::Stopped(pid, sig) => {
|
|
write_jobs(|j| j.fg_to_bg(status))?;
|
|
code = sys::SIG_EXIT_OFFSET + sig as i32;
|
|
},
|
|
WtStat::Signaled(pid, sig, _) => {
|
|
if sig == Signal::SIGTSTP {
|
|
write_jobs(|j| j.fg_to_bg(status))?;
|
|
}
|
|
code = sys::SIG_EXIT_OFFSET + sig as i32;
|
|
},
|
|
_ => { /* Do nothing */ }
|
|
}
|
|
}
|
|
take_term()?;
|
|
shenv.set_code(code);
|
|
log!(TRACE, "exit code: {}", code);
|
|
enable_reaping()?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn log_level() -> crate::libsh::utils::LogLevel {
|
|
let level = env::var("FERN_LOG_LEVEL").unwrap_or_default();
|
|
match level.to_lowercase().as_str() {
|
|
"error" => ERROR,
|
|
"warn" => WARN,
|
|
"info" => INFO,
|
|
"debug" => DEBUG,
|
|
"trace" => TRACE,
|
|
_ => NULL
|
|
}
|
|
}
|
|
|
|
pub fn enable_reaping() -> ShResult<()> {
|
|
log!(TRACE, "Enabling reaping");
|
|
unsafe { signal(Signal::SIGCHLD, SigHandler::Handler(crate::signal::handle_sigchld)) }.unwrap();
|
|
Ok(())
|
|
}
|
|
|
|
pub fn attach_tty(pgid: Pid) -> ShResult<()> {
|
|
// If we aren't attached to a terminal, the pgid already controls it, or the process group does not exist
|
|
// Then return ok
|
|
if !isatty(0).unwrap_or(false) || pgid == term_ctlr() || killpg(pgid, None).is_err() {
|
|
return Ok(())
|
|
}
|
|
log!(DEBUG, "Attaching tty to pgid: {}",pgid);
|
|
|
|
if pgid == getpgrp() && term_ctlr() != getpgrp() {
|
|
kill(term_ctlr(), Signal::SIGTTOU).ok();
|
|
}
|
|
|
|
let mut new_mask = SigSet::empty();
|
|
let mut mask_bkup = SigSet::empty();
|
|
|
|
new_mask.add(Signal::SIGTSTP);
|
|
new_mask.add(Signal::SIGTTIN);
|
|
new_mask.add(Signal::SIGTTOU);
|
|
|
|
pthread_sigmask(SigmaskHow::SIG_BLOCK, Some(&mut new_mask), Some(&mut mask_bkup))?;
|
|
|
|
let result = unsafe { tcsetpgrp(borrow_fd(0), pgid) };
|
|
|
|
pthread_sigmask(SigmaskHow::SIG_SETMASK, Some(&mut mask_bkup), Some(&mut new_mask))?;
|
|
|
|
match result {
|
|
Ok(_) => return Ok(()),
|
|
Err(e) => {
|
|
log!(ERROR, "error while switching term control: {}",e);
|
|
unsafe { tcsetpgrp(borrow_fd(0), getpgrp())? };
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn term_ctlr() -> Pid {
|
|
unsafe { tcgetpgrp(borrow_fd(0)).unwrap_or(getpgrp()) }
|
|
}
|