]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/kern_exit.c
xnu-344.49.tar.gz
[apple/xnu.git] / bsd / kern / kern_exit.c
index 385c637111c6f63c7db3480eb1b08e7cf1e964d0..25dc9ef7122edf290c3150770e7c49bf5771c637 100644 (file)
@@ -1,21 +1,24 @@
 /*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2001 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
  * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
  * 
  * @APPLE_LICENSE_HEADER_END@
  */
 #include <mach/mach_types.h>
 #include <kern/thread.h>
 #include <kern/thread_act.h>
+#include <kern/sched_prim.h>
 #include <kern/assert.h>
+#if KTRACE   
+#include <sys/ktrace.h>
+#endif
 
 extern char init_task_failure_data[];
-void exit1 __P((struct proc *, int));
+int exit1 __P((struct proc *, int, int *));
 
 /*
  * exit --
@@ -104,14 +111,14 @@ exit(p, uap, retval)
        struct exit_args *uap;
        int *retval;
 {
-       exit1(p, W_EXITCODE(uap->rval, 0));
+       exit1(p, W_EXITCODE(uap->rval, 0), retval);
 
-       /* drop funnel befewo we return */
+       /* drop funnel before we return */
        thread_funnel_set(kernel_flock, FALSE);
        thread_exception_return();
        /* NOTREACHED */
        while (TRUE)
-               thread_block(0);
+               thread_block(THREAD_CONTINUE_NULL);
        /* NOTREACHED */
 }
 
@@ -120,10 +127,11 @@ exit(p, uap, retval)
  * to zombie, and unlink proc from allproc and parent's lists.  Save exit
  * status and rusage for wait().  Check for child processes and orphan them.
  */
-void
-exit1(p, rv)
+int
+exit1(p, rv, retval)
        register struct proc *p;
        int rv;
