From b3f8044012254cb2789aa1a69658d220b52d15a2 Mon Sep 17 00:00:00 2001 From: John-Bush14 Date: Tue, 3 Dec 2024 18:24:16 +0100 Subject: [PATCH 1/4] feat: job killing methods have signal parameter --- brush-core/src/builtins/kill.rs | 2 +- brush-core/src/jobs.rs | 4 ++-- brush-core/src/sys/unix/signal.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/brush-core/src/builtins/kill.rs b/brush-core/src/builtins/kill.rs index 3a134104..b73c4d99 100644 --- a/brush-core/src/builtins/kill.rs +++ b/brush-core/src/builtins/kill.rs @@ -49,7 +49,7 @@ impl builtins::Command for KillCommand { if pid_or_job_spec.starts_with('%') { // It's a job spec. if let Some(job) = context.shell.jobs.resolve_job_spec(pid_or_job_spec) { - job.kill()?; + job.kill(None)?; } else { writeln!( context.stderr(), diff --git a/brush-core/src/jobs.rs b/brush-core/src/jobs.rs index 65f573b8..c511b056 100644 --- a/brush-core/src/jobs.rs +++ b/brush-core/src/jobs.rs @@ -408,9 +408,9 @@ impl Job { } /// Kills the job. - pub fn kill(&mut self) -> Result<(), error::Error> { + pub fn kill(&mut self, signal: Option) -> Result<(), error::Error> { if let Some(pid) = self.get_process_group_id() { - sys::signal::kill_process(pid) + sys::signal::kill_process(pid, signal) } else { Err(error::Error::FailedToSendSignal) } diff --git a/brush-core/src/sys/unix/signal.rs b/brush-core/src/sys/unix/signal.rs index 67cb1a6d..1e859c1e 100644 --- a/brush-core/src/sys/unix/signal.rs +++ b/brush-core/src/sys/unix/signal.rs @@ -7,8 +7,8 @@ pub(crate) fn continue_process(pid: sys::process::ProcessId) -> Result<(), error Ok(()) } -pub(crate) fn kill_process(pid: sys::process::ProcessId) -> Result<(), error::Error> { - nix::sys::signal::kill(nix::unistd::Pid::from_raw(pid), nix::sys::signal::SIGKILL) +pub(crate) fn kill_process(pid: sys::process::ProcessId, signal: Option) -> Result<(), error::Error> { + nix::sys::signal::kill(nix::unistd::Pid::from_raw(pid), signal.unwrap_or_else(|| nix::sys::signal::SIGKILL)) .map_err(|_errno| error::Error::FailedToSendSignal)?; Ok(()) From d6ee07f8e93694c884948397ed77f594c2583e0b Mon Sep 17 00:00:00 2001 From: John-Bush14 Date: Thu, 5 Dec 2024 15:07:49 +0100 Subject: [PATCH 2/4] feat: shell.user_tried_exiting flag to next exit get's decreased every input and set to 2 every exit so if another exit is called in the same or next input it's not 0. --- brush-core/src/builtins/exit.rs | 4 ++++ brush-core/src/shell.rs | 5 +++++ brush-interactive/src/interactive_shell.rs | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/brush-core/src/builtins/exit.rs b/brush-core/src/builtins/exit.rs index 9c5b9d02..0ca0bba3 100644 --- a/brush-core/src/builtins/exit.rs +++ b/brush-core/src/builtins/exit.rs @@ -14,6 +14,10 @@ impl builtins::Command for ExitCommand { &self, context: commands::ExecutionContext<'_>, ) -> Result { + if !context.shell.jobs.jobs.is_empty() && context.shell.user_tried_exiting == 0 { + context.shell.user_tried_exiting = 2; // get's decreased this input too so next input will be 1 + } + let code_8bit: u8; #[allow(clippy::cast_sign_loss)] diff --git a/brush-core/src/shell.rs b/brush-core/src/shell.rs index 7e5db481..97feed9e 100644 --- a/brush-core/src/shell.rs +++ b/brush-core/src/shell.rs @@ -42,6 +42,9 @@ pub struct Shell { // Additional state /// The status of the last completed command. pub last_exit_status: u8, + + /// User tried exiting in the previous/current input if != 0. + pub user_tried_exiting: u8, /// Clone depth from the original ancestor shell. pub depth: usize, @@ -100,6 +103,7 @@ impl Clone for Shell { builtins: self.builtins.clone(), program_location_cache: self.program_location_cache.clone(), depth: self.depth + 1, + user_tried_exiting: self.user_tried_exiting } } } @@ -191,6 +195,7 @@ impl Shell { builtins: builtins::get_default_builtins(options), program_location_cache: pathcache::PathCache::default(), depth: 0, + user_tried_exiting: 0 }; // TODO: Without this a script that sets extglob will fail because we diff --git a/brush-interactive/src/interactive_shell.rs b/brush-interactive/src/interactive_shell.rs index cfe0a360..1ca038e9 100644 --- a/brush-interactive/src/interactive_shell.rs +++ b/brush-interactive/src/interactive_shell.rs @@ -135,6 +135,10 @@ pub trait InteractiveShell { ReadResult::Input(read_result) => { let mut shell_mut = self.shell_mut(); + if shell_mut.as_mut().user_tried_exiting > 0 { + shell_mut.as_mut().user_tried_exiting -= 1; + } + let precmd_prompt = shell_mut.as_mut().compose_precmd_prompt().await?; if !precmd_prompt.is_empty() { print!("{precmd_prompt}"); From c03362b55d0a8fc2d56eb4f3c2ba15fab36b95cd Mon Sep 17 00:00:00 2001 From: John-Bush14 Date: Thu, 5 Dec 2024 15:08:29 +0100 Subject: [PATCH 3/4] feat(exit): warning about running jobs and sending SIGHUP --- brush-core/src/builtins/exit.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/brush-core/src/builtins/exit.rs b/brush-core/src/builtins/exit.rs index 0ca0bba3..a57c3bb8 100644 --- a/brush-core/src/builtins/exit.rs +++ b/brush-core/src/builtins/exit.rs @@ -1,5 +1,7 @@ use clap::Parser; +use std::io::Write; + use crate::{builtins, commands}; /// Exit the shell. @@ -15,7 +17,15 @@ impl builtins::Command for ExitCommand { context: commands::ExecutionContext<'_>, ) -> Result { if !context.shell.jobs.jobs.is_empty() && context.shell.user_tried_exiting == 0 { + writeln!(context.stdout(), "brush: You have suspended jobs.")?; + context.shell.user_tried_exiting = 2; // get's decreased this input too so next input will be 1 + + return Ok(builtins::ExitCode::Custom(1)) + } + + for job in &mut context.shell.jobs.jobs { + job.kill(Some(nix::sys::signal::SIGHUP))?; } let code_8bit: u8; From bad26a6939c08b928065aabfc524dc4cd5d6cd78 Mon Sep 17 00:00:00 2001 From: John-Bush14 Date: Thu, 5 Dec 2024 15:10:16 +0100 Subject: [PATCH 4/4] feat: don't count empty inputs as next exit denying inputs --- brush-interactive/src/interactive_shell.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brush-interactive/src/interactive_shell.rs b/brush-interactive/src/interactive_shell.rs index 1ca038e9..bc6d6565 100644 --- a/brush-interactive/src/interactive_shell.rs +++ b/brush-interactive/src/interactive_shell.rs @@ -135,7 +135,7 @@ pub trait InteractiveShell { ReadResult::Input(read_result) => { let mut shell_mut = self.shell_mut(); - if shell_mut.as_mut().user_tried_exiting > 0 { + if shell_mut.as_mut().user_tried_exiting > 0 && !read_result.is_empty() { shell_mut.as_mut().user_tried_exiting -= 1; }