fix bug that can cause recursive .forward files to fail
[unix-history] / usr / src / sys / kern / kern_exit.c
index 80cfe16..7aa1695 100644 (file)
@@ -1,78 +1,90 @@
 /*
 /*
- * Copyright (c) 1982, 1986, 1989, 1991 Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1986, 1989, 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)kern_exit.c 7.31 (Berkeley) %G%
+ *     @(#)kern_exit.c 8.2 (Berkeley) %G%
  */
 
  */
 
-#include "param.h"
-#include "systm.h"
-#include "map.h"
-#include "ioctl.h"
-#include "tty.h"
-#include "time.h"
-#include "resource.h"
-#include "user.h"
-#include "kernel.h"
-#include "proc.h"
-#include "buf.h"
-#include "wait.h"
-#include "file.h"
-#include "vnode.h"
-#include "syslog.h"
-#include "malloc.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/map.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#include <sys/tty.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/kernel.h>
+#include <sys/buf.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/syslog.h>
+#include <sys/malloc.h>
+#include <sys/resourcevar.h>
+#include <sys/ptrace.h>
 
 
+#include <machine/cpu.h>
 #ifdef COMPAT_43
 #ifdef COMPAT_43
-#include "machine/reg.h"
-#include "machine/psl.h"
+#include <machine/reg.h>
+#include <machine/psl.h>
 #endif
 
 #endif
 
-#include "vm/vm_param.h"
-#include "vm/vm_map.h"
-#include "vm/vm_kern.h"
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+
+__dead void cpu_exit __P((struct proc *));
+__dead void exit1 __P((struct proc *, int));
 
 /*
 
 /*
- * Exit system call: pass back caller's arg
+ * exit --
+ *     Death of process.
  */
  */
-/* ARGSUSED */
-rexit(p, uap, retval)
+struct rexit_args {
+       int     rval;
+};
+__dead void
+exit(p, uap, retval)
        struct proc *p;
        struct proc *p;