+       int * retval;
 {
        register struct proc *q, *nq;
        thread_t self = current_thread();
@@ -137,12 +145,20 @@ exit1(p, rv)
         * called exit(), then halt any others
         * right here.
         */
+
+        ut = get_bsdthread_info(th_act_self);
+        if (ut->uu_flag & P_VFORK) {
+                       (void)vfork_exit(p, rv);
+                       vfork_return(th_act_self, p->p_pptr, p , retval);
+                       unix_syscall_return(0);
+                       /* NOT REACHED */
+        }
         signal_lock(p);
        while (p->exit_thread != self) {
                if (sig_try_locked(p) <= 0) {
                        if (get_threadtask(th_act_self) != task) {
                                 signal_unlock(p);
-                               return;
+                               return(0);
                         }
                        signal_unlock(p);
                        thread_terminate(th_act_self);
@@ -163,7 +179,7 @@ exit1(p, rv)
        s = splsched();
        p->p_flag |= P_WEXIT;
        splx(s);
-       proc_prepareexit(p);
+       (void)proc_prepareexit(p);
        p->p_xstat = rv;
 
        /* task terminate will call proc_terminate and that cleans it up */
@@ -184,6 +200,7 @@ exit1(p, rv)
                /*NOTREACHED*/
        }
 #endif
+       return(0);
 }
 
 void
@@ -194,7 +211,6 @@ proc_prepareexit(struct proc *p)
        thread_t self = current_thread();
        thread_act_t th_act_self = current_act();
 
-
        /*
         * Remove proc from allproc queue and from pidhash chain.
         * Need to do this before we do anything that can block.
@@ -215,20 +231,18 @@ proc_prepareexit(struct proc *p)
        p->p_sigignore = ~0;
        p->p_siglist = 0;
        ut = get_bsdthread_info(th_act_self);
-       ut->uu_sig = 0;
-       untimeout(realitexpire, (caddr_t)p);
-
+       ut->uu_siglist = 0;
+       untimeout(realitexpire, (caddr_t)p->p_pid);
 }
 
 void 
 proc_exit(struct proc *p)
 {
-       register struct proc *q, *nq;
+       register struct proc *q, *nq, *pp;
        thread_t self = current_thread();
        thread_act_t th_act_self = current_act();
        struct task *task = p->task;
        register int i,s;
-       struct uthread *ut;
        boolean_t funnel_state;
 
        /* This can happen if thread_terminate of the single thread
@@ -255,11 +269,15 @@ proc_exit(struct proc *p)
        /* Close ref SYSV Shared memory*/
        if (p->vm_shm)
                shmexit(p);
+       /* Release SYSV semaphores */
+       semexit(p);
        
        if (SESS_LEADER(p)) {
                register struct session *sp = p->p_session;
 
                if (sp->s_ttyvp) {
+                       struct vnode *ttyvp;
+
                        /*
                         * Controlling process.
                         * Signal foreground pgrp,
@@ -277,9 +295,10 @@ proc_exit(struct proc *p)
                                if (sp->s_ttyvp)
                                        VOP_REVOKE(sp->s_ttyvp, REVOKEALL);
                        }
-                       if (sp->s_ttyvp)
-                               vrele(sp->s_ttyvp);
+                       ttyvp = sp->s_ttyvp;
                        sp->s_ttyvp = NULL;
+                       if (ttyvp)
+                               vrele(ttyvp);
                        /*
                         * s_ttyp is not zero'd; we use this to indicate
                         * that the session once had a controlling terminal.
@@ -291,16 +310,20 @@ proc_exit(struct proc *p)
 
        fixjobc(p, p->p_pgrp, 0);
        p->p_rlimit[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
+       (void)acct_process(p);
+
 #if KTRACE
        /* 
         * release trace file
         */
        p->p_traceflag = 0;     /* don't trace the vrele() */
-       if (p->p_tracep)
-               vrele(p->p_tracep);
+       if (p->p_tracep) {
+               struct vnode *tvp = p->p_tracep;
+               p->p_tracep = NULL;
+               vrele(tvp);
+       }
 #endif
 
-
        q = p->p_children.lh_first;
        if (q)          /* only need this if any child is S_ZOMB */
                wakeup((caddr_t) initproc);
@@ -314,7 +337,9 @@ proc_exit(struct proc *p)
                if (q->p_flag & P_TRACED) {
                        q->p_flag &= ~P_TRACED;
                        if (q->sigwait_thread) {
-                               thread_t sig_shuttle  = getshuttle_thread(q->sigwait_thread);
+                               thread_t sig_shuttle;
+
+                               sig_shuttle = (thread_t)getshuttle_thread((thread_act_t)q->sigwait_thread);
                                /*
                                 * The sigwait_thread could be stopped at a
                                 * breakpoint. Wake it up to kill.
@@ -322,15 +347,14 @@ proc_exit(struct proc *p)
                                 * the first thread in the task. So any attempts to kill
                                 * the process would result into a deadlock on q->sigwait.
                                 */
-                               thread_resume((struct thread *)q->sigwait_thread);
+                               thread_resume((thread_act_t)q->sigwait_thread);
                                clear_wait(sig_shuttle, THREAD_INTERRUPTED);
-                               threadsignal(q->sigwait_thread, SIGKILL, 0);
+                               threadsignal((thread_act_t)q->sigwait_thread, SIGKILL, 0);
                        }
                        psignal(q, SIGKILL);
                }
        }
 
-
        /*
         * Save exit status and final rusage info, adding in child rusage
         * info and self times.
@@ -366,7 +390,6 @@ proc_exit(struct proc *p)
                timeradd(&st,&p->p_ru->ru_stime,&p->p_ru->ru_stime);
        }
 
-
        ruadd(p->p_ru, &p->p_stats->p_cru);
 
        /*
@@ -410,7 +433,24 @@ proc_exit(struct proc *p)
        /*
         * Notify parent that we're gone.
         */
-       psignal(p->p_pptr, SIGCHLD);
+       if (p->p_pptr->p_flag & P_NOCLDWAIT) {
+               struct proc * pp = p->p_pptr;
+
+               proc_reparent(p, initproc);
+               /* If there are no more children wakeup parent */
+               if (LIST_EMPTY(&pp->p_children))
+                       wakeup((caddr_t)pp);
+       }
+       /* should be fine as parent proc would be initproc */
+       pp = p->p_pptr;
+       if (pp != initproc) {
+               pp->si_pid = p->p_pid;
+               pp->si_status = p->p_xstat;
+               pp->si_code = CLD_EXITED;
+               pp->si_uid = p->p_cred->p_ruid;
+       }
+       psignal(pp, SIGCHLD);
+
 
        /* Place onto zombproc. */
        LIST_INSERT_HEAD(&zombproc, p, p_list);
@@ -454,7 +494,6 @@ wait4(p, uap, retval)
        struct wait4_args *uap;
        int *retval;
 {
-
        return (wait1(p, uap, retval, 0));
 }
 
@@ -489,23 +528,18 @@ owait3(p, uap, retval)
 int
 wait1continue(result)
 {
-  void *vt;
-  thread_act_t thread;
-  struct uthread *ut;
-  int *retval;
-  struct proc *p;
-
-       p = get_bsdtask_info(current_task());
-       p->p_flag &= ~P_WAITING;
+       void *vt;
+       thread_act_t thread;
+       int *retval;
+       struct proc *p;
 
-      if (result != 0) {
-         return(result);
-       }
+       if (result)
+               return(result);
 
-    thread = current_act();
-       ut = get_bsdthread_info(thread);
-       vt = get_bsduthreadarg(thread);
-       retval = get_bsduthreadrval(thread);
+       p = current_proc();
+       thread = current_act();
+       vt = (void *)get_bsduthreadarg(thread);
+       retval = (int *)get_bsduthreadrval(thread);
        wait1((struct proc *)p, (struct wait4_args *)vt, retval, 0);
 }
 
@@ -521,16 +555,9 @@ wait1(q, uap, retval, compat)
        register int nfound;
        register struct proc *p, *t;
        int status, error;
+       struct vnode *tvp;
 
-
-#if 0
-       /* since we are funneled we don't need to do this atomically, yet */
-       if (q->p_flag & P_WAITING) {
-         return(EINVAL);
-       }
-       q->p_flag |= P_WAITING;   /* only allow single thread to wait() */
-#endif
-
+retry:
        if (uap->pid == 0)
                uap->pid = -q->p_pgid;
 
@@ -542,6 +569,12 @@ loop:
                    p->p_pgid != -(uap->pid))
                        continue;
                nfound++;
+               if (p->p_flag & P_WAITING) {
+                       (void)tsleep(&p->p_stat, PWAIT, "waitcoll", 0);
+                       goto loop;
+               }
+               p->p_flag |= P_WAITING;   /* only allow single thread to wait() */
+
                if (p->p_stat == SZOMB) {
                        retval[0] = p->p_pid;
 #if COMPAT_43
@@ -554,7 +587,8 @@ loop:
                                if (error = copyout((caddr_t)&status,
                                    (caddr_t)uap->status,
                                                    sizeof(status))) {
-                                       q->p_flag &= ~P_WAITING;
+                                       p->p_flag &= ~P_WAITING;
+                                       wakeup(&p->p_stat);
                                        return (error);
                                }
                        }
@@ -562,7 +596,8 @@ loop:
                            (error = copyout((caddr_t)p->p_ru,
                            (caddr_t)uap->rusage,
                                             sizeof (struct rusage)))) {
-                                       q->p_flag &= ~P_WAITING;
+                               p->p_flag &= ~P_WAITING;
+                               wakeup(&p->p_stat);
                                return (error);
                        }
                        /*
@@ -572,9 +607,16 @@ loop:
                        if (p->p_oppid && (t = pfind(p->p_oppid))) {
                                p->p_oppid = 0;
                                proc_reparent(p, t);
+                               if (t != initproc) {
+                                       t->si_pid = p->p_pid;
+                                       t->si_status = p->p_xstat;
+                                       t->si_code = CLD_CONTINUED;
+                                       t->si_uid = p->p_cred->p_ruid;
+                               }
                                psignal(t, SIGCHLD);
                                wakeup((caddr_t)t);
-                                       q->p_flag &= ~P_WAITING;
+                               p->p_flag &= ~P_WAITING;
+                               wakeup(&p->p_stat);
                                return (0);
                        }
                        p->p_xstat = 0;
@@ -610,8 +652,10 @@ loop:
                        /*
                         * Release reference to text vnode
                         */
