1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 20:47:45 +00:00

Kernel: Add support for the WSTOPPED flag to the waitpid() syscall.

This makes waitpid() return when a child process is stopped via a signal.
Use this in Shell to catch stopped children and return control to the
command line. :^)

Fixes #298.
This commit is contained in:
Andreas Kling 2019-07-14 11:35:49 +02:00
parent de03b72979
commit 3073ea7d84
6 changed files with 44 additions and 20 deletions

View file

@ -1393,8 +1393,12 @@ int Process::reap(Process& process)
pid_t Process::sys$waitpid(pid_t waitee, int* wstatus, int options) pid_t Process::sys$waitpid(pid_t waitee, int* wstatus, int options)
{ {
dbgprintf("sys$waitpid(%d, %p, %d)\n", waitee, wstatus, options); dbgprintf("sys$waitpid(%d, %p, %d)\n", waitee, wstatus, options);
// FIXME: Respect options
(void)options; if (!options) {
// FIXME: This can't be right.. can it? Figure out how this should actually work.
options = WEXITED;
}
if (wstatus) if (wstatus)
if (!validate_write_typed(wstatus)) if (!validate_write_typed(wstatus))
return -EFAULT; return -EFAULT;
@ -1409,6 +1413,7 @@ pid_t Process::sys$waitpid(pid_t waitee, int* wstatus, int options)
} }
if (options & WNOHANG) { if (options & WNOHANG) {
// FIXME: Figure out what WNOHANG should do with stopped children.
if (waitee == -1) { if (waitee == -1) {
pid_t reaped_pid = 0; pid_t reaped_pid = 0;
InterruptDisabler disabler; InterruptDisabler disabler;
@ -1417,7 +1422,7 @@ pid_t Process::sys$waitpid(pid_t waitee, int* wstatus, int options)
reaped_pid = process.pid(); reaped_pid = process.pid();
exit_status = reap(process); exit_status = reap(process);
} }
return true; return IterationDecision::Continue;
}); });
return reaped_pid; return reaped_pid;
} else { } else {
@ -1435,17 +1440,22 @@ pid_t Process::sys$waitpid(pid_t waitee, int* wstatus, int options)
} }
current->m_waitee_pid = waitee; current->m_waitee_pid = waitee;
current->m_wait_options = options;
current->block(Thread::State::BlockedWait); current->block(Thread::State::BlockedWait);
if (current->m_was_interrupted_while_blocked) if (current->m_was_interrupted_while_blocked)
return -EINTR; return -EINTR;
Process* waitee_process;
{ InterruptDisabler disabler;
InterruptDisabler disabler;
// NOTE: If waitee was -1, m_waitee will have been filled in by the scheduler. // NOTE: If waitee was -1, m_waitee_pid will have been filled in by the scheduler.
waitee_process = Process::from_pid(current->m_waitee_pid); Process* waitee_process = Process::from_pid(current->m_waitee_pid);
}
ASSERT(waitee_process); ASSERT(waitee_process);
exit_status = reap(*waitee_process); if (waitee_process->is_dead()) {
exit_status = reap(*waitee_process);
} else {
ASSERT(waitee_process->main_thread().state() == Thread::State::Stopped);
exit_status = 0x7f;
}
return current->m_waitee_pid; return current->m_waitee_pid;
} }

View file

@ -411,7 +411,7 @@ inline void Process::for_each_child(Callback callback)
for (auto* process = g_processes->head(); process;) { for (auto* process = g_processes->head(); process;) {
auto* next_process = process->next(); auto* next_process = process->next();
if (process->ppid() == my_pid) { if (process->ppid() == my_pid) {
if (!callback(*process)) if (callback(*process) == IterationDecision::Break)
break; break;
} }
process = next_process; process = next_process;

View file

@ -83,14 +83,21 @@ bool Scheduler::pick_next()
if (thread.state() == Thread::BlockedWait) { if (thread.state() == Thread::BlockedWait) {
process.for_each_child([&](Process& child) { process.for_each_child([&](Process& child) {
if (!child.is_dead()) if (thread.waitee_pid() != -1 && thread.waitee_pid() != child.pid())
return true; return IterationDecision::Continue;
if (thread.waitee_pid() == -1 || thread.waitee_pid() == child.pid()) {
thread.m_waitee_pid = child.pid(); bool child_exited = child.is_dead();
thread.unblock(); bool child_stopped = child.main_thread().state() == Thread::State::Stopped;
return false;
} bool wait_finished = ((thread.m_wait_options & WEXITED) && child_exited)
return true; || ((thread.m_wait_options & WSTOPPED) && child_stopped);
if (!wait_finished)
return IterationDecision::Continue;
thread.m_waitee_pid = child.pid();
thread.unblock();
return IterationDecision::Break;
}); });
return IterationDecision::Continue; return IterationDecision::Continue;
} }

View file

@ -182,6 +182,7 @@ private:
RefPtr<Region> m_kernel_stack_region; RefPtr<Region> m_kernel_stack_region;
RefPtr<Region> m_kernel_stack_for_signal_handler_region; RefPtr<Region> m_kernel_stack_for_signal_handler_region;
pid_t m_waitee_pid { -1 }; pid_t m_waitee_pid { -1 };
int m_wait_options { 0 };
RefPtr<FileDescription> m_blocked_description; RefPtr<FileDescription> m_blocked_description;
timeval m_select_timeout; timeval m_select_timeout;
SignalActionData m_signal_action_data[32]; SignalActionData m_signal_action_data[32];

View file

@ -3,6 +3,10 @@
#include <AK/Types.h> #include <AK/Types.h>
#define WNOHANG 1 #define WNOHANG 1
#define WUNTRACED 2
#define WSTOPPED WUNTRACED
#define WEXITED 4
#define WCONTINUED 8
#define R_OK 4 #define R_OK 4
#define W_OK 2 #define W_OK 2

View file

@ -488,7 +488,7 @@ static int run_command(const String& cmd)
for (int i = 0; i < children.size(); ++i) { for (int i = 0; i < children.size(); ++i) {
auto& child = children[i]; auto& child = children[i];
do { do {
int rc = waitpid(child.pid, &wstatus, 0); int rc = waitpid(child.pid, &wstatus, WEXITED | WSTOPPED);
if (rc < 0 && errno != EINTR) { if (rc < 0 && errno != EINTR) {
if (errno != ECHILD) if (errno != ECHILD)
perror("waitpid"); perror("waitpid");
@ -499,6 +499,8 @@ static int run_command(const String& cmd)
printf("Shell: %s(%d) exited with status %d\n", child.name.characters(), child.pid, WEXITSTATUS(wstatus)); printf("Shell: %s(%d) exited with status %d\n", child.name.characters(), child.pid, WEXITSTATUS(wstatus));
if (i == 0) if (i == 0)
return_value = WEXITSTATUS(wstatus); return_value = WEXITSTATUS(wstatus);
} else if (WIFSTOPPED(wstatus)) {
printf("Shell: %s(%d) stopped.\n", child.name.characters(), child.pid);
} else { } else {
if (WIFSIGNALED(wstatus)) { if (WIFSIGNALED(wstatus)) {
printf("Shell: %s(%d) exited due to signal '%s'\n", child.name.characters(), child.pid, strsignal(WTERMSIG(wstatus))); printf("Shell: %s(%d) exited due to signal '%s'\n", child.name.characters(), child.pid, strsignal(WTERMSIG(wstatus)));