Subject: multiple copies of wait4()
To: None <tech-kern@netbsd.org>
From: David Laight <david@l8s.co.uk>
List: tech-kern
Date: 10/24/2002 16:03:46
The source tree contains what are (effectively) 5 different
copies of the code for wait4(). This routine contains the
non-trivial code to free up a process.
Not all the copies actually match properly because changes
have not been applied to all of them.
The code is in:
sys_wait4() (kern/kern_exit.c)
irix_sys_waitsys (compat/irix/irix_signal.c)
netbsd32_wait4 (compat/netbsd32/netbsd32_wait.c)
svr4_sys_waitsys (compat/svr4/svr4_misc.c)
svr4_32_sys_waitsys (compat/svr4_32/svr4_32_misc.c)
Although these compat options (probably) aren't that common
the cost of commoning the code is minimal.
Replacing sys_wait4() with the following code allows the
emulations to avoid knowing the internals of process allocation.
(The code for the emulations is now similar to sys_wait4, but
not given here to save bandwidth.)
int
sys_wait4(struct proc *parent, void *v, register_t *retval)
{
struct sys_wait4_args /* {
syscallarg(int) pid;
syscallarg(int *) status;
syscallarg(int) options;
syscallarg(struct rusage *) rusage;
} */ *uap = v;
struct proc *child;
int error, status;
if (SCARG(uap, pid) == 0)
SCARG(uap, pid) = -parent->p_pgid;
if (SCARG(uap, options) & ~(WUNTRACED|WNOHANG|WALTSIG|WALLSIG))
return (EINVAL);
error = find_stopped_child( parent, SCARG(uap,pid), SCARG(uap,options),
&child);
if (error)
return error;
if (!child) {
*retval = 0;
return 0;
}
*retval = child->p_pid;
if (child->p_stat == SZOMB) {
if (SCARG(uap, status)) {
status = child->p_xstat;
error = copyout(&status, SCARG(uap, status),
sizeof(status));
if (error)
return (error);
}
if (SCARG(uap, rusage)) {
error = copyout(child->p_ru, SCARG(uap, rusage),
sizeof(struct rusage));
if (error)
return (error);
}
/* Finally finished with old proc entry. */
proc_free(child);
return 0;
}
/* child state must be SSTOP */
if (SCARG(uap, status)) {
status = W_STOPCODE(child->p_xstat);
error = copyout(&status, SCARG(uap, status), sizeof(status));
if (error)
return error;
}
return 0;
}
/* Find a stopped child (for 'wait4' and it's friends in the compat world) */
int
find_stopped_child(struct proc *parent, pid_t pid, int options,
struct proc **child_p)
{
struct proc *child;
int c_found, error;
do {
c_found = 0;
LIST_FOREACH(child, &parent->p_children, p_sibling) {
if (pid != WAIT_ANY && child->p_pid != pid &&
child->p_pgid != -pid)
continue;
/*
* Wait for processes with p_exitsig != SIGCHLD
* processes only if WALTSIG is set; wait for
* processes with p_exitsig == SIGCHLD only if
* WALTSIG is clear.
*/
if (!(options & WALLSIG) &&
(options & WALTSIG ? child->p_exitsig == SIGCHLD
: P_EXITSIG(child) != SIGCHLD))
continue;
c_found = 1;
if (child->p_stat == SZOMB && !(options & WNOZOMBIE)) {
*child_p = child;
return 0;
}
if (child->p_stat == SSTOP &&
!(child->p_flag & P_WAITED) &&
(child->p_flag & P_TRACED || options & WUNTRACED)) {
if (!(options & WNOWAIT))
child->p_flag |= P_WAITED;
*child_p = child;
return 0;
}
}
if (c_found == 0)
return ECHILD;
if (options & WNOHANG) {
*child_p = 0;
return 0;
}
error = tsleep(parent, PWAIT | PCATCH, "wait", 0);
} while (!error);
return error;
}
/* Free a process after parent has taken all the state info... */
void
proc_free( struct proc *p )
{
struct proc *parent = p->p_pptr;
int s;
/*
* If we got the child via ptrace(2) or procfs, and
* the parent is different (meaning the process was
* attached, rather than run as a child), then we need
* to give it back to the old parent, and send the
* parent the exit signal. The rest of the cleanup
* will be done when the old parent waits on the child.
*/
if ((p->p_flag & P_TRACED) && p->p_opptr != parent) {
parent = p->p_opptr;
p->p_opptr = NULL;
if (!parent)
parent = initproc;
proc_reparent(p, parent);
p->p_flag &= ~(P_TRACED|P_WAITED|P_FSTRACE);
if (p->p_exitsig != 0)
psignal(parent, P_EXITSIG(p));
wakeup(parent);
return;
}
scheduler_wait_hook(parent, p);
p->p_xstat = 0;
ruadd(&parent->p_stats->p_cru, p->p_ru);
pool_put(&rusage_pool, p->p_ru);
/* At this point we are going to start freeing the final resources.
If anyone tries to access the proc structure after here they
will get a shock - bits are missing.
Attempt to make it hard!
*/
leavepgrp(p);
s = proclist_lock_write();
LIST_REMOVE(p, p_list); /* probably off zombproc */
LIST_REMOVE(p, p_sibling);
proclist_unlock_write(s);
/* There shouldn't be any references left (unless someone
is sleeping with a ptr to us). */
/*
* 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);
pool_put(&pcred_pool, p->p_cred);
}
/*
* Release reference to text vnode
*/
if (p->p_textvp)
vrele(p->p_textvp);
pool_put(&proc_pool, p);
nprocs--;
}
David
--
David Laight: david@l8s.co.uk