Subject: removing duplicated code from compat version of sys_waitsys
To: None <tech-kern@netbsd.org>
From: David Laight <david@l8s.co.uk>
List: tech-kern
Date: 02/04/2003 16:03:06
The code of sys_waitsys() is replicated in 5 different parts of the
kernel source.  The routine in non-trivial and it is quite easy for
the routines to get out of sync - as indeed they are at the moment.

The change below splits syswait_sys() into 3, only the smaller
part has to be replicated in the 'compat' routines.

The 2 new routines are:
    int find_stopped_child(struct proc *, pid_t, int, struct proc **);
which searches for a child process in an appropriate state, and:
    void proc_free(struct proc *);
which frees all the data areas associated with a process after it is
finished with.

I'm fairly happy that the standard sys_waitsys() if fine, however
I can't test the compat versions so it would be useful if someone
would volunteer.  In particulat compat/irix only applies to sgimips,
compat/netbsd32 and compat/svr4_32 to sparc64 and I don't have
any binaries for compat/svr4.

A job control shell probably tests the important paths - the most
likely errors are (as always) inverted comparisons.

Oh, christos thinks this is a good idea :-)

These diffs make the changes look much worse than they actually are...

	David

Index: sys/sys/proc.h
===================================================================
RCS file: /cvsroot/src/sys/sys/proc.h,v
retrieving revision 1.156
diff -u -r1.156 proc.h
--- sys/sys/proc.h	2003/02/01 06:23:51	1.156
+++ sys/sys/proc.h	2003/02/04 13:17:27
@@ -437,6 +437,8 @@
 void	reaper(void *);
 void	exit1(struct lwp *, int);
 void	exit2(struct lwp *);
+int	find_stopped_child(struct proc *, pid_t, int, struct proc **);
+void	proc_free(struct proc *);
 void	exit_lwps(struct lwp *l);
 int	fork1(struct lwp *, int, int, void *, size_t,
 	    void (*)(void *), void *, register_t *, struct proc **);
Index: sys/sys/wait.h
===================================================================
RCS file: /cvsroot/src/sys/sys/wait.h,v
retrieving revision 1.17
diff -u -r1.17 wait.h
--- sys/sys/wait.h	2001/07/18 19:10:26	1.17
+++ sys/sys/wait.h	2003/02/04 13:17:28
@@ -95,6 +95,13 @@
  */
 #define	__WCLONE	WALTSIG
 #define	__WALL		WALLSIG
+
+/* 
+ * These bits are used in order to support SVR4 (etc) functionality
+ * without replicating sys_wait4 5 times.
+ */
+#define	WNOWAIT		0x00010000	/* Don't mark child 'P_WAITED' */ 
+#define	WNOZOMBIE	0x00020000	/* Ignore zombies */
 #endif /* ! _POSIX_SOURCE */
 
 #ifndef _POSIX_SOURCE
Index: sys/kern/kern_exit.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_exit.c,v
retrieving revision 1.108
diff -u -r1.108 kern_exit.c
--- sys/kern/kern_exit.c	2003/01/27 20:30:32	1.108
+++ sys/kern/kern_exit.c	2003/02/04 13:17:24
@@ -610,140 +612,186 @@
 		syscallarg(int)			options;
 		syscallarg(struct rusage *)	rusage;
 	} */ *uap = v;
-	struct proc	*p, *q, *t;
-	int		nfound, status, error, s;
+	struct proc	*child, *parent;
+	int		status, error;
 
-	q = l->l_proc;
+	parent = l->l_proc;
 
 	if (SCARG(uap, pid) == 0)
-		SCARG(uap, pid) = -q->p_pgid;
-	if (SCARG(uap, options) &~ (WUNTRACED|WNOHANG|WALTSIG))
+		SCARG(uap, pid) = -parent->p_pgid;
+	if (SCARG(uap, options) & ~(WUNTRACED|WNOHANG|WALTSIG|WALLSIG))
 		return (EINVAL);
 
