X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/0b4e3aa066abc0728aacb4bbeb86f53f9737156e..5eebf7385fedb1517b66b53c28e5aa6bb0a2be50:/bsd/kern/kern_exit.c diff --git a/bsd/kern/kern_exit.c b/bsd/kern/kern_exit.c index ffb3efdcf..157ec5f05 100644 --- a/bsd/kern/kern_exit.c +++ b/bsd/kern/kern_exit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -82,14 +82,26 @@ #include #include #include +#include + +#include +#include #include #include #include +#include #include +#if KTRACE +#include +#include +#endif extern char init_task_failure_data[]; int exit1 __P((struct proc *, int, int *)); +void proc_prepareexit(struct proc *p); +int vfork_exit(struct proc *p, int rv); +void vproc_exit(struct proc *p); /* * exit -- @@ -106,12 +118,12 @@ exit(p, uap, retval) { 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 */ } @@ -127,8 +139,7 @@ exit1(p, rv, retval) int * retval; { register struct proc *q, *nq; - thread_t self = current_thread(); - thread_act_t th_act_self = current_act(); + thread_act_t self = current_act(); struct task *task = p->task; register int i,s; struct uthread *ut; @@ -139,22 +150,25 @@ exit1(p, rv, retval) * right here. */ - ut = get_bsdthread_info(th_act_self); + ut = get_bsdthread_info(self); if (ut->uu_flag & P_VFORK) { - vfork_exit(p, rv); - vfork_return(th_act_self, p->p_pptr, p , retval); + if (!vfork_exit(p, rv)) { + vfork_return(self, p->p_pptr, p , retval); unix_syscall_return(0); /* NOT REACHED */ + } + return(EINVAL); } + AUDIT_SYSCALL_EXIT(0, p, ut); /* Exit is always successfull */ signal_lock(p); while (p->exit_thread != self) { if (sig_try_locked(p) <= 0) { - if (get_threadtask(th_act_self) != task) { + if (get_threadtask(self) != task) { signal_unlock(p); return(0); } signal_unlock(p); - thread_terminate(th_act_self); + thread_terminate(self); thread_funnel_set(kernel_flock, FALSE); thread_exception_return(); /* NOTREACHED */ @@ -178,21 +192,6 @@ exit1(p, rv, retval) /* task terminate will call proc_terminate and that cleans it up */ task_terminate_internal(task); - /* - * we come back and returns to AST which - * should cleanup the rest - */ -#if 0 - if (task == current_task()) { - thread_exception_return(); - /*NOTREACHED*/ - } - - while (task == current_task()) { - thread_terminate_self(); - /*NOTREACHED*/ - } -#endif return(0); } @@ -201,9 +200,12 @@ proc_prepareexit(struct proc *p) { int s; struct uthread *ut; - thread_t self = current_thread(); - thread_act_t th_act_self = current_act(); + exception_data_t code[EXCEPTION_CODE_MAX]; + thread_act_t self = current_act(); + code[0] = 0xFF000001; /* Set terminate code */ + code[1] = p->p_pid; /* Pass out the pid */ + (void)sys_perf_notify(p->task, &code, 2); /* Notify the perf server */ /* * Remove proc from allproc queue and from pidhash chain. @@ -212,6 +214,7 @@ proc_prepareexit(struct proc *p) * in partially cleaned state. */ LIST_REMOVE(p, p_list); + LIST_INSERT_HEAD(&zombproc, p, p_list); /* Place onto zombproc. */ LIST_REMOVE(p, p_hash); #ifdef PGINPROF @@ -224,21 +227,17 @@ proc_prepareexit(struct proc *p) p->p_flag &= ~(P_TRACED | P_PPWAIT); 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 = get_bsdthread_info(self); + ut->uu_siglist = 0; + untimeout(realitexpire, (caddr_t)p->p_pid); } void proc_exit(struct proc *p) { - register struct proc *q, *nq; - thread_t self = current_thread(); - thread_act_t th_act_self = current_act(); + register struct proc *q, *nq, *pp; 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 @@ -256,6 +255,12 @@ proc_exit(struct proc *p) MALLOC_ZONE(p->p_ru, struct rusage *, sizeof (*p->p_ru), M_ZOMBIE, M_WAITOK); + /* + * need to cancel async IO requests that can be cancelled and wait for those + * already active. MAY BLOCK! + */ + _aio_exit( p ); + /* * Close open files and release open-file table. * This may block! @@ -265,11 +270,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, @@ -287,9 +296,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. @@ -301,15 +311,22 @@ 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); -#endif + if (p->p_tracep) { + struct vnode *tvp = p->p_tracep; + p->p_tracep = NULL; + if (UBCINFOEXISTS(tvp)) + ubc_rele(tvp); + vrele(tvp); + } +#endif q = p->p_children.lh_first; if (q) /* only need this if any child is S_ZOMB */ @@ -324,7 +341,6 @@ 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); /* * The sigwait_thread could be stopped at a * breakpoint. Wake it up to kill. @@ -332,15 +348,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); - clear_wait(sig_shuttle, THREAD_INTERRUPTED); - threadsignal(q->sigwait_thread, SIGKILL, 0); + thread_resume((thread_act_t)q->sigwait_thread); + clear_wait(q->sigwait_thread, 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. @@ -376,7 +391,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); /* @@ -408,6 +422,9 @@ proc_exit(struct proc *p) FREE_ZONE(p->p_limit, sizeof *p->p_limit, M_SUBPROC); p->p_limit = NULL; + /* Free the auditing info */ + audit_proc_free(p); + /* * Finish up by terminating the task * and halt this thread (only if a @@ -417,13 +434,37 @@ proc_exit(struct proc *p) //task->proc = NULL; set_bsdtask_info(task, NULL); + KNOTE(&p->p_klist, NOTE_EXIT); + /* * 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; + + /* + * Add child resource usage to parent before giving + * zombie to init + */ + ruadd(&p->p_pptr->p_stats->p_cru, p->p_ru); - /* Place onto zombproc. */ - LIST_INSERT_HEAD(&zombproc, p, p_list); + 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); + + + /* mark as a zombie */ p->p_stat = SZOMB; /* and now wakeup the parent */ @@ -464,7 +505,6 @@ wait4(p, uap, retval) struct wait4_args *uap; int *retval; { - return (wait1(p, uap, retval, 0)); } @@ -482,7 +522,7 @@ owait3(p, uap, retval) { struct wait4_args *a; - a = (struct wait4_args *)get_bsduthreadarg(current_act); + a = (struct wait4_args *)get_bsduthreadarg(current_act()); a->rusage = uap->rusage; a->options = uap->options; @@ -499,24 +539,19 @@ owait3(p, uap, retval) int wait1continue(result) { - void *vt; - thread_act_t thread; - struct uthread *ut; - int *retval; - struct proc *p; - - p = current_proc(); - 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); - wait1((struct proc *)p, (struct wait4_args *)vt, retval, 0); + p = current_proc(); + thread = current_act(); + vt = (void *)get_bsduthreadarg(thread); + retval = (int *)get_bsduthreadrval(thread); + return(wait1((struct proc *)p, (struct wait4_args *)vt, retval, 0)); } int @@ -531,16 +566,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; @@ -552,6 +580,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 @@ -564,7 +598,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); } } @@ -572,7 +607,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); } /* @@ -582,9 +618,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; @@ -620,8 +663,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. @@ -630,9 +675,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 && @@ -652,24 +698,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; } @@ -690,18 +736,21 @@ 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(); - if (suser(p->p_ucred, &p->p_acflag)) + AUDIT_MACH_SYSCALL_ENTER(AUE_INITPROCESS); + if (suser(p->p_ucred, &p->p_acflag)) { + AUDIT_MACH_SYSCALL_EXIT(KERN_NO_ACCESS); return(KERN_NO_ACCESS); + } if (p->p_pid != 1 && p->p_pgid != p->p_pid) enterpgrp(p, p->p_pid, 0); @@ -716,6 +765,7 @@ init_process(void) p->p_sibling.le_next = NULL; p->p_pptr = kernproc; + AUDIT_MACH_SYSCALL_EXIT(KERN_SUCCESS); return(KERN_SUCCESS); } @@ -729,58 +779,39 @@ process_terminate_self(void) /*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 +int vfork_exit(p, rv) - register struct proc *p; + struct proc *p; int rv; { register struct proc *q, *nq; - thread_t self = current_thread(); - thread_act_t th_act_self = current_act(); + thread_act_t self = current_act(); struct task *task = p->task; register int i,s; struct uthread *ut; + exception_data_t code[EXCEPTION_CODE_MAX]; - /* - * 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 */ - + ut = get_bsdthread_info(self); + if (p->exit_thread) { + return(1); + } + p->exit_thread = self; + s = splsched(); p->p_flag |= P_WEXIT; splx(s); + + code[0] = 0xFF000001; /* Set terminate code */ + code[1] = p->p_pid; /* Pass out the pid */ + (void)sys_perf_notify(p->task, &code, 2); /* Notify the perf server */ + /* * Remove proc from allproc queue and from pidhash chain. * Need to do this before we do anything that can block. @@ -788,6 +819,7 @@ panic("init died\nState at Last Exception:\n\n%s", init_task_failure_data); * in partially cleaned state. */ LIST_REMOVE(p, p_list); + LIST_INSERT_HEAD(&zombproc, p, p_list); /* Place onto zombproc. */ LIST_REMOVE(p, p_hash); /* * If parent is waiting for us to exit or exec, @@ -797,24 +829,21 @@ panic("init died\nState at Last Exception:\n\n%s", init_task_failure_data); p->p_sigignore = ~0; p->p_siglist = 0; - ut->uu_sig = 0; - untimeout(realitexpire, (caddr_t)p); + ut->uu_siglist = 0; + untimeout(realitexpire, (caddr_t)p->p_pid); p->p_xstat = rv; vproc_exit(p); + return(0); } - void vproc_exit(struct proc *p) { - register struct proc *q, *nq; - thread_t self = current_thread(); - thread_act_t th_act_self = current_act(); + register struct proc *q, *nq, *pp; struct task *task = p->task; register int i,s; - struct uthread *ut; boolean_t funnel_state; MALLOC_ZONE(p->p_ru, struct rusage *, @@ -826,14 +855,12 @@ vproc_exit(struct proc *p) */ fdfree(p); - /* Close ref SYSV Shared memory*/ - if (p->vm_shm) - shmexit(p); - if (SESS_LEADER(p)) { register struct session *sp = p->p_session; if (sp->s_ttyvp) { + struct vnode *ttyvp; + /* * Controlling process. * Signal foreground pgrp, @@ -851,9 +878,10 @@ vproc_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. @@ -865,15 +893,21 @@ vproc_exit(struct proc *p) 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) - vrele(p->p_tracep); -#endif + if (p->p_tracep) { + struct vnode *tvp = p->p_tracep; + p->p_tracep = NULL; + if (UBCINFOEXISTS(tvp)) + ubc_rele(tvp); + vrele(tvp); + } +#endif q = p->p_children.lh_first; if (q) /* only need this if any child is S_ZOMB */ @@ -888,7 +922,6 @@ vproc_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); /* * The sigwait_thread could be stopped at a * breakpoint. Wake it up to kill. @@ -896,15 +929,14 @@ vproc_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); - clear_wait(sig_shuttle, THREAD_INTERRUPTED); - threadsignal(q->sigwait_thread, SIGKILL, 0); + thread_resume((thread_act_t)q->sigwait_thread); + clear_wait(q->sigwait_thread, 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. @@ -983,14 +1015,18 @@ vproc_exit(struct proc *p) /* * 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); + /* mark as a zombie */ p->p_stat = SZOMB; /* and now wakeup the parent */ wakeup((caddr_t)p->p_pptr); - } -