X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..43866e378188c25dd1e2208016ab3cbeb086ae6c:/bsd/kern/kern_exit.c?ds=sidebyside diff --git a/bsd/kern/kern_exit.c b/bsd/kern/kern_exit.c index 385c63711..25dc9ef71 100644 --- a/bsd/kern/kern_exit.c +++ b/bsd/kern/kern_exit.c @@ -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@ */ @@ -86,10 +89,14 @@ #include #include #include +#include #include +#if KTRACE +#include +#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); +}