- loop:
-	nfound = 0;
-	LIST_FOREACH(p, &q->p_children, p_sibling) {
-		if (SCARG(uap, pid) != WAIT_ANY &&
-		    p->p_pid != SCARG(uap, pid) &&
-		    p->p_pgid != -SCARG(uap, 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 (((SCARG(uap, options) & WALLSIG) == 0) &&
-		    ((SCARG(uap, options) & WALTSIG) ?
-		     (p->p_exitsig == SIGCHLD) : (P_EXITSIG(p) != SIGCHLD)))
-			continue;
+	error = find_stopped_child(parent, SCARG(uap,pid), SCARG(uap,options),
+				&child);
+	if (error != 0)
+		return error;
+	if (child == NULL) {
+		*retval = 0;
+		return 0;
+	}
 
-		nfound++;
-		if (p->p_stat == SZOMB) {
-			retval[0] = p->p_pid;
-
-			if (SCARG(uap, status)) {
-				status = p->p_xstat;	/* convert to int */
-				error = copyout((caddr_t)&status,
-						(caddr_t)SCARG(uap, status),
-						sizeof(status));
-				if (error)
-					return (error);
-			}
-			if (SCARG(uap, rusage) &&
-			    (error = copyout((caddr_t)p->p_ru,
-			    (caddr_t)SCARG(uap, rusage),
-			    sizeof(struct rusage))))
-				return (error);
-			/*
-			 * 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 != p->p_pptr){
-				t = p->p_opptr;
-				proc_reparent(p, t ? t : initproc);
-				p->p_opptr = NULL;
-				p->p_flag &= ~(P_TRACED|P_WAITED|P_FSTRACE);
-				if (p->p_exitsig != 0)
-					psignal(p->p_pptr, P_EXITSIG(p));
-				wakeup((caddr_t)p->p_pptr);
-				return (0);
-			}
-			scheduler_wait_hook(q, p);
-			p->p_xstat = 0;
-			ruadd(&q->p_stats->p_cru, p->p_ru);
-			pool_put(&rusage_pool, p->p_ru);
+	retval[0] = child->p_pid;
 
-			/*
-			 * Finally finished with old proc entry.
-			 * Unlink it from its process group and free it.
-			 */
-			leavepgrp(p);
+	if (child->p_stat == SZOMB) {
+		if (SCARG(uap, status)) {
+			status = child->p_xstat;	/* convert to int */
+			error = copyout((caddr_t)&status,
+					(caddr_t)SCARG(uap, status),
+					sizeof(status));
+			if (error)
+				return (error);
+		}
+		if (SCARG(uap, rusage)) {
+			error = copyout((caddr_t)child->p_ru,
+				    (caddr_t)SCARG(uap, rusage),
+				    sizeof(struct rusage));
+			if (error)
+				return (error);
+		}
 
-			s = proclist_lock_write();
-			LIST_REMOVE(p, p_list);	/* off zombproc */
-			proclist_unlock_write(s);
+		proc_free(child);
+		return 0;
+	}
 
-			LIST_REMOVE(p, p_sibling);
+	/* child state must be SSTOP */
+	if (SCARG(uap, status)) {
+		status = W_STOPCODE(child->p_xstat);
+		return copyout((caddr_t)&status,
+				(caddr_t)SCARG(uap, status),
+				sizeof(status));
+	}
+	return 0;
+}
 
-			/*
-			 * Decrement the count of procs running with this uid.
-			 */
-			(void)chgproccnt(p->p_cred->p_ruid, -1);
+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;
 			/*
-			 * Free up credentials.
+			 * 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 (--p->p_cred->p_refcnt == 0) {
-				crfree(p->p_cred->pc_ucred);
-				pool_put(&pcred_pool, p->p_cred);
+			if (((options & WALLSIG) == 0) &&
+			    (options & WALTSIG ? child->p_exitsig == SIGCHLD
+						: P_EXITSIG(child) != SIGCHLD))
+				continue;
+
+			c_found = 1;
+			if (child->p_stat == SZOMB &&
+			    (options & WNOZOMBIE) == 0) {
+				*child_p = child;
+				return 0;
 			}
-
-			/*
-			 * Release reference to text vnode
-			 */
-			if (p->p_textvp)
-				vrele(p->p_textvp);
 
-			/*
-			 * Release any SA state
-			 */
-			if (p->p_sa) {
-				free(p->p_sa->sa_stacks, M_SA);
-				pool_put(&sadata_pool, p->p_sa);
+			if (child->p_stat == SSTOP &&
+			    (child->p_flag & P_WAITED) == 0 &&
+			    (child->p_flag & P_TRACED || options & WUNTRACED)) {
+				if ((options & WNOWAIT) == 0)
+					child->p_flag |= P_WAITED;
+				*child_p = child;
+				return 0;
 			}
-
-			pool_put(&proc_pool, p);
-			nprocs--;
-			return (0);
 		}
-		if (p->p_stat == SSTOP && (p->p_flag & P_WAITED) == 0 &&
-		    (p->p_flag & P_TRACED || SCARG(uap, options) & WUNTRACED)) {
-			p->p_flag |= P_WAITED;
-			retval[0] = p->p_pid;
-
-			if (SCARG(uap, status)) {
-				status = W_STOPCODE(p->p_xstat);
-				error = copyout((caddr_t)&status,
-				    (caddr_t)SCARG(uap, status),
-				    sizeof(status));
-			} else
-				error = 0;
-			return (error);
+		if (c_found == 0)
+			return (ECHILD);
+		if (options & WNOHANG) {
+			*child_p = 0;
+			return 0;
 		}
+		error = tsleep((caddr_t)parent, PWAIT | PCATCH, "wait", 0);
+	} while (error == 0);
+	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;
+		if (parent == NULL)
+			parent = initproc;
+		proc_reparent(p, parent);
+		p->p_opptr = NULL;
+		p->p_flag &= ~(P_TRACED|P_WAITED|P_FSTRACE);
+		if (p->p_exitsig != 0)
+			psignal(parent, P_EXITSIG(p));
+		wakeup((caddr_t)parent);
+		return;
 	}
-	if (nfound == 0)
-		return (ECHILD);
-	if (SCARG(uap, options) & WNOHANG) {
-		retval[0] = 0;
-		return (0);
-	}
-	if ((error = tsleep((caddr_t)q, PWAIT | PCATCH, "wait", 0)) != 0)
-		return (error);
-	goto loop;
+
+	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);
+
+	/*
+	 * Finally finished with old proc entry.
+	 * Unlink it from its process group and free it.
+	 */
+	leavepgrp(p);
+
+	s = proclist_lock_write();
+	LIST_REMOVE(p, p_list);	/* off zombproc */
+	proclist_unlock_write(s);
+
+	LIST_REMOVE(p, p_sibling);
+
+	/*
+	 * 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);
+
+	/*
+	 * Release any SA state
+	 */
+	if (p->p_sa) {
+		free(p->p_sa->sa_stacks, M_SA);
+		pool_put(&sadata_pool, p->p_sa);
+	}
+
+	pool_put(&proc_pool, p);
+	nprocs--;
+	return;
 }
 
 /*
Index: sys/compat/irix/irix_signal.c
===================================================================
RCS file: /cvsroot/src/sys/compat/irix/irix_signal.c,v
retrieving revision 1.24
diff -u -r1.24 irix_signal.c
--- sys/compat/irix/irix_signal.c	2003/01/22 12:58:23	1.24
+++ sys/compat/irix/irix_signal.c	2003/02/04 13:17:09
@@ -837,16 +837,17 @@
 		syscallarg(int) options;
 		syscallarg(struct rusage *) ru;
 	} */ *uap = v;
