From 5d195096163c6838c2df1327cca21eb5ca3a9045 Mon Sep 17 00:00:00 2001 From: AnotherTest Date: Tue, 30 Mar 2021 02:19:02 +0430 Subject: [PATCH] Shell: Handle SIGCHLD after sending SIGCONT to job This fixes `fg` and `bg` causing the shell to go into an infinite loop of trying to `waitpid` until some current job changes state. a.k.a. "Fix Shell backgrounding, yet again!" :P --- Userland/Shell/Builtin.cpp | 4 ++-- Userland/Shell/Job.h | 3 +++ Userland/Shell/Shell.cpp | 8 +++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Userland/Shell/Builtin.cpp b/Userland/Shell/Builtin.cpp index 1c79d056e66..8e90a3fc49b 100644 --- a/Userland/Shell/Builtin.cpp +++ b/Userland/Shell/Builtin.cpp @@ -111,7 +111,7 @@ int Shell::builtin_bg(int argc, const char** argv) job->set_running_in_background(true); job->set_should_announce_exit(true); - job->set_is_suspended(false); + job->set_shell_did_continue(true); dbgln("Resuming {} ({})", job->pid(), job->cmd()); warnln("Resuming job {} - {}", job->job_id(), job->cmd().characters()); @@ -396,7 +396,7 @@ int Shell::builtin_fg(int argc, const char** argv) } job->set_running_in_background(false); - job->set_is_suspended(false); + job->set_shell_did_continue(true); dbgln("Resuming {} ({})", job->pid(), job->cmd()); warnln("Resuming job {} - {}", job->job_id(), job->cmd().characters()); diff --git a/Userland/Shell/Job.h b/Userland/Shell/Job.h index f2470b283d7..af05521dddd 100644 --- a/Userland/Shell/Job.h +++ b/Userland/Shell/Job.h @@ -86,6 +86,7 @@ public: bool should_announce_exit() const { return m_should_announce_exit; } bool should_announce_signal() const { return m_should_announce_signal; } bool is_suspended() const { return m_is_suspended; } + bool shell_did_continue() const { return m_shell_did_continue; } void unblock() const; Core::ElapsedTimer& timer() { return m_command_timer; } @@ -94,6 +95,7 @@ public: void set_signalled(int sig); void set_is_suspended(bool value) const { m_is_suspended = value; } + void set_shell_did_continue(bool value) const { m_shell_did_continue = value; } void set_running_in_background(bool running_in_background) { @@ -129,6 +131,7 @@ private: Core::ElapsedTimer m_command_timer; mutable bool m_active { true }; mutable bool m_is_suspended { false }; + mutable bool m_shell_did_continue { false }; bool m_should_be_disowned { false }; OwnPtr m_command; }; diff --git a/Userland/Shell/Shell.cpp b/Userland/Shell/Shell.cpp index d1d2fa7bae4..296c24a6509 100644 --- a/Userland/Shell/Shell.cpp +++ b/Userland/Shell/Shell.cpp @@ -1057,7 +1057,7 @@ void Shell::block_on_job(RefPtr job) if (!job) return; - if (job->is_suspended()) + if (job->is_suspended() && !job->shell_did_continue()) return; // We cannot wait for a suspended job. ScopeGuard io_restorer { [&]() { @@ -1655,6 +1655,12 @@ void Shell::notify_child_event() } if (child_pid == 0) { // If the child existed, but wasn't dead. + if (job.is_suspended() && job.shell_did_continue()) { + // The job was suspended, and we sent it a SIGCONT. + job.set_is_suspended(false); + job.set_shell_did_continue(false); + found_child = true; + } continue; } if (child_pid == job.pid()) {