-       struct args {
-               int     rval;
-       } *uap;
+       struct rexit_args *uap;
        int *retval;
 {
 
        int *retval;
 {
 
-       exit(p, W_EXITCODE(uap->rval, 0));
+       exit1(p, W_EXITCODE(uap->rval, 0));
        /* NOTREACHED */
 }
 
 /*
        /* 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.
+ * 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.
  */
  */
-exit(p, rv)
+__dead void
+exit1(p, rv)
        register struct proc *p;
        int rv;
 {
        register struct proc *q, *nq;
        register struct proc **pp;
        register struct proc *p;
        int rv;
 {
        register struct proc *q, *nq;
        register struct proc **pp;
+       register struct vmspace *vm;
        int s;
 
        int s;
 
+       if (p->p_pid == 1)
+               panic("init died (signal %d, exit %d)",
+                   WTERMSIG(rv), WEXITSTATUS(rv));
 #ifdef PGINPROF
        vmsizmon();
 #endif
 #ifdef PGINPROF
        vmsizmon();
 #endif
+       if (p->p_flag & SPROFIL)
+               stopprofclock(p);
        MALLOC(p->p_ru, struct rusage *, sizeof(struct rusage),
                M_ZOMBIE, M_WAITOK);
        /*
         * If parent is waiting for us to exit or exec,
         * SPPWAIT is set; we will wakeup the parent below.
         */
        MALLOC(p->p_ru, struct rusage *, sizeof(struct rusage),
                M_ZOMBIE, M_WAITOK);
        /*
         * If parent is waiting for us to exit or exec,
         * SPPWAIT is set; we will wakeup the parent below.
         */
-       p->p_flag &= ~(STRC|SULOCK|SPPWAIT);
+       p->p_flag &= ~(STRC|SPPWAIT);
        p->p_flag |= SWEXIT;
        p->p_sigignore = ~0;
        p->p_sig = 0;
        p->p_flag |= SWEXIT;
        p->p_sigignore = ~0;
        p->p_sig = 0;
@@ -83,28 +95,41 @@ exit(p, rv)
         * This may block!
         */
        fdfree(p);
         * This may block!
         */
        fdfree(p);
-p->p_fd = 0;
+
+       /* The next two chunks should probably be moved to vmspace_exit. */
+       vm = p->p_vmspace;
 #ifdef SYSVSHM
 #ifdef SYSVSHM
-       if (p->p_vmspace->vm_shm)
+       if (vm->vm_shm)
                shmexit(p);
 #endif
                shmexit(p);
 #endif
-       vmspace_free(p->p_vmspace);
-p->p_vmspace = 0;
+       /*
+        * Release user portion of address space.
+        * This releases references to vnodes,
+        * which could cause I/O if the file has been unlinked.
+        * Need to do this early enough that we can still sleep.
+        * Can't free the entire vmspace as the kernel stack
+        * may be mapped within that space also.
+        */
+       if (vm->vm_refcnt == 1)
+               (void) vm_map_remove(&vm->vm_map, VM_MIN_ADDRESS,
+                   VM_MAXUSER_ADDRESS);
 
 
-       if (p->p_pid == 1)
-               panic("init died");
        if (SESS_LEADER(p)) {
                register struct session *sp = p->p_session;
 
                if (sp->s_ttyvp) {
                        /*
                         * Controlling process.
        if (SESS_LEADER(p)) {
                register struct session *sp = p->p_session;
 
                if (sp->s_ttyvp) {
                        /*
                         * Controlling process.
-                        * Signal foreground pgrp and revoke access
-                        * to controlling terminal.
+                        * Signal foreground pgrp,
+                        * drain controlling terminal
+                        * and revoke access to controlling terminal.
                         */
                         */
-                       if (sp->s_ttyp->t_pgrp)
-                               pgsignal(sp->s_ttyp->t_pgrp, SIGHUP, 1);
-                       vgoneall(sp->s_ttyvp);
+                       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);
+                               vgoneall(sp->s_ttyvp);
+                       }
                        vrele(sp->s_ttyvp);
                        sp->s_ttyvp = NULL;
                        /*
                        vrele(sp->s_ttyvp);
                        sp->s_ttyvp = NULL;
                        /*
@@ -113,25 +138,19 @@ p->p_vmspace = 0;
                         * (for logging and informational purposes)
                         */
                }
                         * (for logging and informational purposes)
                         */
                }
-               sp->s_leader = 0;
+               sp->s_leader = NULL;
        }
        fixjobc(p, p->p_pgrp, 0);
        p->p_rlimit[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
        }
        fixjobc(p, p->p_pgrp, 0);
        p->p_rlimit[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
-       (void) acct(p);
-       if (--p->p_limit->p_refcnt == 0)
-               FREE(p->p_limit, M_SUBPROC);
-       if (--p->p_cred->p_refcnt == 0) {
-               crfree(p->p_cred->pc_ucred);
-               FREE(p->p_cred, M_SUBPROC);
-       }
+       (void)acct_process(p);
 #ifdef KTRACE
        /* 
         * release trace file
         */
 #ifdef KTRACE
        /* 
         * release trace file
         */
+       p->p_traceflag = 0;     /* don't trace the vrele() */
        if (p->p_tracep)
                vrele(p->p_tracep);
 #endif
        if (p->p_tracep)
                vrele(p->p_tracep);
 #endif
-
        /*
         * Remove proc from allproc queue and pidhash chain.
         * Place onto zombproc.  Unlink from parent's child list.
        /*
         * Remove proc from allproc queue and pidhash chain.
         * Place onto zombproc.  Unlink from parent's child list.
@@ -143,7 +162,7 @@ p->p_vmspace = 0;
        p->p_prev = &zombproc;
        zombproc = p;
        p->p_stat = SZOMB;
        p->p_prev = &zombproc;
        zombproc = p;
        p->p_stat = SZOMB;
-       noproc = 1;
+
        for (pp = &pidhash[PIDHASH(p->p_pid)]; *pp; pp = &(*pp)->p_hash)
                if (*pp == p) {
                        *pp = p->p_hash;
        for (pp = &pidhash[PIDHASH(p->p_pid)]; *pp; pp = &(*pp)->p_hash)
                if (*pp == p) {
                        *pp = p->p_hash;
@@ -177,13 +196,12 @@ done:
        p->p_cptr = NULL;
 
        /*
        p->p_cptr = NULL;
 
        /*
-        * Save exit status and final rusage info,
-        * adding in child rusage info and self times.
+        * Save exit status and final rusage info, adding in child rusage
+        * info and self times.
         */
        p->p_xstat = rv;
        *p->p_ru = p->p_stats->p_ru;
         */
        p->p_xstat = rv;
        *p->p_ru = p->p_stats->p_ru;
-       p->p_ru->ru_stime = p->p_stime;
-       p->p_ru->ru_utime = p->p_utime;
+       calcru(p, &p->p_ru->ru_utime, &p->p_ru->ru_stime, NULL);
        ruadd(p->p_ru, &p->p_stats->p_cru);
 
        /*
        ruadd(p->p_ru, &p->p_stats->p_cru);
 
        /*
@@ -192,57 +210,80 @@ done:
        psignal(p->p_pptr, SIGCHLD);
        wakeup((caddr_t)p->p_pptr);
 #if defined(tahoe)
        psignal(p->p_pptr, SIGCHLD);
        wakeup((caddr_t)p->p_pptr);
 #if defined(tahoe)
-       u.u_pcb.pcb_savacc.faddr = (float *)NULL;
+       /* move this to cpu_exit */
+       p->p_addr->u_pcb.pcb_savacc.faddr = (float *)NULL;
 #endif
        /*
 #endif
        /*
-        * Free the memory for the user structure and kernel stack.
-        * As we continue using it until the swtch completes
-        * (or switches to an interrupt stack), we need to block
-        * memory allocation by raising priority until we are gone.
+        * Clear curproc after we've done all operations
+        * that could block, and before tearing down the rest
+        * of the process state that might be used from clock, etc.
+        * Also, can't clear curproc while we're still runnable,
+        * as we're not on a run queue (we are current, just not
+        * a proper proc any longer!).
+        *
+        * Other substructures are freed from wait().
         */
         */
-       (void) splimp();
-       /* I don't think this will cause a sleep/realloc anywhere... */
-       kmem_free(kernel_map, (vm_offset_t)p->p_addr,
-           round_page(ctob(UPAGES)));
-       swtch();
-       /* NOTREACHED */
+       curproc = NULL;
+       if (--p->p_limit->p_refcnt == 0)
+               FREE(p->p_limit, M_SUBPROC);
+
+       /*
+        * Finally, call machine-dependent code to release the remaining
+        * resources including address space, the kernel stack and pcb.
+        * The address space is released by "vmspace_free(p->p_vmspace)";
+        * This is machine-dependent, as we may have to change stacks
+        * or ensure that the current one isn't reallocated before we
+        * finish.  cpu_exit will end with a call to cpu_swtch(), finishing
+        * our execution (pun intended).
+        */
+       cpu_exit(p);
 }
 
 }
 
