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
This commit is contained in:
AnotherTest 2021-03-30 02:19:02 +04:30 committed by Andreas Kling
parent 50f5959996
commit 5d19509616
Notes: sideshowbarker 2024-07-18 20:56:00 +09:00
3 changed files with 12 additions and 3 deletions

View file

@ -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());

View file

@ -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<AST::Command> m_command;
};

View file

@ -1057,7 +1057,7 @@ void Shell::block_on_job(RefPtr<Job> 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()) {