implemented the trap builtin
This commit is contained in:
@@ -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')
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
162
src/builtin/trap.rs
Normal 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(())
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
201
src/signal.rs
201
src/signal.rs
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
35
src/state.rs
35
src/state.rs
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user