/* 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 #include #include #include #include "proc.h" #include #include #include #include #include #include #include "process_S.h" #include "process_reply_U.h" #include "ourmsg_U.h" #include /* 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. Check if the parent is waiting for us to exit; if so wake it up, otherwise, enter us as a zombie. */ void alert_parent (struct proc *p) { struct zombie *z; send_signal (p->p_parent->p_msgport, SIGCHLD, p->p_parent->p_task); if (!p->p_exiting) p->p_status = W_EXITCODE (0, SIGKILL); if (p->p_parent->p_waiting) { condition_broadcast (&p->p_parent->p_wakeup); p->p_parent->p_waiting = 0; } z = malloc (sizeof (struct zombie)); z->pid = p->p_pid; z->pgrp = p->p_pgrp->pg_pgid; z->parent = p->p_parent; z->exit_status = p->p_status; bzero (&z->ru, sizeof (struct rusage)); z->next = zombie_list; zombie_list = z; } /* Process P is exiting. Find all the zombies who claim P as their parent and make them claim startup_proc as their parent; then wake it up if appropriate. */ void reparent_zombies (struct proc *p) { struct zombie *z, *prevz; struct wait_c *w = &startup_proc->p_continuation.wait_c; int initsignalled = 0; for (z = zombie_list, prevz = 0; z; prevz = z, z = z->next) { if (z->parent != p) continue; z->parent = startup_proc; if (!initsignalled) { send_signal (startup_proc->p_msgport, SIGCHLD, startup_proc->p_task); initsignalled = 1; } if (startup_proc->p_waiting) { condition_broadcast (&startup_proc->p_wakeup); startup_proc->p_waiting = 0; } } } /* Implement proc_wait as described in . */ 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, struct rusage *ru, pid_t *pid_status) { struct wait_c *w; struct zombie *z, *prevz; start_over: for (z = zombie_list, prevz = 0; z; prevz = z, z = z->next) { if (z->parent == p && waiter_cares (pid, p->p_pgrp->pg_pgid, z->pid, z->pgrp)) { *status = z->exit_status; bzero (ru, sizeof (struct rusage)); *pid_status = z->pid; (prevz ? prevz->next : zombie_list) = z->next; free (z); return 0; } } /* 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 (pid); if (!child || child->p_parent != p) return ECHILD; if (child->p_stopped && !child->p_waited && ((options & WUNTRACED) || child->p_traced)) { child->p_waited = 1; *status = child->p_status; 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->p_stopped && !child->p_waited && ((options & WUNTRACED) || child->p_traced)) { child->p_waited = 1; *status = child->p_status; bzero (ru, sizeof (struct rusage)); *pid_status = child->p_pid; return 0; } } if (!had_a_match) return ECHILD; } if (options & WNOHANG) return EWOULDBLOCK; p->p_waiting = 1; condition_wait (&p->p_wakeup, &global_lock); goto start_over; } /* Implement proc_mark_stop as described in . */ kern_return_t S_proc_mark_stop (struct proc *p, int signo) { p->p_stopped = 1; p->p_status = W_STOPCODE (signo); 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 . */ kern_return_t S_proc_mark_exit (struct proc *p, int status) { if (WIFSTOPPED (status)) return EINVAL; p->p_exiting = 1; p->p_status = status; return 0; } /* Implement proc_mark_cont as described in . */ kern_return_t S_proc_mark_cont (struct proc *p) { p->p_stopped = 0; return 0; } /* Implement proc_mark_traced as described in . */ kern_return_t S_proc_mark_traced (struct proc *p) { p->p_traced = 1; return 0; } /* Implement proc_mark_nostopchild as described in . */ kern_return_t S_proc_mod_stopchild (struct proc *p, int value) { /* VALUE is nonzero if we should send SIGCHLD. */ p->p_nostopcld = ! value; return 0; } /* Return 1 if pid is in use by a zombie. */ int zombie_check_pid (pid_t pid) { struct zombie *z; for (z = zombie_list; z; z = z->next) if (z->pid == pid || -z->pid == pid || z->pgrp == pid || -z->pgrp == pid) return 1; return 0; }