-	struct proc *p = l->l_proc;
+	struct proc *parent = l->l_proc;
 	int nfound, error, s;
-	struct proc *q, *t;
+	struct proc *child, *t;
+	int options;
 
 	switch (SCARG(uap, type)) {
 	case SVR4_P_PID:	
 		break;
 
 	case SVR4_P_PGID:
-		SCARG(uap, pid) = -p->p_pgid;
+		SCARG(uap, pid) = -parent->p_pgid;
 		break;
 
 	case SVR4_P_ALL:
@@ -863,125 +864,57 @@
 		 SCARG(uap, info), SCARG(uap, options), SCARG(uap, ru));
 #endif
 
-loop:
-	nfound = 0;
-	for (q = p->p_children.lh_first; q != 0; q = q->p_sibling.le_next) {
-		if (SCARG(uap, pid) != WAIT_ANY &&
-		    q->p_pid != SCARG(uap, pid) &&
-		    q->p_pgid != -SCARG(uap, pid)) {
-#ifdef DEBUG_IRIX
-			printf("pid %d pgid %d != %d\n", q->p_pid,
-				 q->p_pgid, SCARG(uap, pid));
-#endif
-			continue;
-		}
-		nfound++;
-		if (q->p_stat == SZOMB && 
-		    ((SCARG(uap, options) & (SVR4_WEXITED|SVR4_WTRAPPED)))) {
-			*retval = 0;
+	/* Translate options */
+	options = 0;
+	if (SCARG(uap, options) & SVR4_WNOWAIT)
+		options |= WNOWAIT;
+	if (SCARG(uap, options) & SVR4_WNOHANG)
+		options |= WNOHANG;
+	if ((SCARG(uap, options) & (SVR4_WEXITED|SVR4_WTRAPPED)) == 0)
+		options |= WNOZOMBIE;
+	if (SCARG(uap, options) & (SVR4_WSTOPPED|SVR4_WCONTINUED))
+		options |= WUNTRACED;
+
+	error = find_stopped_child(parent, SCARG(uap,pid), options, &child);
+	if (error != 0)
+		return error;
+	*retval = 0;
+	if (child == NULL)
+		return irix_wait_siginfo(NULL, 0, SCARG(uap, info));
+
+	if (child->p_stat == SZOMB) {
 #ifdef DEBUG_IRIX
-			printf("irix_sys_wait(): found %d\n", q->p_pid);
+		printf("irix_sys_wait(): found %d\n", child->p_pid);
 #endif
-			if ((error = irix_wait_siginfo(q, q->p_xstat,
+		if ((error = irix_wait_siginfo(child, child->p_xstat,
 						  SCARG(uap, info))) != 0)
-				return error;
+			return error;
 
 
-			if ((SCARG(uap, options) & SVR4_WNOWAIT)) {
+		if ((SCARG(uap, options) & SVR4_WNOWAIT)) {
 #ifdef DEBUG_IRIX
-				printf(("irix_sys_wait(): Don't wait\n"));
+			printf(("irix_sys_wait(): Don't wait\n"));
 #endif
-				return 0;
-			}
-			if (SCARG(uap, ru) &&
-			    (error = copyout(&(p->p_stats->p_ru),
-			    (caddr_t)SCARG(uap, ru), sizeof(struct rusage))))
-				return (error);
-
-			/*
-			 * 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 a SIGCHLD.  The rest of the cleanup will be
-			 * done when the old parent waits on the child.
-			 */
-			if ((q->p_flag & P_TRACED) && q->p_opptr != q->p_pptr){
-				t = q->p_opptr;
-				proc_reparent(q, t ? t : initproc);
-				q->p_opptr = NULL;
-				q->p_flag &= ~(P_TRACED|P_WAITED|P_FSTRACE);
-				psignal(q->p_pptr, SIGCHLD);
-				wakeup((caddr_t)q->p_pptr);
-				return (0);
-			}
-			q->p_xstat = 0;
-			ruadd(&p->p_stats->p_cru, q->p_ru);
-			pool_put(&rusage_pool, q->p_ru);
-
-			/*
-			 * Finally finished with old proc entry.
-			 * Unlink it from its process group and free it.
-			 */
-			leavepgrp(q);
-
-			s = proclist_lock_write();
-			LIST_REMOVE(q, p_list);	/* off zombproc */
-			proclist_unlock_write(s);
-
-			LIST_REMOVE(q, p_sibling);
-
-			/*
-			 * Decrement the count of procs running with this uid.
-			 */
-			(void)chgproccnt(q->p_cred->p_ruid, -1);
-
-			/*
-			 * Free up credentials.
-			 */
-			if (--q->p_cred->p_refcnt == 0) {
-				crfree(q->p_cred->pc_ucred);
-				pool_put(&pcred_pool, q->p_cred);
-			}
-
-			/*
-			 * Release reference to text vnode
-			 */
-			if (q->p_textvp)
-				vrele(q->p_textvp);
-
-			pool_put(&proc_pool, q);
-			nprocs--;
 			return 0;
-		}
-		if (q->p_stat == SSTOP && (q->p_flag & P_WAITED) == 0 &&
-		    (q->p_flag & P_TRACED ||
-		     (SCARG(uap, options) & (SVR4_WSTOPPED|SVR4_WCONTINUED)))) {
-#ifdef DEBUG_IRIX
-			printf("jobcontrol %d\n", q->p_pid);
-#endif
-			if (((SCARG(uap, options) & SVR4_WNOWAIT)) == 0)
-				q->p_flag |= P_WAITED;
-			*retval = 0;
-			return irix_wait_siginfo(q, W_STOPCODE(q->p_xstat),
-					    SCARG(uap, info));
 		}
-	}
-
-	if (nfound == 0)
-		return ECHILD;
-
-	if (SCARG(uap, options) & SVR4_WNOHANG) {
-		*retval = 0;
-		if ((error = irix_wait_siginfo(NULL, 0, SCARG(uap, info))) != 0)
+		if (SCARG(uap, ru) &&
+		    /* XXX (dsl) is this copying out the right data???
+		       child->p_ru would seem more appropriate! */
+		    (error = copyout(&(parent->p_stats->p_ru),
+		    (caddr_t)SCARG(uap, ru), sizeof(struct rusage))))
 			return error;
+
+		proc_free(child);
 		return 0;
 	}
 
-	if ((error = tsleep((caddr_t)p, PWAIT | PCATCH, "svr4_wait", 0)) != 0)
-		return error;
-	goto loop;
+	/* Child state must be SSTOP */
 
+#ifdef DEBUG_IRIX
+	printf("jobcontrol %d\n", child->p_pid);
+#endif
+	return irix_wait_siginfo(child, W_STOPCODE(child->p_xstat),
+				    SCARG(uap, info));
 }
 
 int 
Index: sys/compat/netbsd32/netbsd32_wait.c
===================================================================
RCS file: /cvsroot/src/sys/compat/netbsd32/netbsd32_wait.c,v
retrieving revision 1.5
diff -u -r1.5 netbsd32_wait.c
--- sys/compat/netbsd32/netbsd32_wait.c	2003/01/18 08:28:26	1.5
+++ sys/compat/netbsd32/netbsd32_wait.c	2003/02/04 13:17:10
@@ -58,121 +58,54 @@
 		syscallarg(int) options;
 		syscallarg(netbsd32_rusagep_t) rusage;
 	} */ *uap = v;
-	struct proc *q = l->l_proc;
+	struct proc *parent = l->l_proc;
 	struct netbsd32_rusage ru32;
-	int nfound;
-	struct proc *p, *t;
+	struct proc *child;
 	int status, error;
 
 	if (SCARG(uap, pid) == 0)
-		SCARG(uap, pid) = -q->p_pgid;
+		SCARG(uap, pid) = -parent->p_pgid;
 	if (SCARG(uap, options) &~ (WUNTRACED|WNOHANG))
 		return (EINVAL);
 
-loop:
-	nfound = 0;
-	for (p = q->p_children.lh_first; p != 0; p = p->p_sibling.le_next) {
-		if (SCARG(uap, pid) != WAIT_ANY &&
-		    p->p_pid != SCARG(uap, pid) &&
-		    p->p_pgid != -SCARG(uap, pid))
-			continue;
-		nfound++;
-		if (p->p_stat == SZOMB) {
-			retval[0] = p->p_pid;
+	error =  find_stopped_child(parent, SCARG(uap,pid), SCARG(uap,options),
+					&child);
+	if (error != 0)
+		return error;
+	if (child == NULL) {
+		retval[0] = 0;
+		return 0;
+	}
 
-			if (SCARG(uap, status)) {
-				status = p->p_xstat;	/* convert to int */
-				error = copyout((caddr_t)&status,
+	retval[0] = child->p_pid;
+	if (child->p_stat == SZOMB) {
+		if (SCARG(uap, status)) {
+			status = child->p_xstat;	/* convert to int */
+			error = copyout((caddr_t)&status,
 				    (caddr_t)NETBSD32PTR64(SCARG(uap, status)),
 				    sizeof(status));
-				if (error)
-					return (error);
-			}
-			if (SCARG(uap, rusage)) {
-				netbsd32_from_rusage(p->p_ru, &ru32);
-				if ((error = copyout((caddr_t)&ru32,
-				    (caddr_t)NETBSD32PTR64(SCARG(uap, rusage)),
-				    sizeof(struct netbsd32_rusage))))
-					return (error);
-			}
-			/*
-			 * 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 a SIGCHLD.  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 != p->p_pptr){
-				t = p->p_opptr;
-				proc_reparent(p, t ? t : initproc);
-				p->p_opptr = NULL;
-				p->p_flag &= ~(P_TRACED|P_WAITED|P_FSTRACE);
-				psignal(p->p_pptr, SIGCHLD);
-				wakeup((caddr_t)p->p_pptr);
-				return (0);
-			}
-			p->p_xstat = 0;
-			ruadd(&q->p_stats->p_cru, p->p_ru);
-			pool_put(&rusage_pool, p->p_ru);
-
-			/*
-			 * Finally finished with old proc entry.
-			 * Unlink it from its process group and free it.
-			 */
-			leavepgrp(p);
-
-			LIST_REMOVE(p, p_list);	/* off zombproc */
-
-			LIST_REMOVE(p, p_sibling);
-
-			/*
-			 * 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--;
-			return (0);
+			if (error)
+				return error;
 		}
-		if (p->p_stat == SSTOP && (p->p_flag & P_WAITED) == 0 &&
-		    (p->p_flag & P_TRACED || SCARG(uap, options) & WUNTRACED)) {
-			p->p_flag |= P_WAITED;
-			retval[0] = p->p_pid;
-
-			if (SCARG(uap, status)) {
-				status = W_STOPCODE(p->p_xstat);
-				error = copyout((caddr_t)&status,
-				    (caddr_t)NETBSD32PTR64(SCARG(uap, status)),
-				    sizeof(status));
-			} else
-				error = 0;
-			return (error);
+		if (SCARG(uap, rusage)) {
+			netbsd32_from_rusage(child->p_ru, &ru32);
+			error = copyout((caddr_t)&ru32,
+				    (caddr_t)NETBSD32PTR64(SCARG(uap, rusage)),
+				    sizeof(struct netbsd32_rusage));
+			if (error)
+				return error;
 		}
+		proc_free(child);
+		return 0;
 	}
-	if (nfound == 0)
-		return (ECHILD);
-	if (SCARG(uap, options) & WNOHANG) {
-		retval[0] = 0;
-		return (0);
+
+	if (SCARG(uap, status)) {
+		status = W_STOPCODE(child->p_xstat);
+		return copyout((caddr_t)&status,
+			    (caddr_t)NETBSD32PTR64(SCARG(uap, status)),
+			    sizeof(status));
 	}
-	if ((error = tsleep((caddr_t)q, PWAIT | PCATCH, "wait", 0)) != 0)
-		return (error);
-	goto loop;
+	return 0;
 }
 
 int
Index: sys/compat/svr4/svr4_misc.c
===================================================================
RCS file: /cvsroot/src/sys/compat/svr4/svr4_misc.c,v
retrieving revision 1.101
diff -u -r1.101 svr4_misc.c
--- sys/compat/svr4/svr4_misc.c	2003/01/29 07:00:39	1.101
+++ sys/compat/svr4/svr4_misc.c	2003/02/04 13:17:15
@@ -1182,17 +1182,17 @@
 	register_t *retval;
 {
 	struct svr4_sys_waitsys_args *uap = v;
-	int nfound, error, s;
-	struct proc *p = l->l_proc;
-	struct proc *q, *t;
+	int options;
+	int error;
+	struct proc *parent = l->l_proc;
+	struct proc *child;
 
-
 	switch (SCARG(uap, grp)) {
 	case SVR4_P_PID:	
 		break;
 
 	case SVR4_P_PGID:
-		SCARG(uap, id) = -p->p_pgid;
+		SCARG(uap, id) = -parent->p_pgid;
 		break;
 
 	case SVR4_P_ALL:
@@ -1203,116 +1203,47 @@
 		return EINVAL;
 	}
 
+	/* Translate options */
+	options = 0;
+	if (SCARG(uap, options) & SVR4_WNOWAIT)
+		options |= WNOWAIT;
+	if (SCARG(uap, options) & SVR4_WNOHANG)
+		options |= WNOHANG;
+	if ((SCARG(uap, options) & (SVR4_WEXITED|SVR4_WTRAPPED)) == 0)
+		options |= WNOZOMBIE;
+	if (SCARG(uap, options) & (SVR4_WSTOPPED|SVR4_WCONTINUED))
+		options |= WUNTRACED;
+
 	DPRINTF(("waitsys(%d, %d, %p, %x)\n", 
 	         SCARG(uap, grp), SCARG(uap, id),
 		 SCARG(uap, info), SCARG(uap, options)));
 
-loop:
-	nfound = 0;
-	for (q = p->p_children.lh_first; q != 0; q = q->p_sibling.le_next) {
-		if (SCARG(uap, id) != WAIT_ANY &&
-		    q->p_pid != SCARG(uap, id) &&
-		    q->p_pgid != -SCARG(uap, id)) {
-			DPRINTF(("pid %d pgid %d != %d\n", q->p_pid,
-				 q->p_pgid, SCARG(uap, id)));
-			continue;
-		}
-		nfound++;
-		if (q->p_stat == SZOMB && 
-		    ((SCARG(uap, options) & (SVR4_WEXITED|SVR4_WTRAPPED)))) {
-			*retval = 0;
-			DPRINTF(("found %d\n", q->p_pid));
-			if ((error = svr4_setinfo(q, q->p_xstat,
-						  SCARG(uap, info))) != 0)
-				return error;
-
-
-		        if ((SCARG(uap, options) & SVR4_WNOWAIT)) {
-				DPRINTF(("Don't wait\n"));
-				return 0;
-			}
-
-			/*
-			 * 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 a SIGCHLD.  The rest of the cleanup will be
-			 * done when the old parent waits on the child.
-			 */
-			if ((q->p_flag & P_TRACED) && q->p_opptr != q->p_pptr){
-				t = q->p_opptr;
-				proc_reparent(q, t ? t : initproc);
-				q->p_opptr = NULL;
-				q->p_flag &= ~(P_TRACED|P_WAITED|P_FSTRACE);
-				psignal(q->p_pptr, SIGCHLD);
-				wakeup((caddr_t)q->p_pptr);
-				return (0);
-			}
-			q->p_xstat = 0;
-			ruadd(&p->p_stats->p_cru, q->p_ru);
-			pool_put(&rusage_pool, q->p_ru);
-
-			/*
-			 * Finally finished with old proc entry.
-			 * Unlink it from its process group and free it.
-			 */
-			leavepgrp(q);
-
-			s = proclist_lock_write();
-			LIST_REMOVE(q, p_list);	/* off zombproc */
-			proclist_unlock_write(s);
-
-			LIST_REMOVE(q, p_sibling);
-
-			/*
-			 * Decrement the count of procs running with this uid.
-			 */
-			(void)chgproccnt(q->p_cred->p_ruid, -1);
-
-			/*
-			 * Free up credentials.
-			 */
-			if (--q->p_cred->p_refcnt == 0) {
-				crfree(q->p_cred->pc_ucred);
-				pool_put(&pcred_pool, q->p_cred);
-			}
-
-			/*
-			 * Release reference to text vnode
-			 */
-			if (q->p_textvp)
-				vrele(q->p_textvp);
+	error = find_stopped_child(parent, SCARG(uap, id), options, &child);
+	if (error != 0)
+		return error;
+	*retval = 0;
+	if (child == NULL)
+		return svr4_setinfo(NULL, 0, SCARG(uap, info));
+
+	if (child->p_stat == SZOMB) {
+		DPRINTF(("found %d\n", child->p_pid));
+		if ((error = svr4_setinfo(child, child->p_xstat,
+					  SCARG(uap, info))) != 0)
+			return error;
+
 
-			pool_put(&proc_pool, q);
-			nprocs--;
+		if ((SCARG(uap, options) & SVR4_WNOWAIT)) {
+			DPRINTF(("Don't wait\n"));
 			return 0;
 		}
-		if (q->p_stat == SSTOP && (q->p_flag & P_WAITED) == 0 &&
-		    (q->p_flag & P_TRACED ||
-		     (SCARG(uap, options) & (SVR4_WSTOPPED|SVR4_WCONTINUED)))) {
-			DPRINTF(("jobcontrol %d\n", q->p_pid));
-		        if (((SCARG(uap, options) & SVR4_WNOWAIT)) == 0)
-				q->p_flag |= P_WAITED;
-			*retval = 0;
-			return svr4_setinfo(q, W_STOPCODE(q->p_xstat),
-					    SCARG(uap, info));
-		}
-	}
-
-	if (nfound == 0)
-		return ECHILD;
 
-	if (SCARG(uap, options) & SVR4_WNOHANG) {
-		*retval = 0;
-		if ((error = svr4_setinfo(NULL, 0, SCARG(uap, info))) != 0)
-			return error;
+		proc_free(child);
 		return 0;
 	}
 
-	if ((error = tsleep((caddr_t)p, PWAIT | PCATCH, "svr4_wait", 0)) != 0)
-		return error;
-	goto loop;
+	DPRINTF(("jobcontrol %d\n", child->p_pid));
+	return svr4_setinfo(child, W_STOPCODE(child->p_xstat),
+			    SCARG(uap, info));
 }
 
 
Index: sys/compat/svr4_32/svr4_32_misc.c
===================================================================
RCS file: /cvsroot/src/sys/compat/svr4_32/svr4_32_misc.c,v
retrieving revision 1.16
diff -u -r1.16 svr4_32_misc.c
--- sys/compat/svr4_32/svr4_32_misc.c	2003/01/29 07:00:40	1.16
+++ sys/compat/svr4_32/svr4_32_misc.c	2003/02/04 13:17:20
@@ -1201,17 +1201,16 @@
 	register_t *retval;
 {
 	struct svr4_32_sys_waitsys_args *uap = v;
-	struct proc *p = l->l_proc;
-	int nfound, error, s;
-	struct proc *q, *t;
+	struct proc *parent = l->l_proc;
+	int options, error;
+	struct proc *child;
 
-
 	switch (SCARG(uap, grp)) {
 	case SVR4_P_PID:	
 		break;
 
 	case SVR4_P_PGID:
-		SCARG(uap, id) = -p->p_pgid;
+		SCARG(uap, id) = -parent->p_pgid;
 		break;
 
 	case SVR4_P_ALL:
@@ -1226,112 +1225,42 @@
 	         SCARG(uap, grp), SCARG(uap, id),
 		 SCARG(uap, info), SCARG(uap, options)));
 
-loop:
-	nfound = 0;
-	for (q = p->p_children.lh_first; q != 0; q = q->p_sibling.le_next) {
-		if (SCARG(uap, id) != WAIT_ANY &&
-		    q->p_pid != SCARG(uap, id) &&
-		    q->p_pgid != -SCARG(uap, id)) {
-			DPRINTF(("pid %d pgid %d != %d\n", q->p_pid,
-				 q->p_pgid, SCARG(uap, id)));
-			continue;
-		}
-		nfound++;
-		if (q->p_stat == SZOMB && 
-		    ((SCARG(uap, options) & (SVR4_WEXITED|SVR4_WTRAPPED)))) {
-			*retval = 0;
-			DPRINTF(("found %d\n", q->p_pid));
-			if ((error = 
-			     svr4_32_setinfo(q, q->p_xstat, SCARG(uap, info))) != 0)
-				return error;
-
-
-		        if ((SCARG(uap, options) & SVR4_WNOWAIT)) {
-				DPRINTF(("Don't wait\n"));
-				return 0;
-			}
-
-			/*
-			 * 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 a SIGCHLD.  The rest of the cleanup will be
-			 * done when the old parent waits on the child.
-			 */
-			if ((q->p_flag & P_TRACED) && q->p_opptr != q->p_pptr){
-				t = q->p_opptr;
-				proc_reparent(q, t ? t : initproc);
-				q->p_opptr = NULL;
-				q->p_flag &= ~(P_TRACED|P_WAITED|P_FSTRACE);
-				psignal(q->p_pptr, SIGCHLD);
-				wakeup((caddr_t)q->p_pptr);
-				return (0);
-			}
-			q->p_xstat = 0;
-			ruadd(&p->p_stats->p_cru, q->p_ru);
-			pool_put(&rusage_pool, q->p_ru);
-
-			/*
-			 * Finally finished with old proc entry.
-			 * Unlink it from its process group and free it.
-			 */
-			leavepgrp(q);
-
-			s = proclist_lock_write();
-			LIST_REMOVE(q, p_list);	/* off zombproc */
-			proclist_unlock_write(s);
-
-			LIST_REMOVE(q, p_sibling);
-
-			/*
-			 * Decrement the count of procs running with this uid.
-			 */
-			(void)chgproccnt(q->p_cred->p_ruid, -1);
-
-			/*
-			 * Free up credentials.
-			 */
-			if (--q->p_cred->p_refcnt == 0) {
-				crfree(q->p_cred->pc_ucred);
-				pool_put(&pcred_pool, q->p_cred);
-			}
-
-			/*
-			 * Release reference to text vnode
-			 */
-			if (q->p_textvp)
-				vrele(q->p_textvp);
+	/* Translate options */
+	options = 0;
+	if (SCARG(uap, options) & SVR4_WNOWAIT)
+		options |= WNOWAIT;
+	if (SCARG(uap, options) & SVR4_WNOHANG)
+		options |= WNOHANG;
+	if ((SCARG(uap, options) & (SVR4_WEXITED|SVR4_WTRAPPED)) == 0)
+		options |= WNOZOMBIE;
+	if (SCARG(uap, options) & (SVR4_WSTOPPED|SVR4_WCONTINUED))
+		options |= WUNTRACED;
+
+	error = find_stopped_child(parent, SCARG(uap, id), options, &child);
+	if (error != 0)
+		return error;
+	*retval = 0;
+	if (child == NULL)
+		return svr4_32_setinfo(NULL, 0, SCARG(uap, info));
+
+	if (child->p_stat == SZOMB) {
+		DPRINTF(("found %d\n", child->p_pid));
+		error = svr4_32_setinfo(child, child->p_xstat, SCARG(uap,info));
+		if (error)
+			return error;
 
-			pool_put(&proc_pool, q);
-			nprocs--;
+		if ((SCARG(uap, options) & SVR4_WNOWAIT)) {
+			DPRINTF(("Don't wait\n"));
 			return 0;
 		}
-		if (q->p_stat == SSTOP && (q->p_flag & P_WAITED) == 0 &&
-		    (q->p_flag & P_TRACED ||
-		     (SCARG(uap, options) & (SVR4_WSTOPPED|SVR4_WCONTINUED)))) {
-			DPRINTF(("jobcontrol %d\n", q->p_pid));
-		        if (((SCARG(uap, options) & SVR4_WNOWAIT)) == 0)
-				q->p_flag |= P_WAITED;
-			*retval = 0;
-			return svr4_32_setinfo(q, W_STOPCODE(q->p_xstat),
-					       SCARG(uap, info));
-		}
-	}
-
-	if (nfound == 0)
-		return ECHILD;
 
-	if (SCARG(uap, options) & SVR4_WNOHANG) {
-		*retval = 0;
-		if ((error = svr4_32_setinfo(NULL, 0, SCARG(uap, info))) != 0)
-			return error;
+		proc_free(child);
 		return 0;
 	}
 
-	if ((error = tsleep((caddr_t)p, PWAIT | PCATCH, "svr4_32_wait", 0)) != 0)
-		return error;
-	goto loop;
+	DPRINTF(("jobcontrol %d\n", child->p_pid));
+	return svr4_32_setinfo(child, W_STOPCODE(child->p_xstat),
+					       SCARG(uap, info));
 }
 
-- 
David Laight: david@l8s.co.uk