-                       if (p->p_textvp)
-                               vrele(p->p_textvp);
+                       tvp = p->p_textvp;
+                       p->p_textvp = NULL;
+                       if (tvp)
+                               vrele(tvp);
 
                        /*
                         * Finally finished with old proc entry.
@@ -620,9 +664,10 @@ loop:
                        leavepgrp(p);
                        LIST_REMOVE(p, p_list); /* off zombproc */
                        LIST_REMOVE(p, p_sibling);
+                       p->p_flag &= ~P_WAITING;
                        FREE_ZONE(p, sizeof *p, M_PROC);
                        nprocs--;
-                                       q->p_flag &= ~P_WAITING;
+                       wakeup(&p->p_stat);
                        return (0);
                }
                if (p->p_stat == SSTOP && (p->p_flag & P_WAITED) == 0 &&
@@ -642,24 +687,24 @@ loop:
                                    sizeof(status));
                        } else
                                error = 0;
-                                       q->p_flag &= ~P_WAITING;
+                       p->p_flag &= ~P_WAITING;
+                       wakeup(&p->p_stat);
                        return (error);
                }
+               p->p_flag &= ~P_WAITING;
+               wakeup(&p->p_stat);
        }
-       if (nfound == 0) {
-                                       q->p_flag &= ~P_WAITING;
+       if (nfound == 0)
                return (ECHILD);
-       }
+
        if (uap->options & WNOHANG) {
                retval[0] = 0;
-                                       q->p_flag &= ~P_WAITING;
                return (0);
        }
 
