implemented the trap builtin

This commit is contained in:
2026-02-19 16:39:18 -05:00
parent ff00affc90
commit 744693a89d
11 changed files with 340 additions and 129 deletions

View File

@@ -48,7 +48,7 @@ pub fn echo(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<(
flags.contains(EchoFlags::USE_PROMPT)
)?.join(" ");
if !flags.contains(EchoFlags::NO_NEWLINE) {
if !flags.contains(EchoFlags::NO_NEWLINE) && !echo_output.ends_with('\n') {
echo_output.push('\n')
}

View File

@@ -22,12 +22,13 @@ pub mod source;
pub mod test; // [[ ]] thing
pub mod read;
pub mod zoltraak;
pub mod trap;
pub const BUILTINS: [&str; 20] = [
pub const BUILTINS: [&str; 21] = [
"echo", "cd", "read", "export", "pwd", "source",
"shift", "jobs", "fg", "bg", "alias", "unalias",
"return", "break", "continue", "exit", "zoltraak",
"shopt", "builtin", "command",
"shopt", "builtin", "command", "trap"
];
/// Sets up a builtin command

View File

@@ -155,6 +155,7 @@ pub fn read_builtin(node: Node, _io_stack: &mut IoStack, job: &mut JobBldr) -> S
}
}
state::set_status(0);
Ok(())
}

162
src/builtin/trap.rs Normal file
View File

