Index: sys/proc.h =================================================================== --- sys/proc.h (revision 231328) +++ sys/proc.h (working copy) @@ -507,8 +507,6 @@ struct proc { /* The following fields are all zeroed upon creation in fork. */ #define p_startzero p_oppid pid_t p_oppid; /* (c + e) Save ppid in ptrace. XXX */ - int p_dbg_child; /* (c + e) # of debugged children in - ptrace. */ struct vmspace *p_vmspace; /* (b) Address space. */ u_int p_swtick; /* (c) Tick when swapped in or out. */ struct itimerval p_realtimer; /* (c) Alarm timer. */ @@ -576,6 +574,11 @@ struct proc { after fork. */ uint64_t p_prev_runtime; /* (c) Resource usage accounting. */ struct racct *p_racct; /* (b) Resource accounting. */ + /* An orphan is process that has beed re-parented to the debugger + as a result of attaching to it. Need to keep track of tham to + be able to collect the exit status of what used to be children. */ + LIST_ENTRY(proc) p_orphan; /* (e) List of orphan processes. */ + LIST_HEAD(, proc) p_orphans; /* (e) Pointer to list of orphans. */ }; #define p_session p_pgrp->pg_session @@ -867,7 +870,7 @@ void procinit(void); void proc_linkup0(struct proc *p, struct thread *td); void proc_linkup(struct proc *p, struct thread *td); void proc_reap(struct thread *td, struct proc *p, int *status, int options, - struct rusage *rusage); + struct rusage *rusage, int child); void proc_reparent(struct proc *child, struct proc *newparent); struct pstats *pstats_alloc(void); void pstats_fork(struct pstats *src, struct pstats *dst); Index: kern/kern_exit.c =================================================================== --- kern/kern_exit.c (revision 231328) +++ kern/kern_exit.c (working copy) @@ -680,7 +680,7 @@ sys_wait4(struct thread *td, struct wait_args *uap */ void proc_reap(struct thread *td, struct proc *p, int *status, int options, - struct rusage *rusage) + struct rusage *rusage, int child) { struct proc *q, *t; @@ -720,7 +720,6 @@ proc_reap(struct thread *td, struct proc *p, int * if (p->p_oppid && (t = pfind(p->p_oppid)) != NULL) { PROC_LOCK(p); proc_reparent(p, t); - p->p_pptr->p_dbg_child--; p->p_oppid = 0; PROC_UNLOCK(p); pksignal(t, SIGCHLD, p->p_ksi); @@ -738,7 +737,10 @@ proc_reap(struct thread *td, struct proc *p, int * sx_xlock(&allproc_lock); LIST_REMOVE(p, p_list); /* off zombproc */ sx_xunlock(&allproc_lock); - LIST_REMOVE(p, p_sibling); + if (child) + LIST_REMOVE(p, p_sibling); + else + LIST_REMOVE(p, p_orphan); leavepgrp(p); #ifdef PROCDESC if (p->p_procdesc != NULL) @@ -803,12 +805,54 @@ proc_reap(struct thread *td, struct proc *p, int * sx_xunlock(&allproc_lock); } +static int +proc_to_reap (struct thread *td, struct proc *p, pid_t pid, int *status, + int options, struct rusage *rusage, int child) +{ + struct proc *q; + + q = td->td_proc; + PROC_LOCK(p); + if (pid != WAIT_ANY && + p->p_pid != pid && p->p_pgid != -pid) { + PROC_UNLOCK(p); + return (0); + } + if (p_canwait(td, p)) { + PROC_UNLOCK(p); + return (0); + } + + /* + * This special case handles a kthread spawned by linux_clone + * (see linux_misc.c). The linux_wait4 and linux_waitpid + * functions need to be able to distinguish between waiting + * on a process and waiting on a thread. It is a thread if + * p_sigparent is not SIGCHLD, and the WLINUXCLONE option + * signifies we want to wait for threads and not processes. + */ + if ((p->p_sigparent != SIGCHLD) ^ + ((options & WLINUXCLONE) != 0)) { + PROC_UNLOCK(p); + return (0); + } + + PROC_SLOCK(p); + if (p->p_state == PRS_ZOMBIE) { + proc_reap(td, p, status, options, rusage, child); + return (-1); + } + PROC_SUNLOCK(p); + PROC_UNLOCK(p); + return (1); +} + int kern_wait(struct thread *td, pid_t pid, int *status, int options, struct rusage *rusage) { struct proc *p, *q; - int error, nfound; + int error, nfound, ret; AUDIT_ARG_PID(pid); AUDIT_ARG_VALUE(options); @@ -831,37 +875,16 @@ loop: nfound = 0; sx_xlock(&proctree_lock); LIST_FOREACH(p, &q->p_children, p_sibling) { - PROC_LOCK(p); - if (pid != WAIT_ANY && - p->p_pid != pid && p->p_pgid != -pid) { - PROC_UNLOCK(p); + ret = proc_to_reap (td, p, pid, status, options, rusage, 1); + if (ret == 0) continue; - } - if (p_canwait(td, p)) { - PROC_UNLOCK(p); - continue; - } + else if (ret == 1) + nfound++; + else + return (0); - /* - * This special case handles a kthread spawned by linux_clone - * (see linux_misc.c). The linux_wait4 and linux_waitpid - * functions need to be able to distinguish between waiting - * on a process and waiting on a thread. It is a thread if - * p_sigparent is not SIGCHLD, and the WLINUXCLONE option - * signifies we want to wait for threads and not processes. - */ - if ((p->p_sigparent != SIGCHLD) ^ - ((options & WLINUXCLONE) != 0)) { - PROC_UNLOCK(p); - continue; - } - - nfound++; + PROC_LOCK(p); PROC_SLOCK(p); - if (p->p_state == PRS_ZOMBIE) { - proc_reap(td, p, status, options, rusage); - return (0); - } if ((p->p_flag & P_STOPPED_SIG) && (p->p_suspcount == p->p_numthreads) && (p->p_flag & P_WAITED) == 0 && @@ -893,16 +916,21 @@ loop: if (status) *status = SIGCONT; - return (0); } PROC_UNLOCK(p); } + LIST_FOREACH(p, &q->p_orphans, p_orphan) { + ret = proc_to_reap (td, p, pid, status, options, rusage, 0); + if (ret == 0) + continue; + else if (ret == 1) + nfound++; + else + return (0); + } if (nfound == 0) { sx_xunlock(&proctree_lock); - if (td->td_proc->p_dbg_child) - return (0); - else - return (ECHILD); + return (ECHILD); } if (options & WNOHANG) { sx_xunlock(&proctree_lock); @@ -929,6 +957,7 @@ loop: void proc_reparent(struct proc *child, struct proc *parent) { + struct proc *p; sx_assert(&proctree_lock, SX_XLOCKED); PROC_LOCK_ASSERT(child, MA_OWNED); @@ -940,5 +969,15 @@ proc_reparent(struct proc *child, struct proc *par PROC_UNLOCK(child->p_pptr); LIST_REMOVE(child, p_sibling); LIST_INSERT_HEAD(&parent->p_children, child, p_sibling); + + LIST_FOREACH(p, &parent->p_orphans, p_orphan) { + if (p == child) { + LIST_REMOVE(child, p_orphan); + break; + } + } + if (child->p_flag & P_TRACED) + LIST_INSERT_HEAD(&child->p_pptr->p_orphans, child, p_orphan); + child->p_pptr = parent; } Index: kern/kern_fork.c =================================================================== --- kern/kern_fork.c (revision 231328) +++ kern/kern_fork.c (working copy) @@ -590,6 +590,7 @@ do_fork(struct thread *td, int flags, struct proc LIST_INSERT_AFTER(p1, p2, p_pglist); PGRP_UNLOCK(p1->p_pgrp); LIST_INIT(&p2->p_children); + LIST_INIT(&p2->p_orphans); callout_init(&p2->p_itcallout, CALLOUT_MPSAFE); Index: kern/sys_process.c =================================================================== --- kern/sys_process.c (revision 231328) +++ kern/sys_process.c (working copy) @@ -841,8 +841,6 @@ kern_ptrace(struct thread *td, int req, pid_t pid, p->p_flag |= P_TRACED; p->p_oppid = p->p_pptr->p_pid; if (p->p_pptr != td->td_proc) { - /* Remember that a child is being debugged(traced). */ - p->p_pptr->p_dbg_child++; proc_reparent(p, td->td_proc); } data = SIGSTOP; @@ -931,7 +929,6 @@ kern_ptrace(struct thread *td, int req, pid_t pid, PROC_UNLOCK(pp); PROC_LOCK(p); proc_reparent(p, pp); - p->p_pptr->p_dbg_child--; if (pp == initproc) p->p_sigparent = SIGCHLD; } Index: kern/sys_procdesc.c =================================================================== --- kern/sys_procdesc.c (revision 231328) +++ kern/sys_procdesc.c (working copy) @@ -367,7 +367,7 @@ procdesc_close(struct file *fp, struct thread *td) * procdesc_reap(). */ PROC_SLOCK(p); - proc_reap(curthread, p, NULL, 0, NULL); + proc_reap(curthread, p, NULL, 0, NULL, 0); } else { /* * If the process is not yet dead, we need to kill it, but we