-       if (error = tsleep0((caddr_t)q, PWAIT | PCATCH, "wait", 0, wait1continue)) {
-                                       q->p_flag &= ~P_WAITING;
+       if (error = tsleep0((caddr_t)q, PWAIT | PCATCH, "wait", 0, wait1continue))
                return (error);
-       }
+
        goto loop;
 }
 
@@ -680,13 +725,13 @@ proc_reparent(child, parent)
        child->p_pptr = parent;
 }
 
-kern_return_t
-init_process(void)
 /*
  *     Make the current process an "init" process, meaning
  *     that it doesn't have a parent, and that it won't be
  *     gunned down by kill(-1, 0).
  */
+kern_return_t
+init_process(void)
 {
        register struct proc *p = current_proc();
 
@@ -715,7 +760,279 @@ process_terminate_self(void)
        struct proc *p = current_proc();
 
        if (p != NULL) {
-               exit1(p, W_EXITCODE(0, SIGKILL));
+               exit1(p, W_EXITCODE(0, SIGKILL), (int *)NULL);
                /*NOTREACHED*/
        }
 }
+
+/*
+ * Exit: deallocate address space and other resources, change proc state
+ * to zombie, and unlink proc from allproc and parent's lists.  Save exit
+ * status and rusage for wait().  Check for child processes and orphan them.
+ */
+
+void
+vfork_exit(p, rv)
+       struct proc *p;
+       int rv;
+{
+       register struct proc *q, *nq;
+       thread_t self = current_thread();
+       thread_act_t th_act_self = current_act();
+       struct task *task = p->task;
+       register int i,s;
+       struct uthread *ut;
+
+       /*
+        * If a thread in this task has already
+        * called exit(), then halt any others
+        * right here.
+        */
+
+        ut = get_bsdthread_info(th_act_self);
+#ifdef FIXME
+        signal_lock(p);
+       while (p->exit_thread != self) {
+               if (sig_try_locked(p) <= 0) {
+                       if (get_threadtask(th_act_self) != task) {
+                                signal_unlock(p);
+                               return;
+                        }
+                       signal_unlock(p);
+                       thread_terminate(th_act_self);
+                       thread_funnel_set(kernel_flock, FALSE);
+                       thread_exception_return();
+                       /* NOTREACHED */
+               }
+               sig_lock_to_exit(p);
+       }
+        signal_unlock(p);
+       if (p->p_pid == 1) {
+               printf("pid 1 exited (signal %d, exit %d)",
+                   WTERMSIG(rv), WEXITSTATUS(rv));
+panic("init died\nState at Last Exception:\n\n%s", init_task_failure_data);
+       }
+#endif /* FIXME */
+
+       s = splsched();
+       p->p_flag |= P_WEXIT;
+       splx(s);
+       /*
+        * Remove proc from allproc queue and from pidhash chain.
+        * Need to do this before we do anything that can block.
+        * Not doing causes things like mount() find this on allproc
+        * in partially cleaned state.
+        */
+       LIST_REMOVE(p, p_list);
+       LIST_REMOVE(p, p_hash);
+       /*
+        * If parent is waiting for us to exit or exec,
+        * P_PPWAIT is set; we will wakeup the parent below.
+        */
+       p->p_flag &= ~(P_TRACED | P_PPWAIT);
+       p->p_sigignore = ~0;
+       p->p_siglist = 0;
+
+       ut->uu_siglist = 0;
+       untimeout(realitexpire, (caddr_t)p->p_pid);
+
+       p->p_xstat = rv;
+
+       (void)vproc_exit(p);
+}
+
+void 
+vproc_exit(struct proc *p)
+{
+       register struct proc *q, *nq, *pp;
+       thread_t self = current_thread();
+       thread_act_t th_act_self = current_act();
+       struct task *task = p->task;
+       register int i,s;
+       boolean_t funnel_state;
+
+       MALLOC_ZONE(p->p_ru, struct rusage *,
+                       sizeof (*p->p_ru), M_ZOMBIE, M_WAITOK);
+
+       /*
+        * Close open files and release open-file table.
+        * This may block!
+        */
+       fdfree(p);
+
+       if (SESS_LEADER(p)) {
+               register struct session *sp = p->p_session;
+
+               if (sp->s_ttyvp) {
+                       struct vnode *ttyvp;
+
+                       /*
+                        * Controlling process.
+                        * Signal foreground pgrp,
+                        * drain controlling terminal
+                        * and revoke access to controlling terminal.
+                        */
+                       if (sp->s_ttyp->t_session == sp) {
+                               if (sp->s_ttyp->t_pgrp)
+                                       pgsignal(sp->s_ttyp->t_pgrp, SIGHUP, 1);
+                               (void) ttywait(sp->s_ttyp);
+                               /*
+                                * The tty could have been revoked
+                                * if we blocked.
+                                */
+                               if (sp->s_ttyvp)
+                                       VOP_REVOKE(sp->s_ttyvp, REVOKEALL);
+                       }
+                       ttyvp = sp->s_ttyvp;
+                       sp->s_ttyvp = NULL;
+                       if (ttyvp)
+                               vrele(ttyvp);
+                       /*
+                        * s_ttyp is not zero'd; we use this to indicate
+                        * that the session once had a controlling terminal.
+                        * (for logging and informational purposes)
+                        */
+               }
+               sp->s_leader = NULL;
+       }
+
+       fixjobc(p, p->p_pgrp, 0);
+       p->p_rlimit[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
+
+#if KTRACE
+       /* 
+        * release trace file
+        */
+       p->p_traceflag = 0;     /* don't trace the vrele() */
+       if (p->p_tracep) {
+               struct vnode *tvp = p->p_tracep;
+               p->p_tracep = NULL;
+               vrele(tvp);
+       }
+#endif
+
+       q = p->p_children.lh_first;
+       if (q)          /* only need this if any child is S_ZOMB */
+               wakeup((caddr_t) initproc);
+       for (; q != 0; q = nq) {
+               nq = q->p_sibling.le_next;
+               proc_reparent(q, initproc);
+               /*
+                * Traced processes are killed
+                * since their existence means someone is messing up.
+                */
+               if (q->p_flag & P_TRACED) {
+                       q->p_flag &= ~P_TRACED;
+                       if (q->sigwait_thread) {
+                               thread_t sig_shuttle;
+
+                               sig_shuttle = (thread_t) getshuttle_thread((thread_act_t)q->sigwait_thread);
+                               /*
+                                * The sigwait_thread could be stopped at a
+                                * breakpoint. Wake it up to kill.
+                                * Need to do this as it could be a thread which is not
+                                * the first thread in the task. So any attempts to kill
+                                * the process would result into a deadlock on q->sigwait.
+                                */
+                               thread_resume((thread_act_t)q->sigwait_thread);
+                               clear_wait(sig_shuttle, THREAD_INTERRUPTED);
+                               threadsignal((thread_act_t)q->sigwait_thread, SIGKILL, 0);
+                       }
+                       psignal(q, SIGKILL);
+               }
+       }
+
+       /*
+        * Save exit status and final rusage info, adding in child rusage
+        * info and self times.
+        */
+       *p->p_ru = p->p_stats->p_ru;
+
+       timerclear(&p->p_ru->ru_utime);
+       timerclear(&p->p_ru->ru_stime);
+
+#ifdef  FIXME
+       if (task) {
+               task_basic_info_data_t tinfo;
+               task_thread_times_info_data_t ttimesinfo;
+               int task_info_stuff, task_ttimes_stuff;
+               struct timeval ut,st;
+
+               task_info_stuff = TASK_BASIC_INFO_COUNT;
+               task_info(task, TASK_BASIC_INFO,
+                         &tinfo, &task_info_stuff);
+               p->p_ru->ru_utime.tv_sec = tinfo.user_time.seconds;
+               p->p_ru->ru_utime.tv_usec = tinfo.user_time.microseconds;
+               p->p_ru->ru_stime.tv_sec = tinfo.system_time.seconds;
+               p->p_ru->ru_stime.tv_usec = tinfo.system_time.microseconds;
+
+               task_ttimes_stuff = TASK_THREAD_TIMES_INFO_COUNT;
+               task_info(task, TASK_THREAD_TIMES_INFO,
+                         &ttimesinfo, &task_ttimes_stuff);
+
+               ut.tv_sec = ttimesinfo.user_time.seconds;
+               ut.tv_usec = ttimesinfo.user_time.microseconds;
+               st.tv_sec = ttimesinfo.system_time.seconds;
+               st.tv_usec = ttimesinfo.system_time.microseconds;
+               timeradd(&ut,&p->p_ru->ru_utime,&p->p_ru->ru_utime);
+               timeradd(&st,&p->p_ru->ru_stime,&p->p_ru->ru_stime);
+       }
+#endif /* FIXME */
+
+       ruadd(p->p_ru, &p->p_stats->p_cru);
+
+       /*
+        * Free up profiling buffers.
+        */
+       {
+               struct uprof *p0 = &p->p_stats->p_prof, *p1, *pn;
+
+               p1 = p0->pr_next;
+               p0->pr_next = NULL;
+               p0->pr_scale = 0;
+
+               for (; p1 != NULL; p1 = pn) {
+                       pn = p1->pr_next;
+                       kfree((vm_offset_t)p1, sizeof *p1);
+               }
+       }
+
+       /*
+        * Other substructures are freed from wait().
+        */
+       FREE_ZONE(p->p_stats, sizeof *p->p_stats, M_SUBPROC);
+       p->p_stats = NULL;
+
+       FREE_ZONE(p->p_sigacts, sizeof *p->p_sigacts, M_SUBPROC);
+       p->p_sigacts = NULL;
+
+       if (--p->p_limit->p_refcnt == 0)
+               FREE_ZONE(p->p_limit, sizeof *p->p_limit, M_SUBPROC);
+       p->p_limit = NULL;
+
+       /*
+        * Finish up by terminating the task
+        * and halt this thread (only if a
+        * member of the task exiting).
+        */
+       p->task = TASK_NULL;
+
+       /*
+        * Notify parent that we're gone.
+        */
+       pp = p->p_pptr;
+       if (pp != initproc) {
+               pp->si_pid = p->p_pid;
+               pp->si_status = p->p_xstat;
+               pp->si_code = CLD_EXITED;
+               pp->si_uid = p->p_cred->p_ruid;
+       }
+       psignal(p->p_pptr, SIGCHLD);
+
+       /* Place onto zombproc. */
+       LIST_INSERT_HEAD(&zombproc, p, p_list);
+       p->p_stat = SZOMB;
+
+       /* and now wakeup the parent */
+       wakeup((caddr_t)p->p_pptr);
+}