diff options
Diffstat (limited to 'proc/wait.c')
-rw-r--r-- | proc/wait.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/proc/wait.c b/proc/wait.c new file mode 100644 index 00000000..60ec58ff --- /dev/null +++ b/proc/wait.c @@ -0,0 +1,235 @@ +/* Implementation of wait + Copyright (C) 1994, 1995, 1996 Free Software Foundation + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <mach.h> +#include <sys/types.h> +#include <hurd/hurd_types.h> +#include <sys/resource.h> + +#include "proc.h" + +#include <signal.h> +#include <string.h> +#include <sys/wait.h> +#include <errno.h> +#include <stdlib.h> +#include <assert.h> + +#include "process_S.h" +#include <mach/mig_errors.h> + +#define EWOULDBLOCK EAGAIN /* XXX */ + +/* Return nonzero if a `waitpid' on WAIT_PID by a process + in MYPGRP cares about the death of PID/PGRP. */ +static inline int +waiter_cares (pid_t wait_pid, pid_t mypgrp, + pid_t pid, pid_t pgrp) +{ + return (wait_pid == pid || + wait_pid == -pgrp || + wait_pid == WAIT_ANY || + (wait_pid == WAIT_MYPGRP && pgrp == mypgrp)); +} + +/* A process is dying. Send SIGCHLD to the parent. Wake the parent +if it is waiting for us to exit. */ +void +alert_parent (struct proc *p) +{ + send_signal (p->p_parent->p_msgport, SIGCHLD, p->p_parent->p_task); + + if (!p->p_exiting) + { + p->p_status = W_EXITCODE (0, SIGKILL); + p->p_sigcode = -1; + } + + if (p->p_parent->p_waiting) + { + condition_broadcast (&p->p_parent->p_wakeup); + p->p_parent->p_waiting = 0; + } +} + +kern_return_t +S_proc_wait (struct proc *p, + mach_port_t reply_port, + mach_msg_type_name_t reply_port_type, + pid_t pid, + int options, + int *status, + int *sigcode, + struct rusage *ru, + pid_t *pid_status) +{ + int cancel; + + int child_ready (struct proc *child) + { + if (child->p_waited) + return 0; + if (child->p_dead) + return 1; + if (!child->p_stopped) + return 0; + if (child->p_traced || (options & WUNTRACED)) + return 1; + return 0; + } + + if (!p) + return EOPNOTSUPP; + + start_over: + /* See if we can satisfy the request with a stopped + child; also check for invalid arguments here. */ + if (!p->p_ochild) + return ECHILD; + + if (pid > 0) + { + struct proc *child = pid_find_allow_zombie (pid); + if (!child || child->p_parent != p) + return ECHILD; + if (child_ready (child)) + { + child->p_waited = 1; + *status = child->p_status; + *sigcode = child->p_sigcode; + if (child->p_dead) + complete_exit (child); + bzero (ru, sizeof (struct rusage)); + *pid_status = pid; + return 0; + } + } + else + { + struct proc *child; + int had_a_match = pid == 0; + + for (child = p->p_ochild; child; child = child->p_sib) + if (waiter_cares (pid, p->p_pgrp->pg_pgid, + child->p_pid, child->p_pgrp->pg_pgid)) + { + had_a_match = 1; + if (child_ready (child)) + { + child->p_waited = 1; + *status = child->p_status; + *sigcode = child->p_sigcode; + *pid_status = child->p_pid; + if (child->p_dead) + complete_exit (child); + bzero (ru, sizeof (struct rusage)); + return 0; + } + } + + if (!had_a_match) + return ECHILD; + } + + if (options & WNOHANG) + return EWOULDBLOCK; + + p->p_waiting = 1; + cancel = hurd_condition_wait (&p->p_wakeup, &global_lock); + if (p->p_dead) + return EOPNOTSUPP; + if (cancel) + return EINTR; + goto start_over; +} + +/* Implement proc_mark_stop as described in <hurd/proc.defs>. */ +kern_return_t +S_proc_mark_stop (struct proc *p, + int signo, + int sigcode) +{ + if (!p) + return EOPNOTSUPP; + + p->p_stopped = 1; + p->p_status = W_STOPCODE (signo); + p->p_sigcode = sigcode; + p->p_waited = 0; + + if (p->p_parent->p_waiting) + { + condition_broadcast (&p->p_parent->p_wakeup); + p->p_parent->p_waiting = 0; + } + + if (!p->p_parent->p_nostopcld) + send_signal (p->p_parent->p_msgport, SIGCHLD, p->p_parent->p_task); + + return 0; +} + +/* Implement proc_mark_exit as described in <hurd/proc.defs>. */ +kern_return_t +S_proc_mark_exit (struct proc *p, + int status, + int sigcode) +{ + if (!p) + return EOPNOTSUPP; + + if (WIFSTOPPED (status)) + return EINVAL; + + p->p_exiting = 1; + p->p_status = status; + p->p_sigcode = sigcode; + return 0; +} + +/* Implement proc_mark_cont as described in <hurd/proc.defs>. */ +kern_return_t +S_proc_mark_cont (struct proc *p) +{ + if (!p) + return EOPNOTSUPP; + p->p_stopped = 0; + return 0; +} + +/* Implement proc_mark_traced as described in <hurd/proc.defs>. */ +kern_return_t +S_proc_mark_traced (struct proc *p) +{ + if (!p) + return EOPNOTSUPP; + p->p_traced = 1; + return 0; +} + +/* Implement proc_mark_nostopchild as described in <hurd/proc.defs>. */ +kern_return_t +S_proc_mod_stopchild (struct proc *p, + int value) +{ + if (!p) + return EOPNOTSUPP; + /* VALUE is nonzero if we should send SIGCHLD. */ + p->p_nostopcld = ! value; + return 0; +} + |