+struct wait_args {
+       int     pid;
+       int     *status;
+       int     options;
+       struct  rusage *rusage;
 #ifdef COMPAT_43
 #ifdef COMPAT_43
+       int     compat;         /* pseudo */
+#endif
+};
+
+#ifdef COMPAT_43
+#if defined(hp300) || defined(luna68k)
+#include <machine/frame.h>
+#define GETPS(rp)      ((struct frame *)(rp))->f_sr
+#else
+#define GETPS(rp)      (rp)[PS]
+#endif
+
 owait(p, uap, retval)
        struct proc *p;
 owait(p, uap, retval)
        struct proc *p;
-       register struct args {
-               int     pid;
-               int     *status;
-               int     options;
-               struct  rusage *rusage;
-               int     compat;
-       } *uap;
+       register struct wait_args *uap;
        int *retval;
 {
 
        int *retval;
 {
 
-       if ((p->p_regs[PS] & PSL_ALLCC) != PSL_ALLCC) {
+#ifdef PSL_ALLCC
+       if ((GETPS(p->p_md.md_regs) & PSL_ALLCC) != PSL_ALLCC) {
                uap->options = 0;
                uap->options = 0;
-               uap->rusage = 0;
+               uap->rusage = NULL;
        } else {
        } else {
-               uap->options = p->p_regs[R0];
-               uap->rusage = (struct rusage *)p->p_regs[R1];
+               uap->options = p->p_md.md_regs[R0];
+               uap->rusage = (struct rusage *)p->p_md.md_regs[R1];
        }
        }
+#else
+       uap->options = 0;
+       uap->rusage = NULL;
+#endif
        uap->pid = WAIT_ANY;
        uap->pid = WAIT_ANY;
-       uap->status = 0;
+       uap->status = NULL;
        uap->compat = 1;
        return (wait1(p, uap, retval));
 }
 
 wait4(p, uap, retval)
        struct proc *p;
        uap->compat = 1;
        return (wait1(p, uap, retval));
 }
 
 wait4(p, uap, retval)
        struct proc *p;
