Index: sys/kern/init_main.c =================================================================== --- sys/kern/init_main.c (revision 274384) +++ sys/kern/init_main.c (working copy) @@ -823,6 +823,7 @@ newcred = crget(); PROC_LOCK(initproc); initproc->p_flag |= P_SYSTEM | P_INMEM; + reaper_init(initproc); oldcred = initproc->p_ucred; crcopy(newcred, oldcred); #ifdef MAC Index: sys/kern/kern_exit.c =================================================================== --- sys/kern/kern_exit.c (revision 274384) +++ sys/kern/kern_exit.c (working copy) @@ -160,7 +160,7 @@ void exit1(struct thread *td, int rv) { - struct proc *p, *nq, *q, *t; + struct proc *p, *nq, *q, *t, *reproc; struct thread *tdt; struct vnode *ttyvp = NULL; @@ -443,15 +443,23 @@ WITNESS_WARN(WARN_PANIC, NULL, "process (pid %d) exiting", p->p_pid); + sx_xlock(&proctree_lock); /* - * Reparent all children processes: - * - traced ones to the original parent (or init if we are that parent) - * - the rest to init + * release controlled reaper for exit if we own it */ - sx_xlock(&proctree_lock); + reaper_exit(p); + + /* + * Reparent all of this process's children to the init process or + * to the designated reaper. + */ + reproc = NULL; q = LIST_FIRST(&p->p_children); - if (q != NULL) /* only need this if any child is S_ZOMB */ - wakeup(initproc); + if (q != NULL) { /* only need this if any child is S_ZOMB */ + reproc = reaper_get(p); + KASSERT(reproc != NULL, ("Null reaper process")); + wakeup(reproc); + } for (; q != NULL; q = nq) { nq = LIST_NEXT(q, p_sibling); PROC_LOCK(q); @@ -458,7 +466,7 @@ q->p_sigparent = SIGCHLD; if (!(q->p_flag & P_TRACED)) { - proc_reparent(q, initproc); + proc_reparent(q, reproc); } else { /* * Traced processes are killed since their existence @@ -466,7 +474,7 @@ */ t = proc_realparent(q); if (t == p) { - proc_reparent(q, initproc); + proc_reparent(q, reproc); } else { PROC_LOCK(t); proc_reparent(q, t); @@ -543,8 +551,10 @@ /* * Notify parent that we're gone. If parent has the * PS_NOCLDWAIT flag set, or if the handler is set to SIG_IGN, - * notify process 1 instead (and hope it will handle this - * situation). + * notify the reaper process instead (it will handle + * this situation). + * + * NOTE: The reaper can still be the parent process. */ PROC_LOCK(p->p_pptr); mtx_lock(&p->p_pptr->p_sigacts->ps_mtx); @@ -555,7 +565,9 @@ mtx_unlock(&p->p_pptr->p_sigacts->ps_mtx); pp = p->p_pptr; PROC_UNLOCK(pp); - proc_reparent(p, initproc); + if (reproc == NULL) + reproc = reaper_get(p); + proc_reparent(p, reproc); p->p_sigparent = SIGCHLD; PROC_LOCK(p->p_pptr); @@ -568,6 +580,9 @@ } else mtx_unlock(&p->p_pptr->p_sigacts->ps_mtx); + /* + * Signal (possibly new) parent. + */ if (p->p_pptr == initproc) kern_psignal(p->p_pptr, SIGCHLD); else if (p->p_sigparent != 0) { Index: sys/kern/kern_fork.c =================================================================== --- sys/kern/kern_fork.c (revision 274384) +++ sys/kern/kern_fork.c (working copy) @@ -66,7 +66,7 @@ #include #include #include -#include +#include #include #include #include @@ -607,11 +607,13 @@ * Attach the new process to its parent. * * If RFNOWAIT is set, the newly created process becomes a child - * of init. This effectively disassociates the child from the - * parent. + * of the reaper (typically init). This effectively disassociates + * the child from the parent. + * + * Temporarily hold pptr for the RFNOWAIT case to avoid ripouts. */ if (flags & RFNOWAIT) - pptr = initproc; + pptr = reaper_get(p1); else pptr = p1; p2->p_pptr = pptr; @@ -1038,3 +1040,40 @@ ktrsysret(SYS_fork, 0, 0); #endif } + +/* + * set the reaper flags + */ +void +reaper_init(struct proc *p) +{ + p->p_flag2 |= P2_REAPER_OWNED; + if (p == initproc) + p->p_flag2 |= P2_REAPER_REALINIT; +} + +/* + * Remove the reaper flags + */ +void +reaper_exit(struct proc *p) +{ + p->p_flag2 &= ~(P2_REAPER_OWNED|P2_REAPER_REALINIT); +} + +/* + * Return the process (self or parent) then hold the reaper flags + * The returned process's token is NOT acquired by this routine. + */ +struct proc * +reaper_get(struct proc *p) +{ + struct proc *p2; + + p2 = p; + while ((p2->p_flag2 & P2_REAPER_OWNED) != P2_REAPER_OWNED) { + p2 = p2->p_pptr; + } + + return (p2); +} Index: sys/kern/sys_process.c =================================================================== --- sys/kern/sys_process.c (revision 274384) +++ sys/kern/sys_process.c (working copy) @@ -1316,6 +1316,58 @@ return (0); } +static int +reaper(struct procctl_args *uap) +{ + struct proc *p, *p2; + int error; + union reaper_info udata; + + error = EINVAL; + + p = curproc; + + if (uap->idtype != P_PID || uap->id != (id_t)p->p_pid) + return (EINVAL); + + switch (uap->com) { + case PROC_REAP_ACQUIRE: + if ((p->p_flag2 & P2_REAPER_OWNED) == P2_REAPER_OWNED) + return (EINVAL); + PROC_LOCK(p); + reaper_init(p); + PROC_UNLOCK(p); + error = 0; + break; + case PROC_REAP_RELEASE: + if ((p->p_flag2 & P2_REAPER_OWNED) == 0) + return (EINVAL); + PROC_LOCK(p); + reaper_exit(p); + PROC_UNLOCK(p); + error = 0; + break; + case PROC_REAP_STATUS: + bzero(&udata, sizeof(udata)); + PROC_LOCK(p); + if ((p->p_flag2 & P2_REAPER_OWNED) == P2_REAPER_OWNED) + udata.status.flags |= REAPER_STAT_OWNED; + if ((p->p_flag2 & P2_REAPER_REALINIT) + == P2_REAPER_REALINIT) + udata.status.flags |= REAPER_STAT_REALINIT; + p2 = LIST_FIRST(&p->p_children); + udata.status.pid_head = p2 ? p2->p_pid : -1; + PROC_UNLOCK(p); + if (uap->data) + error = copyout(&udata, uap->data, + sizeof(udata.status)); + else + error = 0; + } + + return (error); +} + #ifndef _SYS_SYSPROTO_H_ struct procctl_args { idtype_t idtype; @@ -1338,6 +1390,10 @@ return (error); data = &flags; break; + case PROC_REAP_ACQUIRE: + case PROC_REAP_RELEASE: + case PROC_REAP_STATUS: + return (reaper(uap)); default: return (EINVAL); } Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h (revision 274384) +++ sys/sys/proc.h (working copy) @@ -649,6 +649,8 @@ /* These flags are kept in p_flag2. */ #define P2_INHERIT_PROTECTED 0x00000001 /* New children get P_PROTECTED. */ +#define P2_REAPER_OWNED 0x00000002 /* Is a reaper process */ +#define P2_REAPER_REALINIT 0x00000004 /* Is a reaper process and realinit */ /* Flags protected by proctree_lock, kept in p_treeflags. */ #define P_TREE_ORPHANED 0x00000001 /* Reparented, on orphan list */ @@ -960,6 +962,9 @@ int thread_unsuspend_one(struct thread *td); void thread_wait(struct proc *p); struct thread *thread_find(struct proc *p, lwpid_t tid); +void reaper_exit(struct proc *p); +void reaper_init(struct proc *p); +struct proc *reaper_get(struct proc *p); static __inline int curthread_pflags_set(int flags) Index: sys/sys/procctl.h =================================================================== --- sys/sys/procctl.h (revision 274384) +++ sys/sys/procctl.h (working copy) @@ -31,7 +31,13 @@ #define _SYS_PROCCTL_H_ #define PROC_SPROTECT 1 /* set protected state */ +#define PROC_REAP_ACQUIRE 2 /* acquire a reaper */ +#define PROC_REAP_RELEASE 3 /* release a reaper */ +#define PROC_REAP_STATUS 4 /* get the status of the reaper */ +#define REAPER_STAT_OWNED 0x00000001 +#define REAPER_STAT_REALINIT 0x00000002 + /* Operations for PROC_SPROTECT (passed in integer arg). */ #define PPROT_OP(x) ((x) & 0xf) #define PPROT_SET 1 @@ -42,6 +48,18 @@ #define PPROT_DESCEND 0x10 #define PPROT_INHERIT 0x20 +struct reaper_status { + uint32_t flags; + uint32_t refs; + long reserved1[15]; + pid_t pid_head; + int reserved2[15]; +}; + +union reaper_info { + struct reaper_status status; +}; + #ifndef _KERNEL #include #include