exit.c revision e9f7cbf00b5dbfafe45ffb00125fa0cc683595c6
/*
* 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */
#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);
mutex_enter(&p->p_lock);
if (p->p_ttime > 0) {
/*
* Account any remaining ticks charged to this process
* on its way out.
*/
p->p_ttime = 0;
}
mutex_exit(&p->p_lock);
/*
* 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)) {
brand_clearbrand(p, B_FALSE);
}
/*
* 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;
}
lwp_pcb_exit();
/*
* 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 because it is either:
* * an exiting SSYS process, or
* * a posix_spawn() vfork child who requests it,
* we 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);
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);
/* 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
*/
if (AU_AUDITING()) {
/*
* audit exit system call
*/
}
/*
* Free address space.
*/
relvm();
if (exec_vp) {
/*
* Close this executable which has been opened when the process
* was created by getproc().
*/
}
if (execdir_vp)
/*
* 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);
/*
* Have our task accummulate our resource usage data before they
* become contaminated by p_cacct etc., and before we renounce
* membership of the task.
*
* We do this regardless of whether or not task accounting is active.
* This is to avoid having nonsense data reported for this task if
* task accounting is subsequently enabled. The overhead is minimal;
* by this point, this process has accounted for the usage of all its
* LWPs. We nonetheless do the work here, and under the protection of
* pidlock, so that the movement of the process's usage to the task
* happens at the same time as the removal of the process from the
* task, from the point of view of exacct_snapshot_task_usage().
*/
p->p_proc_flag &= ~P_PR_PTRACE;
/*
* Release resource controls, as they are no longer enforceable.
*/
rctl_set_free(p->p_rctls);
/*
* 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. We cannot give up task and project
* membership at this point because that would allow zombies to escape
* from the max-processes resource controls. Zombies stay in their
* current task and project until the process table slot is released
* in freeproc().
*/
p->p_zone->zone_nlwps--;
/*
* 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;
ret_tidhash = p->p_ret_tidhash;
p->p_lwpdir_sz = 0;
p->p_tidhash_sz = 0;
p->p_ret_tidhash = NULL;
/*
* 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 to the 'sched'
* process for the corresponding zone, except in the case when
* the exiting process is in fact a zsched instance, in which
* case the proc pointer is set to p0. We do so, so that the
* process still points at the right zone when we call the VN_RELE()
* below.
*
* This is because curthread's original proc pointer can be freed as
* soon as the child sends a SIGCLD to its parent. We use zsched so
* that for user processes, even in the final moments of death, the
* process is still associated with its zone.
*/
else
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 (cdir)
if (rdir)
if (cwd)
/*
* task_rele() may ultimately cause the zone to go away (or
* may cause the last user process in a zone to go away, which
* signals zsched to go away). So prior to this call, we must
* no longer point at zsched.
*/
while (ret_tidhash != NULL) {
ret_tidhash = next;
}
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;
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;
continue;
continue;
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:
continue;
/*
* 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;
}
}
found++;
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);
}
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;
}
/*
* The process table slot is being freed, so it is now safe to give up
* task and project membership.
*/
mutex_enter(&p->p_lock);
task_detach(p);
mutex_exit(&p->p_lock);
proc_detach(p);
}
/*
* Delete process "child" from the newstate list of process "parent"
*/
void
{
return;
}
}
}
/*
* Add process "child" to the new state list of process "parent"
*/
void
{
}