@@ -0,0 +1,162 @@
use std::{fmt::Display, str::FromStr};
use nix::{libc::{STDERR_FILENO, STDOUT_FILENO}, sys::signal::Signal, unistd::write};
use crate::{builtin::setup_builtin, jobs::JobBldr, libsh::error::{ShErr, ShErrKind, ShResult}, parse::{NdRule, Node}, procio::{IoStack, borrow_fd}, state::{self, read_logic, write_logic}};
#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub enum TrapTarget {
Exit,
Error,
Signal(Signal)
}
impl FromStr for TrapTarget {
type Err = ShErr;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"EXIT" => Ok(TrapTarget::Exit),
"ERR" => Ok(TrapTarget::Error),
"INT" => Ok(TrapTarget::Signal(Signal::SIGINT)),
"QUIT" => Ok(TrapTarget::Signal(Signal::SIGQUIT)),
"ILL" => Ok(TrapTarget::Signal(Signal::SIGILL)),
"TRAP" => Ok(TrapTarget::Signal(Signal::SIGTRAP)),
"ABRT" => Ok(TrapTarget::Signal(Signal::SIGABRT)),
"BUS" => Ok(TrapTarget::Signal(Signal::SIGBUS)),
"FPE" => Ok(TrapTarget::Signal(Signal::SIGFPE)),
"KILL" => Ok(TrapTarget::Signal(Signal::SIGKILL)),
"USR1" => Ok(TrapTarget::Signal(Signal::SIGUSR1)),
"SEGV" => Ok(TrapTarget::Signal(Signal::SIGSEGV)),
"USR2" => Ok(TrapTarget::Signal(Signal::SIGUSR2)),
"PIPE" => Ok(TrapTarget::Signal(Signal::SIGPIPE)),
"ALRM" => Ok(TrapTarget::Signal(Signal::SIGALRM)),
"TERM" => Ok(TrapTarget::Signal(Signal::SIGTERM)),
"STKFLT" => Ok(TrapTarget::Signal(Signal::SIGSTKFLT)),
"CHLD" => Ok(TrapTarget::Signal(Signal::SIGCHLD)),
"CONT" => Ok(TrapTarget::Signal(Signal::SIGCONT)),
"STOP" => Ok(TrapTarget::Signal(Signal::SIGSTOP)),
"TSTP" => Ok(TrapTarget::Signal(Signal::SIGTSTP)),
"TTIN" => Ok(TrapTarget::Signal(Signal::SIGTTIN)),
"TTOU" => Ok(TrapTarget::Signal(Signal::SIGTTOU)),
"URG" => Ok(TrapTarget::Signal(Signal::SIGURG)),
"XCPU" => Ok(TrapTarget::Signal(Signal::SIGXCPU)),
"XFSZ" => Ok(TrapTarget::Signal(Signal::SIGXFSZ)),
"VTALRM" => Ok(TrapTarget::Signal(Signal::SIGVTALRM)),
"PROF" => Ok(TrapTarget::Signal(Signal::SIGPROF)),
"WINCH" => Ok(TrapTarget::Signal(Signal::SIGWINCH)),
"IO" => Ok(TrapTarget::Signal(Signal::SIGIO)),
"PWR" => Ok(TrapTarget::Signal(Signal::SIGPWR)),
"SYS" => Ok(TrapTarget::Signal(Signal::SIGSYS)),
_ => {
return Err(ShErr::simple(
ShErrKind::ExecFail,
format!("invalid trap target '{}'", s),
))
}
}
}
}
impl Display for TrapTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TrapTarget::Exit => write!(f, "EXIT"),
TrapTarget::Error => write!(f, "ERR"),
TrapTarget::Signal(s) => {
match s {
Signal::SIGHUP => write!(f, "HUP"),
Signal::SIGINT => write!(f, "INT"),
Signal::SIGQUIT => write!(f, "QUIT"),
Signal::SIGILL => write!(f, "ILL"),
Signal::SIGTRAP => write!(f, "TRAP"),
Signal::SIGABRT => write!(f, "ABRT"),
Signal::SIGBUS => write!(f, "BUS"),
Signal::SIGFPE => write!(f, "FPE"),
Signal::SIGKILL => write!(f, "KILL"),
Signal::SIGUSR1 => write!(f, "USR1"),
Signal::SIGSEGV => write!(f, "SEGV"),
Signal::SIGUSR2 => write!(f, "USR2"),
Signal::SIGPIPE => write!(f, "PIPE"),
Signal::SIGALRM => write!(f, "ALRM"),
Signal::SIGTERM => write!(f, "TERM"),
Signal::SIGSTKFLT => write!(f, "STKFLT"),
Signal::SIGCHLD => write!(f, "CHLD"),
Signal::SIGCONT => write!(f, "CONT"),
Signal::SIGSTOP => write!(f, "STOP"),
Signal::SIGTSTP => write!(f, "TSTP"),
Signal::SIGTTIN => write!(f, "TTIN"),
Signal::SIGTTOU => write!(f, "TTOU"),
Signal::SIGURG => write!(f, "URG"),
Signal::SIGXCPU => write!(f, "XCPU"),
Signal::SIGXFSZ => write!(f, "XFSZ"),
Signal::SIGVTALRM => write!(f, "VTALRM"),
Signal::SIGPROF => write!(f, "PROF"),
Signal::SIGWINCH => write!(f, "WINCH"),
Signal::SIGIO => write!(f, "IO"),
Signal::SIGPWR => write!(f, "PWR"),
Signal::SIGSYS => write!(f, "SYS"),
_ => {
log::warn!("TrapTarget::fmt() : unrecognized signal {}", s);
Err(std::fmt::Error)
}
}
}
}
}
}
pub fn trap(node: Node, io_stack: &mut IoStack, job: &mut JobBldr) -> ShResult<()> {
let span = node.get_span();
let NdRule::Command {
assignments: _,
argv,
} = node.class
else {
unreachable!()
};
let (argv, _guard) = setup_builtin(argv, job, Some((io_stack, node.redirs)))?;
if argv.is_empty() {
let stdout = borrow_fd(STDOUT_FILENO);
return read_logic(|l| -> ShResult<()> {
for l in l.traps() {
let target = l.0;
let command = l.1;
write(stdout, format!("trap -- '{command}' {target}\n").as_bytes())?;
}
Ok(())
});
}
if argv.len() == 1 {
let stderr = borrow_fd(STDERR_FILENO);
write(stderr, b"usage: trap <COMMAND> [SIGNAL...]\n")?;
state::set_status(1);
return Ok(())
}
let mut args = argv.into_iter();
let command = args.next().unwrap().0;
let mut targets = vec![];
while let Some((arg, _)) = args.next() {
let target = arg.parse::<TrapTarget>()?;
targets.push(target);
}
for target in targets {
if &command == "-" {
write_logic(|l| l.remove_trap(target))
} else {
write_logic(|l| l.insert_trap(target, command.clone()))
}
}
state::set_status(0);
Ok(())
}

View File

