From c3eb46cadffe4fe1eb6d51327f81d7c6c40aab48 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy <mmstickman@gmail.com> Date: Sat, 9 Dec 2017 00:21:48 -0500 Subject: [PATCH] Implement huponexit shell option To enable this option, use `set -o huponexit`. This will send **SIGHUP** to all background jobs when exiting the shell. If a background job is stopped, that job will be resumed with a **SIGCONT** before being sent a **SIGHUP**. --- src/builtins/set.rs | 30 ++++++++++++++++++------------ src/shell/flags.rs | 1 + src/shell/mod.rs | 13 +++++++++++-- src/shell/pipe_exec/job_control.rs | 10 ++++++++++ 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/builtins/set.rs b/src/builtins/set.rs index c48049a6..e97fe16f 100644 --- a/src/builtins/set.rs +++ b/src/builtins/set.rs @@ -1,7 +1,6 @@ use liner::KeyBindings; use shell::Shell; use shell::flags::*; -use std::io::{self, Write}; use std::iter; enum PositionalArgs { @@ -12,7 +11,6 @@ enum PositionalArgs { use self::PositionalArgs::*; pub(crate) fn set(args: &[&str], shell: &mut Shell) -> i32 { - let stderr = io::stderr(); let mut args_iter = args.iter(); let mut positionals = None; @@ -32,22 +30,19 @@ pub(crate) fn set(args: &[&str], shell: &mut Shell) -> i32 { match flag { b'e' => shell.flags |= ERR_EXIT, b'o' => match args_iter.next() { - Some(&mode) if mode == "vi" => { - if let Some(context) = shell.context.as_mut() { - context.key_bindings = KeyBindings::Vi; - } + Some(&"vi") => if let Some(context) = shell.context.as_mut() { + context.key_bindings = KeyBindings::Vi; } - Some(&mode) if mode == "emacs" => { - if let Some(context) = shell.context.as_mut() { - context.key_bindings = KeyBindings::Emacs; - } + Some(&"emacs") => if let Some(context) = shell.context.as_mut() { + context.key_bindings = KeyBindings::Emacs; } + Some(&"huponexit") => shell.flags |= HUPONEXIT, Some(_) => { - let _ = stderr.lock().write_all(b"set: invalid keymap\n"); + eprintln!("ion: set: invalid option"); return 0; } None => { - let _ = stderr.lock().write_all(b"set: no keymap given\n"); + eprintln!("ion: set: no option given"); return 0; } }, @@ -60,6 +55,17 @@ pub(crate) fn set(args: &[&str], shell: &mut Shell) -> i32 { match flag { b'e' => shell.flags &= 255 ^ ERR_EXIT, b'x' => shell.flags &= 255 ^ PRINT_COMMS, + b'o' => match args_iter.next() { + Some(&"huponexit") => shell.flags &= 255 ^ HUPONEXIT, + Some(_) => { + eprintln!("ion: set: invalid option"); + return 0; + } + None => { + eprintln!("ion: set: no option given"); + return 0; + } + } _ => return 0, } } diff --git a/src/shell/flags.rs b/src/shell/flags.rs index b5de908e..9dcb63b9 100644 --- a/src/shell/flags.rs +++ b/src/shell/flags.rs @@ -1,3 +1,4 @@ pub const ERR_EXIT: u8 = 1; pub const PRINT_COMMS: u8 = 2; pub const NO_EXEC: u8 = 4; +pub const HUPONEXIT: u8 = 8; \ No newline at end of file diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 88e36656..00397a61 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -165,9 +165,18 @@ impl<'a> Shell { } pub(crate) fn exit(&mut self, status: i32) -> ! { - if let Some(context) = self.context.as_mut() { - context.history.commit_history(); + // The context has two purposes: if it exists, this is an interactive shell; and the + // context will also be sent a signal to commit all changes to the history file, + // and waiting for the history thread in the background to finish. + if self.context.is_some() { + if self.flags & HUPONEXIT != 0 { + self.resume_stopped(); + self.background_send(sys::SIGHUP); + } + let context = self.context.as_mut().unwrap(); + context.history.commit_history() } + process::exit(status); } diff --git a/src/shell/pipe_exec/job_control.rs b/src/shell/pipe_exec/job_control.rs index ebc03148..aab408fe 100644 --- a/src/shell/pipe_exec/job_control.rs +++ b/src/shell/pipe_exec/job_control.rs @@ -29,6 +29,7 @@ pub(crate) trait JobControl { /// will /// be returned, and ownership of the TTY given back to the shell. fn set_bg_task_in_foreground(&self, pid: u32, cont: bool) -> i32; + fn resume_stopped(&mut self); fn handle_signal(&self, signal: i32) -> bool; fn foreground_send(&self, signal: i32); fn background_send(&self, signal: i32); @@ -177,6 +178,15 @@ impl JobControl for Shell { } } + /// Resumes all stopped background jobs + fn resume_stopped(&mut self) { + for process in self.background.lock().unwrap().iter() { + if process.state == ProcessState::Stopped { + signals::resume(process.pid); + } + } + } + /// Send a kill signal to all running background tasks. fn background_send(&self, signal: i32) { if signal == sys::SIGHUP { -- GitLab