Files
shed/src/shellenv/mod.rs

127 lines
3.0 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;
pub mod shopt;
/// 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 dispatch_job(job: Job, is_bg: bool, shenv: &mut ShEnv) -> ShResult<()> {
if is_bg {
write_jobs(|j| {
j.insert_job(job, false)
})?;
} else {
wait_fg(job, shenv)?;
}
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!(TRACE, "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()) }
}