exit.c revision 8ceba33e02de3517d5be4b514f5702e177ae5900
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
#pragma ident "%Z%%M% %I% %E% SMI" /* from SVr4.0 1.74 */
#include <sys/sysmacros.h>
#include <sys/ucontext.h>
#include <sys/sem_impl.h>
#include <sys/aio_impl.h>
#include <sys/lwpchan_impl.h>
#include <sys/schedctl.h>
#include <sys/contract_impl.h>
#include <sys/libc_kernel.h>
/*
*/
int
{
switch (code) {
case CLD_EXITED:
stat <<= 8;
break;
case CLD_DUMPED:
break;
case CLD_KILLED:
break;
case CLD_TRAPPED:
case CLD_STOPPED:
stat <<= 8;
break;
case CLD_CONTINUED:
break;
default:
/* NOTREACHED */
}
return (stat);
}
static char *
{
switch (why) {
case CLD_EXITED:
break;
case CLD_KILLED:
break;
case CLD_DUMPED:
break;
default:
break;
}
return (buf);
}
/*
* exit system call: pass back caller's arg.
*/
void
{
}
/*
* Called by proc_exit() when a zone's init exits, presumably because
* it failed. As long as the given zone is still in the "running"
* state, we will re-exec() init, but first we need to reset things
* which are usually inherited across exec() but will break init's
* assumption that it is being exec()'d from a virgin process. Most
* importantly this includes closing all file descriptors (exec only
* closes those marked close-on-exec) and resetting signals (exec only
* resets handled signals, and we need to clear any signals which
* killed init). Anything else that exec(2) says would be inherited,
* but would affect the execution of init, needs to be reset.
*/
static int
{
int i, err;
char reason_buf[64];
/*
* Let zone admin (and global zone admin if this is for a non-global
* zone) know that init has failed and will be restarted.
*/
"init(1M) %s: restarting automatically",
if (!INGLOBALZONE(p)) {
"restarting automatically",
}
/*
* Remove any fpollinfo_t's for this (last) thread from our file
* descriptors so closeall() can ASSERT() that they're all gone.
* Then close all open file descriptors in the process.
*/
pollcleanup();
/*
* Grab p_lock and begin clearing miscellaneous global process
* state that needs to be reset before we exec the new init(1M).
*/
mutex_enter(&p->p_lock);
prbarrier(p);
sigemptyset(&t->t_hold);
sigemptyset(&t->t_sig);
sigemptyset(&t->t_extsig);
sigemptyset(&p->p_sig);
sigemptyset(&p->p_extsig);
sigdelq(p, t, 0);
if (p->p_killsqp) {
siginfofree(p->p_killsqp);
}
/*
* Reset any signals that are ignored back to the default disposition.
* Other u_signal members will be cleared when exec calls sigdefault().
*/
for (i = 1; i < NSIG; i++) {
}
}
/*
* Clear the current signal, any signal info associated with it, and
*/
lwp->lwp_cursig = 0;
lwp->lwp_extsig = 0;
}
/*
* Reset both the process root directory and the current working
* directory to the root of the zone just as we do during boot.
*/
}
mutex_exit(&p->p_lock);
/* Free the controlling tty. (freectty() always assumes curproc.) */
/*
* Now exec() the new init(1M) on top of the current process. If we
* succeed, the caller will treat this like a successful system call.
* If we fail, we issue messages and the caller will proceed with exit.
*/
if (err == 0)
return (0);
"failed to restart init(1M) (err=%d): system reboot required", err);
if (!INGLOBALZONE(p)) {
"(pid %d, err=%d): zoneadm(1M) boot required",
}
return (-1);
}
/*
* Release resources.
* Enter zombie state.
* Wake up parent and init processes,
* and dispose of children.
*/
void
{
/*
* If proc_exit() fails, then some other lwp in the process
* got there first. We just have to call lwp_exit() to allow
* the other lwp to finish exiting the process. Otherwise we're
* restarting init, and should return.
*/
lwp_exit();
/* NOTREACHED */
}
}
/*
* Set the SEXITING flag on the process, after making sure /proc does
* not have it locked. This is done in more places than proc_exit(),
* so it is a separate function.
*/
void
{
mutex_enter(&p->p_lock);
prbarrier(p);
mutex_exit(&p->p_lock);
}
/*
* Return value:
* 1 - exitlwps() failed, call (or continue) lwp_exit()
* 0 - restarting init. Return through system call path
*/
int
{
int rv;
proc_t *q;
int evaporate;
/*
* Stop and discard the process's lwps except for the current one,
* unless some other lwp beat us to it. If exitlwps() fails then
* return and the calling lwp will call (or continue in) lwp_exit().
*/
proc_is_exiting(p);
if (exitlwps(0) != 0)
return (1);
/*
* Will perform any brand specific proc exit processing, since this
* is always the last lwp, will also perform lwp_exit and free brand
* data
*/
if (PROC_IS_BRANDED(p))
/*
* Don't let init exit unless zone_start_init() failed its exec, or
* we are shutting down the zone or the machine.
*
* Since we are single threaded, we don't need to lock the
* following accesses to zone_proc_initpid.
*/
if (p->p_pid == z->zone_proc_initpid) {
if (z->zone_boot_err == 0 &&
zone_status_get(z) < ZONE_IS_SHUTTING_DOWN &&
z->zone_restart_init == B_TRUE &&
return (0);
/*
* Since we didn't or couldn't restart init, we clear
* the zone's init state and proceed with exit
* processing.
*/
z->zone_proc_initpid = -1;
}
/*
* Allocate a sigqueue now, before we grab locks.
* It will be given to sigcld(), below.
* Special case: If we will be making the process disappear
* without a trace (for the benefit of posix_spawn() in libc)
* don't bother to allocate a useless sigqueue.
*/
if (!evaporate)
/*
* revoke any doors created by the process.
*/
if (p->p_door_list)
door_exit();
/*
* Release schedctl data structures.
*/
if (p->p_pagep)
/*
* make sure all pending kaio has completed.
*/
if (p->p_aio)
/*
* discard the lwpchan cache.
*/
/*
* Clean up any DTrace helper actions or probes for the process.
*/
if (p->p_dtrace_helpers != NULL) {
(*dtrace_helpers_cleanup)();
}
/* untimeout the realtime timers */
timer_exit();
p->p_alarmid = 0;
}
/*
* Remove any fpollinfo_t's for this (last) thread from our file
* descriptors so closeall() can ASSERT() that they're all gone.
*/
pollcleanup();
if (p->p_rprof_cyclic != CYCLIC_NONE) {
}
mutex_enter(&p->p_lock);
/*
* Clean up any DTrace probes associated with this process.
*/
if (p->p_dtrace_probes) {
}
while ((tmp_id = p->p_itimerid) != 0) {
p->p_itimerid = 0;
mutex_exit(&p->p_lock);
mutex_enter(&p->p_lock);
}
lwp_cleanup();
/*
* We are about to exit; prevent our resource associations from
* being changed.
*/
/*
* Block the process against /proc now that we have really
* acquired p->p_lock (to manipulate p_tlist at least).
*/
prbarrier(p);
#ifdef SUN_SRC_COMPAT
if (code == CLD_KILLED)
#endif
sigfillset(&p->p_ignore);
sigemptyset(&p->p_siginfo);
sigemptyset(&p->p_sig);
sigemptyset(&p->p_extsig);
sigemptyset(&t->t_sig);
sigemptyset(&t->t_extsig);
sigemptyset(&p->p_sigmask);
sigdelq(p, t, 0);
lwp->lwp_cursig = 0;
lwp->lwp_extsig = 0;
if (lwp->lwp_curinfo) {
}
t->t_proc_flag |= TP_LWPEXIT;
prlwpexit(t); /* notify /proc */
lwp_hash_out(p, t->t_tid);
prexit(p);
p->p_lwpcnt = 0;
sigqfree(p);
term_mstate(t);
execdir_vp = p->p_execdir;
mutex_exit(&p->p_lock);
if (exec_vp)
if (execdir_vp)
/* Free the controlling tty. (freectty() always assumes curproc.) */
#if defined(__sparc)
utrap_free(p);
#endif
if (p->p_semacct) /* IPC semaphore exit */
semexit(p);
exacct_commit_proc(p, rv);
/*
* Release any resources associated with C2 auditing
*/
#ifdef C2_AUDIT
if (audit_active) {
/*
* audit exit system call
*/
}
#endif
/*
* Free address space.
*/
relvm();
/*
* Release held contracts.
*/
contract_exit(p);
/*
* Depart our encapsulating process contract.
*/
ASSERT(p->p_ct_process);
}
/*
* Remove pool association, and block if requested by pool_do_bind.
*/
mutex_enter(&p->p_lock);
p->p_pool = pool_default;
/*
* Now that our address space has been freed and all other threads
* in this process have exited, set the PEXITED pool flag. This
* tells the pools subsystems to ignore this process if it was
* requested to rebind this process to a new pool.
*/
p->p_poolflag |= PEXITED;
mutex_exit(&p->p_lock);
/*
* Delete this process from the newstate list of its parent. We
* will put it in the right place in the sigcld in the end.
*/
/*
* Reassign the orphans to the next of kin.
* Don't rearrange init's orphanage.
*/
for (;;) {
q->p_nextofkin = nokp;
if (q->p_nextorph == NULL)
break;
q = q->p_nextorph;
}
}
/*
* Reassign the children to init.
* Don't try to assign init's children to init.
*/
if (!INGLOBALZONE(curproc))
setzonetop = B_TRUE;
pgdetach(p);
do {
/*
* Delete it from its current parent new state
* list and add it to init new state list
*/
q->p_ppid = 1;
if (setzonetop) {
mutex_enter(&q->p_lock);
mutex_exit(&q->p_lock);
}
/*
* Since q will be the first child,
* it will not have a previous sibling.
*/
q->p_psibling = NULL;
}
if (q->p_proc_flag & P_PR_PTRACE) {
mutex_enter(&q->p_lock);
mutex_exit(&q->p_lock);
}
/*
* sigcld() will add the child to parents
* newstate list.
*/
}
mutex_enter(&p->p_lock);
p->p_proc_flag &= ~P_PR_PTRACE;
/*
* Release resource controls, as they are no longer enforceable.
*/
rctl_set_free(p->p_rctls);
/*
* Give up task and project memberships. Decrement tk_nlwps counter
* for our task.max-lwps resource control. An extended accounting
* record, if that facility is active, is scheduled to be written.
* Zombie processes are false members of task0 for the remainder of
* their lifetime; no accounting information is recorded for them.
*/
p->p_zone->zone_nlwps--;
task_detach(p);
/*
* Clear the lwp directory and the lwpid hash table
* now that /proc can't bother us any more.
* We free the memory below, after dropping p->p_lock.
*/
lwpdir_sz = p->p_lwpdir_sz;
tidhash_sz = p->p_tidhash_sz;
p->p_lwpdir_sz = 0;
p->p_tidhash_sz = 0;
/*
* If the process has context ops installed, call the exit routine
* on behalf of this last remaining thread. Normally exitpctx() is
* called during thread_exit() or lwp_exit(), but because this is the
* last thread in the process, we must call it here. By the time
* thread_exit() is called (below), the association with the relevant
* process has been lost.
*
* We also free the context here.
*/
if (p->p_pctx) {
exitpctx(p);
freepctx(p, 0);
}
/*
* curthread's proc pointer is changed to point at p0 because
* curthread's original proc pointer can be freed as soon as
* the child sends a SIGCLD to its parent.
*/
mutex_exit(&p->p_lock);
if (!evaporate)
else {
/*
* Do what sigcld() would do if the disposition
* of the SIGCHLD signal were set to be ignored.
*/
cv_broadcast(&p->p_srwchan_cv);
freeproc(p);
}
/*
* We don't release u_cdir and u_rdir until SZOMB is set.
* This protects us against dofusers().
*/
if (rdir)
if (cwd)
lwp_pcb_exit();
thread_exit();
/* NOTREACHED */
}
/*
* Format siginfo structure for wait system calls.
*/
void
{
if (waitflag) {
}
}
/*
* Wait system call.
* Search for a terminated (zombie) child,
* finally lay it to rest, and collect its status.
* Look also for stopped children,
* and pass back status from them.
*/
int
{
int found;
int proc_gone;
/*
* Obsolete flag, defined here only for binary compatibility
* with old statically linked executables. Delete this when
* we no longer care about these old and broken applications.
*/
#define _WNOCHLD 0400
return (EINVAL);
switch (idtype) {
case P_PID:
case P_PGID:
return (EINVAL);
/* FALLTHROUGH */
case P_ALL:
break;
default:
return (EINVAL);
}
/*
* lock parent mutex so that sibling chain can be searched.
*/
/*
* if we are only looking for exited processes and child_ns list
* is empty no reason to look at all children.
*/
return (0);
}
return (ECHILD);
}
proc_gone = 0;
continue;
}
continue;
}
case CLD_TRAPPED:
case CLD_STOPPED:
case CLD_CONTINUED:
"waitid: wrong state %d on the p_newstate"
break;
case CLD_EXITED:
case CLD_DUMPED:
case CLD_KILLED:
/*
* Count how many are already gone
* for good.
*/
proc_gone++;
break;
}
if (!waitflag) {
} else {
}
if (waitflag) { /* accept SIGCLD */
}
return (0);
}
break;
}
/*
* Wow! None of the threads on the p_sibling_ns list were
* interesting threads. Check all the kids!
*/
found = 0;
do {
continue;
}
continue;
}
found++;
case CLD_TRAPPED:
break;
if (waitflag) { /* accept SIGCLD */
}
return (0);
case CLD_STOPPED:
break;
/* Is it still stopped? */
if (!jobstopped(cp)) {
break;
}
if (waitflag) { /* accept SIGCLD */
}
return (0);
case CLD_CONTINUED:
if (!(options & WCONTINUED))
break;
if (waitflag) { /* accept SIGCLD */
}
return (0);
case CLD_EXITED:
case CLD_DUMPED:
case CLD_KILLED:
/*
* Don't complain if a process was found in
* the first loop but we broke out of the loop
* because of the arguments passed to us.
*/
if (proc_gone == 0) {
"waitid: wrong state on the"
" p_child list");
} else {
break;
}
}
break;
/*
* If we found no interesting processes at all,
* break out and return ECHILD.
*/
break;
/*
* We should set ip->si_signo = SIGCLD,
* but there is an SVVS test that expects
* ip->si_signo to be zero in this case.
*/
return (0);
}
/*
* If we found no processes of interest that could
* change state while we wait, we don't wait at all.
* Get out with ECHILD according to SVID.
*/
break;
return (EINTR);
}
}
return (ECHILD);
}
/*
* For implementations that don't require binary compatibility,
* the wait system call may be made into a library call to the
* waitid system call.
*/
wait(void)
{
int error;
rval_t r;
return (r.r_vals);
}
int
{
int error;
return (0);
}
#ifdef _SYSCALL32_IMPL
int
{
int error;
return (0);
}
#endif /* _SYSCALL32_IMPL */
void
proc_detach(proc_t *p)
{
proc_t *q;
q = p->p_parent;
/*
* Take it off the newstate list of its parent
*/
delete_ns(q, p);
if (q->p_child == p) {
/*
* If the parent has no children, it better not
* have any with new states either!
*/
}
if (p->p_sibling) {
}
if (p->p_psibling) {
}
}
/*
* Remove zombie children from the process table.
*/
void
{
proc_t *q;
if (p->p_killsqp) {
siginfofree(p->p_killsqp);
}
prfree(p); /* inform /proc */
/*
* Don't free the init processes.
* Other dying processes will access it.
*/
if (p == proc_init)
return;
/*
* We wait until now to free the cred structure because a
* zombie process's credentials may be examined by /proc.
* No cred locking needed because there are no threads at this point.
*/
if (p->p_corefile != NULL) {
p->p_corefile = NULL;
}
}
/*
* get set to the correct value on process exit, so it
* should get properly updated
*/
+= p->p_acct[LMS_USER_LOCK];
+= p->p_acct[LMS_WAIT_CPU];
}
q = p->p_nextofkin;
if (q && q->p_orphan == p)
q->p_orphan = p->p_nextorph;
else if (q) {
for (q = q->p_orphan; q; q = q->p_nextorph)
if (q->p_nextorph == p)
break;
ASSERT(q && q->p_nextorph == p);
q->p_nextorph = p->p_nextorph;
}
proc_detach(p);
pid_exit(p); /* frees pid and proc structure */
}
/*
* Delete process "child" from the newstate list of process "parent"
*/
void
{
return;
}
}
}
/*
* Add process "child" to the new state list of process "parent"
*/
void
{
}