@@ -2,7 +2,7 @@ use crate::{
libsh::{
error::ShResult,
term::{Style, Styled},
}, prelude::*, procio::{IoMode, borrow_fd}, signal::{disable_reaping, enable_reaping}, state::{self, set_status, write_jobs}
}, prelude::*, procio::{IoMode, borrow_fd}, signal::{disable_reaping, enable_reaping}, state::{self, set_status, read_jobs, write_jobs}
};
pub const SIG_EXIT_OFFSET: i32 = 128;
@@ -610,11 +610,7 @@ impl Job {
let job_stat = *self.get_stats().get(i).unwrap();
let fmt_stat = DisplayWaitStatus(job_stat).to_string();
let mut stat_line = if init {
"".to_string()
} else {
fmt_stat.clone()
};
let mut stat_line = fmt_stat.clone();
stat_line = format!("{}{} ", pid, stat_line);
stat_line = format!("{} {}", stat_line, cmd);
stat_line = match job_stat {

View File

@@ -27,6 +27,7 @@ use nix::poll::{PollFd, PollFlags, PollTimeout, poll};
use nix::unistd::read;
use nix::errno::Errno;
use crate::builtin::trap::TrapTarget;
use crate::libsh::error::{ShErr, ShErrKind, ShResult};
use crate::parse::execute::exec_input;
use crate::prelude::*;
@@ -34,7 +35,7 @@ use crate::prompt::get_prompt;
use crate::prompt::readline::term::raw_mode;
use crate::prompt::readline::{FernVi, ReadlineEvent};
use crate::signal::{QUIT_CODE, check_signals, sig_setup, signals_pending};
use crate::state::{source_rc, write_meta};
use crate::state::{read_logic, source_rc, write_meta};
use clap::Parser;
use state::{read_vars, write_vars};
@@ -85,6 +86,11 @@ fn main() -> ExitCode {
eprintln!("fern: {e}");
};
if let Some(trap) = read_logic(|l| l.get_trap(TrapTarget::Exit))
&& let Err(e) = exec_input(trap, None, false) {
eprintln!("fern: error running EXIT trap: {e}");
}
ExitCode::from(QUIT_CODE.load(Ordering::SeqCst) as u8)
}

View File

@@ -2,7 +2,7 @@ use std::collections::{HashSet, VecDeque};
use crate::{
builtin::{
alias::{alias, unalias}, cd::cd, echo::echo, export::export, flowctl::flowctl, jobctl::{JobBehavior, continue_job, jobs}, pwd::pwd, read::read_builtin, shift::shift, shopt::shopt, source::source, test::double_bracket_test, zoltraak::zoltraak
alias::{alias, unalias}, cd::cd, echo::echo, export::export, flowctl::flowctl, jobctl::{JobBehavior, continue_job, jobs}, pwd::pwd, read::read_builtin, shift::shift, shopt::shopt, source::source, test::double_bracket_test, trap::{TrapTarget, trap}, zoltraak::zoltraak
},
expand::expand_aliases,
jobs::{ChildProc, JobStack, dispatch_job},
@@ -126,7 +126,16 @@ pub fn exec_input(input: String, io_stack: Option<IoStack>, interactive: bool) -
if let Some(mut stack) = io_stack {
dispatcher.io_stack.extend(stack.drain(..));
}
dispatcher.begin_dispatch()
let result = dispatcher.begin_dispatch();
if state::get_status() != 0
&& let Some(trap) = read_logic(|l| l.get_trap(TrapTarget::Error)) {
let saved_status = state::get_status();
exec_input(trap, None, false)?;
state::set_status(saved_status);
}
result
}
pub struct Dispatcher {
@@ -602,6 +611,7 @@ impl Dispatcher {
"zoltraak" => zoltraak(cmd, io_stack_mut, curr_job_mut),
"shopt" => shopt(cmd, io_stack_mut, curr_job_mut),
"read" => read_builtin(cmd, io_stack_mut, curr_job_mut),
"trap" => trap(cmd, io_stack_mut, curr_job_mut),
_ => unimplemented!(
"Have not yet added support for builtin '{}'",
cmd_raw.span.as_str()

View File

@@ -1238,11 +1238,18 @@ impl ParseStream {
fn parse_pipeln(&mut self) -> ShResult<Option<Node>> {
let mut cmds = vec![];
let mut node_tks = vec![];
let mut flags = NdFlags::empty();
while let Some(cmd) = self.parse_block(false)? {
let is_punctuated = node_is_punctuated(&cmd.tokens);
node_tks.append(&mut cmd.tokens.clone());
cmds.push(cmd);
if *self.next_tk_class() != TkRule::Pipe || is_punctuated {
if *self.next_tk_class() == TkRule::Bg {
let tk = self.next_tk().unwrap();
node_tks.push(tk.clone());
flags |= NdFlags::BACKGROUND;
break;
} else if *self.next_tk_class() != TkRule::Pipe || is_punctuated {
break;
} else if let Some(pipe) = self.next_tk() {
node_tks.push(pipe)
@@ -1259,7 +1266,7 @@ impl ParseStream {
cmds,
pipe_err: false,
},
flags: NdFlags::empty(),
flags,
redirs: vec![],
tokens: node_tks,
}))
@@ -1271,6 +1278,7 @@ impl ParseStream {
let mut node_tks = vec![];
let mut redirs = vec![];
let mut argv = vec![];
let mut flags = NdFlags::empty();
let mut assignments = vec![];
while let Some(prefix_tk) = tk_iter.next() {
@@ -1316,15 +1324,18 @@ impl ParseStream {
return Ok(Some(Node {
class: NdRule::Command { assignments, argv },
tokens: node_tks,
flags: NdFlags::empty(),
flags,
redirs,
}));
}
}
while let Some(tk) = tk_iter.next() {
if *self.next_tk_class() == TkRule::Bg {
break;
}
match tk.class {
TkRule::EOI | TkRule::Pipe | TkRule::And | TkRule::BraceGrpEnd | TkRule::Or => break,
TkRule::EOI | TkRule::Pipe | TkRule::And | TkRule::BraceGrpEnd | TkRule::Or | TkRule::Bg => break,
TkRule::Sep => {
node_tks.push(tk.clone());
break;
@@ -1370,7 +1381,7 @@ impl ParseStream {
Ok(Some(Node {
class: NdRule::Command { assignments, argv },
tokens: node_tks,
flags: NdFlags::empty(),
flags,
redirs,
}))
}

View File

@@ -17,7 +17,7 @@ use unicode_segmentation::UnicodeSegmentation;
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
use vte::{Parser, Perform};
use crate::{prelude::*, procio::borrow_fd};
use crate::{prelude::*, procio::borrow_fd, state::{read_meta, write_meta}};
use crate::{
libsh::error::{ShErr, ShErrKind, ShResult},
prompt::readline::keys::{KeyCode, ModKeys},
@@ -914,8 +914,16 @@ impl LineWriter for TermWriter {
let end = new_layout.end;
let cursor = new_layout.cursor;
if read_meta(|m| m.system_msg_pending()) {
let mut system_msg = String::new();
while let Some(msg) = write_meta(|m| m.pop_system_message()) {
writeln!(system_msg, "{msg}").map_err(err)?;
}
self.buffer.push_str(&system_msg);
}
self.buffer.push_str(prompt);
self.buffer.push_str(&line.to_string());
self.buffer.push_str(line);
if end.col == 0 && end.row > 0 && !self.buffer.ends_with('\n') {
// The line has wrapped. We need to use our own line break.

View File

@@ -1,47 +1,84 @@
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering};
use nix::sys::signal::{SaFlags, SigAction, sigaction};
use crate::{
jobs::{JobCmdFlags, JobID, take_term},
libsh::error::{ShErr, ShErrKind, ShResult},
prelude::*,
state::{read_jobs, write_jobs},
builtin::trap::TrapTarget, jobs::{JobCmdFlags, JobID, take_term}, libsh::error::{ShErr, ShErrKind, ShResult}, parse::execute::exec_input, prelude::*, state::{read_jobs, read_logic, write_jobs, write_meta}
};
static GOT_SIGINT: AtomicBool = AtomicBool::new(false);
static GOT_SIGHUP: AtomicBool = AtomicBool::new(false);
static GOT_SIGTSTP: AtomicBool = AtomicBool::new(false);
static GOT_SIGCHLD: AtomicBool = AtomicBool::new(false);
static REAPING_ENABLED: AtomicBool = AtomicBool::new(true);
static SIGNALS: AtomicU64 = AtomicU64::new(0);
pub static REAPING_ENABLED: AtomicBool = AtomicBool::new(true);
pub static SHOULD_QUIT: AtomicBool = AtomicBool::new(false);
pub static QUIT_CODE: AtomicI32 = AtomicI32::new(0);
const MISC_SIGNALS: [Signal;22] = [
Signal::SIGILL,
Signal::SIGTRAP,
Signal::SIGABRT,
Signal::SIGBUS,
Signal::SIGFPE,
Signal::SIGUSR1,
Signal::SIGSEGV,
Signal::SIGUSR2,
Signal::SIGPIPE,
Signal::SIGALRM,
Signal::SIGTERM,
Signal::SIGSTKFLT,
Signal::SIGCONT,
Signal::SIGURG,
Signal::SIGXCPU,
Signal::SIGXFSZ,
Signal::SIGVTALRM,
Signal::SIGPROF,
Signal::SIGWINCH,
Signal::SIGIO,
Signal::SIGPWR,
Signal::SIGSYS,
];
pub fn signals_pending() -> bool {
GOT_SIGINT.load(Ordering::SeqCst)
|| GOT_SIGHUP.load(Ordering::SeqCst)
|| GOT_SIGTSTP.load(Ordering::SeqCst)
|| (REAPING_ENABLED.load(Ordering::SeqCst)
&& GOT_SIGCHLD.load(Ordering::SeqCst))
|| SHOULD_QUIT.load(Ordering::SeqCst)
SIGNALS.load(Ordering::SeqCst) != 0 || SHOULD_QUIT.load(Ordering::SeqCst)
}
pub fn check_signals() -> ShResult<()> {
if GOT_SIGINT.swap(false, Ordering::SeqCst) {
let pending = SIGNALS.swap(0, Ordering::SeqCst);
let got_signal = |sig: Signal| -> bool { pending & (1 << sig as u64) != 0 };
let run_trap = |sig: Signal| -> ShResult<()> {
if let Some(command) = read_logic(|l| l.get_trap(TrapTarget::Signal(sig))) {
exec_input(command, None, false)?;
}
Ok(())
};
if got_signal(Signal::SIGINT) {
interrupt()?;
run_trap(Signal::SIGINT)?;
return Err(ShErr::simple(ShErrKind::ClearReadline, ""));
}
if GOT_SIGHUP.swap(false, Ordering::SeqCst) {
if got_signal(Signal::SIGHUP) {
run_trap(Signal::SIGHUP)?;
hang_up(0);
}
if GOT_SIGTSTP.swap(false, Ordering::SeqCst) {
if got_signal(Signal::SIGQUIT) {
run_trap(Signal::SIGQUIT)?;
hang_up(0);
}
if got_signal(Signal::SIGTSTP) {
run_trap(Signal::SIGTSTP)?;
terminal_stop()?;
}
if REAPING_ENABLED.load(Ordering::SeqCst) && GOT_SIGCHLD.swap(false, Ordering::SeqCst) {
if got_signal(Signal::SIGCHLD) && REAPING_ENABLED.load(Ordering::SeqCst) {
run_trap(Signal::SIGCHLD)?;
wait_child()?;
} else if GOT_SIGCHLD.load(Ordering::SeqCst) {
}
for sig in MISC_SIGNALS {
if got_signal(sig) {
run_trap(sig)?;
}
}
if SHOULD_QUIT.load(Ordering::SeqCst) {
let code = QUIT_CODE.load(Ordering::SeqCst);
return Err(ShErr::simple(ShErrKind::CleanExit(code), "exit"));
@@ -59,78 +96,52 @@ pub fn enable_reaping() {
pub fn sig_setup() {
let flags = SaFlags::empty();
let actions = [
SigAction::new(
SigHandler::Handler(handle_sigchld),
flags,
SigSet::empty(),
),
SigAction::new(
SigHandler::Handler(handle_sigquit),
flags,
SigSet::empty(),
),
SigAction::new(
SigHandler::Handler(handle_sigtstp),
flags,
SigSet::empty(),
),
SigAction::new(
SigHandler::Handler(handle_sighup),
flags,
SigSet::empty(),
),
SigAction::new(
SigHandler::Handler(handle_sigint),
flags,
SigSet::empty(),
),
SigAction::new( // SIGTTIN
SigHandler::SigIgn,
flags,
SigSet::empty(),
),
SigAction::new( // SIGTTOU
SigHandler::SigIgn,
flags,
SigSet::empty(),
),
SigAction::new(
SigHandler::Handler(handle_sigwinch),
flags,
SigSet::empty(),
),
];
let action = SigAction::new(SigHandler::Handler(handle_signal), flags, SigSet::empty());
let ignore = SigAction::new(SigHandler::SigIgn, flags, SigSet::empty());
unsafe {
sigaction(Signal::SIGCHLD, &actions[0]).unwrap();
sigaction(Signal::SIGQUIT, &actions[1]).unwrap();
sigaction(Signal::SIGTSTP, &actions[2]).unwrap();
sigaction(Signal::SIGHUP, &actions[3]).unwrap();
sigaction(Signal::SIGINT, &actions[4]).unwrap();
sigaction(Signal::SIGTTIN, &actions[5]).unwrap();
sigaction(Signal::SIGTTOU, &actions[6]).unwrap();
sigaction(Signal::SIGWINCH, &actions[7]).unwrap();
sigaction(Signal::SIGTTIN, &ignore).unwrap();
sigaction(Signal::SIGTTOU, &ignore).unwrap();
sigaction(Signal::SIGCHLD, &action).unwrap();
sigaction(Signal::SIGHUP, &action).unwrap();
sigaction(Signal::SIGINT, &action).unwrap();
sigaction(Signal::SIGQUIT, &action).unwrap();
sigaction(Signal::SIGILL, &action).unwrap();
sigaction(Signal::SIGTRAP, &action).unwrap();
sigaction(Signal::SIGABRT, &action).unwrap();
sigaction(Signal::SIGBUS, &action).unwrap();
sigaction(Signal::SIGFPE, &action).unwrap();
sigaction(Signal::SIGUSR1, &action).unwrap();
sigaction(Signal::SIGSEGV, &action).unwrap();
sigaction(Signal::SIGUSR2, &action).unwrap();
sigaction(Signal::SIGPIPE, &action).unwrap();
sigaction(Signal::SIGALRM, &action).unwrap();
sigaction(Signal::SIGTERM, &action).unwrap();
sigaction(Signal::SIGSTKFLT, &action).unwrap();
sigaction(Signal::SIGCONT, &action).unwrap();
sigaction(Signal::SIGTSTP, &action).unwrap();
sigaction(Signal::SIGURG, &action).unwrap();
sigaction(Signal::SIGXCPU, &action).unwrap();
sigaction(Signal::SIGXFSZ, &action).unwrap();
sigaction(Signal::SIGVTALRM, &action).unwrap();
sigaction(Signal::SIGPROF, &action).unwrap();
sigaction(Signal::SIGWINCH, &action).unwrap();
sigaction(Signal::SIGIO, &action).unwrap();
sigaction(Signal::SIGPWR, &action).unwrap();
sigaction(Signal::SIGSYS, &action).unwrap();
}
}
extern "C" fn handle_sigwinch(_: libc::c_int) {
/* do nothing
* this exists for the sole purpose of interrupting readline
* readline will be refreshed after the interruption,
* which will cause window size calculations to be re-run
* and we get window resize handling for free as a result
*/
}
extern "C" fn handle_sighup(_: libc::c_int) {
GOT_SIGHUP.store(true, Ordering::SeqCst);
SHOULD_QUIT.store(true, Ordering::SeqCst);
QUIT_CODE.store(128 + libc::SIGHUP, Ordering::SeqCst);
extern "C" fn handle_signal(sig: libc::c_int) {
SIGNALS.fetch_or(1 << sig, Ordering::SeqCst);
}
pub fn hang_up(_: libc::c_int) {
SHOULD_QUIT.store(true, Ordering::SeqCst);
QUIT_CODE.store(1, Ordering::SeqCst);
write_jobs(|j| {
for job in j.jobs_mut().iter_mut().flatten() {
job.killpg(Signal::SIGTERM).ok();
@@ -138,10 +149,6 @@ pub fn hang_up(_: libc::c_int) {
});
}
extern "C" fn handle_sigtstp(_: libc::c_int) {
GOT_SIGTSTP.store(true, Ordering::SeqCst);
}
pub fn terminal_stop() -> ShResult<()> {
write_jobs(|j| {
if let Some(job) = j.get_fg_mut() {
@@ -153,10 +160,6 @@ pub fn terminal_stop() -> ShResult<()> {
// TODO: It seems like there is supposed to be a take_term() call here
}
extern "C" fn handle_sigint(_: libc::c_int) {
GOT_SIGINT.store(true, Ordering::SeqCst);
}
pub fn interrupt() -> ShResult<()> {
write_jobs(|j| {
if let Some(job) = j.get_fg_mut() {
@@ -167,20 +170,11 @@ pub fn interrupt() -> ShResult<()> {
})
}
extern "C" fn handle_sigquit(_: libc::c_int) {
SHOULD_QUIT.store(true, Ordering::SeqCst);
QUIT_CODE.store(128 + libc::SIGQUIT, Ordering::SeqCst);
}
extern "C" fn handle_sigchld(_: libc::c_int) {
GOT_SIGCHLD.store(true, Ordering::SeqCst);
}
pub fn wait_child() -> ShResult<()> {
let flags = WtFlag::WNOHANG | WtFlag::WSTOPPED;
while let Ok(status) = waitpid(None, Some(flags)) {
match status {
WtStat::Exited(pid, code) => {
WtStat::Exited(pid, _) => {
child_exited(pid, status)?;
}
WtStat::Signaled(pid, signal, _) => {
@@ -284,7 +278,8 @@ pub fn child_exited(pid: Pid, status: WtStat) -> ShResult<()> {
let job_order = read_jobs(|j| j.order().to_vec());
let result = read_jobs(|j| j.query(JobID::Pgid(pgid)).cloned());
if let Some(job) = result {
println!("{}", job.display(&job_order, JobCmdFlags::PIDS))
let job_complete_msg = job.display(&job_order, JobCmdFlags::PIDS).to_string();
write_meta(|m| m.post_system_message(job_complete_msg))
}
}
}

View File

@@ -5,15 +5,10 @@ use std::{
use nix::unistd::{gethostname, getppid, User};
use crate::{
exec_input,
jobs::JobTab,
libsh::{
builtin::trap::TrapTarget, exec_input, jobs::JobTab, libsh::{
error::{ShErr, ShErrKind, ShResult},
utils::VecDequeExt,
},
parse::{ConjunctNode, NdRule, Node, ParsedSrc},
prelude::*,
shopt::ShOpts,
}, parse::{ConjunctNode, NdRule, Node, ParsedSrc}, prelude::*, shopt::ShOpts
};
pub struct Fern {
@@ -292,6 +287,7 @@ impl Deref for ShFunc {
pub struct LogTab {
functions: HashMap<String, ShFunc>,
aliases: HashMap<String, String>,
traps: HashMap<TrapTarget, String>,
}
impl LogTab {
@@ -301,6 +297,18 @@ impl LogTab {
pub fn insert_func(&mut self, name: &str, src: ShFunc) {
self.functions.insert(name.into(), src);
}
pub fn insert_trap(&mut self, target: TrapTarget, command: String) {
self.traps.insert(target, command);
}
pub fn get_trap(&self, target: TrapTarget) -> Option<String> {
self.traps.get(&target).cloned()
}
pub fn remove_trap(&mut self, target: TrapTarget) {
self.traps.remove(&target);
}
pub fn traps(&self) -> &HashMap<TrapTarget, String> {
&self.traps
}
pub fn get_func(&self, name: &str) -> Option<ShFunc> {
self.functions.get(name).cloned()
}
@@ -682,8 +690,12 @@ impl VarTab {
/// A table of metadata for the shell
#[derive(Default, Debug)]
pub struct MetaTab {
// command running duration
runtime_start: Option<Instant>,
runtime_stop: Option<Instant>,
// pending system messages
system_msg: Vec<String>
}
impl MetaTab {
@@ -703,6 +715,15 @@ impl MetaTab {
None
}
}
pub fn post_system_message(&mut self, message: String) {
self.system_msg.push(message);
}
pub fn pop_system_message(&mut self) -> Option<String> {
self.system_msg.pop()
}
pub fn system_msg_pending(&self) -> bool {
!self.system_msg.is_empty()
}
}
/// Read from the job table