-       struct args {
-               int     pid;
-               int     *status;
-               int     options;
-               struct  rusage *rusage;
-               int     compat;
-       } *uap;
+       struct wait_args *uap;
        int *retval;
 {
 
        int *retval;
 {
 
@@ -253,26 +294,14 @@ wait4(p, uap, retval)
 #define        wait1   wait4
 #endif
 
 #define        wait1   wait4
 #endif
 
-/*
- * Wait: check child processes to see if any have exited,
- * stopped under trace, or (optionally) stopped by a signal.
- * Pass back status and deallocate exited child's proc structure.
- */
+int
 wait1(q, uap, retval)
        register struct proc *q;
 wait1(q, uap, retval)
        register struct proc *q;
-       register struct args {
-               int     pid;
-               int     *status;
-               int     options;
-               struct  rusage *rusage;
-#ifdef COMPAT_43
-               int compat;
-#endif
-       } *uap;
+       register struct wait_args *uap;
        int retval[];
 {
        register int nfound;
        int retval[];
 {
        register int nfound;
-       register struct proc *p;
+       register struct proc *p, *t;
        int status, error;
 
        if (uap->pid == 0)
        int status, error;
 
        if (uap->pid == 0)
@@ -304,10 +333,34 @@ loop:
                        if (uap->rusage && (error = copyout((caddr_t)p->p_ru,
                            (caddr_t)uap->rusage, sizeof (struct rusage))))
                                return (error);
                        if (uap->rusage && (error = copyout((caddr_t)p->p_ru,
                            (caddr_t)uap->rusage, sizeof (struct rusage))))
                                return (error);
+                       /*
+                        * If we got the child via a ptrace 'attach',
+                        * we need to give it back to the old parent.
+                        */
+                       if (p->p_oppid && (t = pfind(p->p_oppid))) {
+                               p->p_oppid = 0;
+                               proc_reparent(p, t);
+                               psignal(t, SIGCHLD);
+                               wakeup((caddr_t)t);
+                               return (0);
+                       }
                        p->p_xstat = 0;
                        ruadd(&q->p_stats->p_cru, p->p_ru);
                        FREE(p->p_ru, M_ZOMBIE);
 
                        p->p_xstat = 0;
                        ruadd(&q->p_stats->p_cru, p->p_ru);
                        FREE(p->p_ru, M_ZOMBIE);
 
+                       /*
+                        * Decrement the count of procs running with this uid.
+                        */
+                       (void)chgproccnt(p->p_cred->p_ruid, -1);
+
+                       /*
+                        * Free up credentials.
+                        */
+                       if (--p->p_cred->p_refcnt == 0) {
+                               crfree(p->p_cred->pc_ucred);
+                               FREE(p->p_cred, M_SUBPROC);
+                       }
+
                        /*
                         * Finally finished with old proc entry.
                         * Unlink it from its process group and free it.
                        /*
                         * Finally finished with old proc entry.
                         * Unlink it from its process group and free it.
@@ -321,6 +374,13 @@ loop:
                                q->p_ysptr = p->p_ysptr;
                        if ((q = p->p_pptr)->p_cptr == p)
                                q->p_cptr = p->p_osptr;
                                q->p_ysptr = p->p_ysptr;
                        if ((q = p->p_pptr)->p_cptr == p)
                                q->p_cptr = p->p_osptr;
+
+                       /*
+                        * Give machine-dependent layer a chance
+                        * to free anything that cpu_exit couldn't
+                        * release while still running in process context.
+                        */
+                       cpu_wait(p);
                        FREE(p, M_PROC);
                        nprocs--;
                        return (0);
                        FREE(p, M_PROC);
                        nprocs--;
                        return (0);
@@ -338,7 +398,7 @@ loop:
                        if (uap->status) {
                                status = W_STOPCODE(p->p_xstat);
                                error = copyout((caddr_t)&status,
                        if (uap->status) {
                                status = W_STOPCODE(p->p_xstat);
                                error = copyout((caddr_t)&status,
-                                   (caddr_t)uap->status, sizeof(status));
+                                       (caddr_t)uap->status, sizeof(status));
                        } else
                                error = 0;
                        return (error);
                        } else
                                error = 0;
                        return (error);
@@ -354,3 +414,37 @@ loop:
                return (error);
        goto loop;
 }
                return (error);
        goto loop;
 }
+
+/*
+ * make process 'parent' the new parent of process 'child'.
+ */
+void
+proc_reparent(child, parent)
+       register struct proc *child;
+       register struct proc *parent;
+{
+       register struct proc *o;
+       register struct proc *y;
+
+       if (child->p_pptr == parent)
+               return;
+
+       /* fix up the child linkage for the old parent */
+       o = child->p_osptr;
+       y = child->p_ysptr;
+       if (y)
+               y->p_osptr = o;
+       if (o)
+               o->p_ysptr = y;
+       if (child->p_pptr->p_cptr == child)
+               child->p_pptr->p_cptr = o;
+
+       /* fix up child linkage for new parent */
+       o = parent->p_cptr;
+       if (o)
+               o->p_ysptr = child;
+       child->p_osptr = o;
+       child->p_ysptr = NULL;
+       parent->p_cptr = child;
+       child->p_pptr = parent;
+}