1/*
2 * waitpid.c --
3 *
4 *	This procedure emulates the POSIX waitpid kernel call on
5 *	BSD systems that don't have waitpid but do have wait3.
6 *	This code is based on a prototype version written by
7 *	Mark Diekhans and Karl Lehenbauer.
8 *
9 * Copyright (c) 1993 The Regents of the University of California.
10 * Copyright (c) 1994 Sun Microsystems, Inc.
11 *
12 * See the file "license.terms" for information on usage and redistribution
13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 *
15 * RCS: @(#) $Id: waitpid.c,v 1.3 2000/01/11 22:08:50 hobbs Exp $
16 */
17
18#include "tclInt.h"
19#include "tclPort.h"
20
21#ifndef pid_t
22#define pid_t int
23#endif
24
25/*
26 * A linked list of the following structures is used to keep track
27 * of processes for which we received notification from the kernel,
28 * but the application hasn't waited for them yet (this can happen
29 * because wait may not return the process we really want).  We
30 * save the information here until the application finally does
31 * wait for the process.
32 */
33
34typedef struct WaitInfo {
35    pid_t pid;				/* Pid of process that exited. */
36    WAIT_STATUS_TYPE status;		/* Status returned when child exited
37					 * or suspended. */
38    struct WaitInfo *nextPtr;		/* Next in list of exited processes. */
39} WaitInfo;
40
41static WaitInfo *deadList = NULL;	/* First in list of all dead
42					 * processes. */
43
44/*
45 *----------------------------------------------------------------------
46 *
47 * waitpid --
48 *
49 *	This procedure emulates the functionality of the POSIX
50 *	waitpid kernel call, using the BSD wait3 kernel call.
51 *	Note:  it doesn't emulate absolutely all of the waitpid
52 *	functionality, in that it doesn't support pid's of 0
53 *	or < -1.
54 *
55 * Results:
56 *	-1 is returned if there is an error in the wait kernel call.
57 *	Otherwise the pid of an exited or suspended process is
58 *	returned and *statusPtr is set to the status value of the
59 *	process.
60 *
61 * Side effects:
62 *	None.
63 *
64 *----------------------------------------------------------------------
65 */
66
67#ifdef waitpid
68#   undef waitpid
69#endif
70
71pid_t
72waitpid(pid, statusPtr, options)
73    pid_t pid;			/* The pid to wait on.  Must be -1 or
74				 * greater than zero. */
75    int *statusPtr;		/* Where to store wait status for the
76				 * process. */
77    int options;		/* OR'ed combination of WNOHANG and
78				 * WUNTRACED. */
79{
80    register WaitInfo *waitPtr, *prevPtr;
81    pid_t result;
82    WAIT_STATUS_TYPE status;
83
84    if ((pid < -1) || (pid == 0)) {
85	errno = EINVAL;
86	return -1;
87    }
88
89    /*
90     * See if there's a suitable process that has already stopped or
91     * exited. If so, remove it from the list of exited processes and
92     * return its information.
93     */
94
95    for (waitPtr = deadList, prevPtr = NULL; waitPtr != NULL;
96	    prevPtr = waitPtr, waitPtr = waitPtr->nextPtr) {
97	if ((pid != waitPtr->pid) && (pid != -1)) {
98	    continue;
99	}
100	if (!(options & WUNTRACED) && (WIFSTOPPED(waitPtr->status))) {
101	    continue;
102	}
103	result = waitPtr->pid;
104	*statusPtr = *((int *) &waitPtr->status);
105	if (prevPtr == NULL) {
106	    deadList = waitPtr->nextPtr;
107	} else {
108	    prevPtr->nextPtr = waitPtr->nextPtr;
109	}
110	ckfree((char *) waitPtr);
111	return result;
112    }
113
114    /*
115     * Wait for any process to stop or exit.  If it's an acceptable one
116     * then return it to the caller;  otherwise store information about it
117     * in the list of exited processes and try again.  On systems that
118     * have only wait but not wait3, there are several situations we can't
119     * handle, but we do the best we can (e.g. can still handle some
120     * combinations of options by invoking wait instead of wait3).
121     */
122
123    while (1) {
124#if NO_WAIT3
125	if (options & WNOHANG) {
126	    return 0;
127	}
128	if (options != 0) {
129	    errno = EINVAL;
130	    return -1;
131	}
132	result = wait(&status);
133#else
134	result = wait3(&status, options, 0);
135#endif
136	if ((result == -1) && (errno == EINTR)) {
137	    continue;
138	}
139	if (result <= 0) {
140	    return result;
141	}
142
143	if ((pid != result) && (pid != -1)) {
144	    goto saveInfo;
145	}
146	if (!(options & WUNTRACED) && (WIFSTOPPED(status))) {
147	    goto saveInfo;
148	}
149	*statusPtr = *((int *) &status);
150	return result;
151
152	/*
153	 * Can't return this info to caller.  Save it in the list of
154	 * stopped or exited processes.  Tricky point: first check for
155	 * an existing entry for the process and overwrite it if it
156	 * exists (e.g. a previously stopped process might now be dead).
157	 */
158
159	saveInfo:
160	for (waitPtr = deadList; waitPtr != NULL; waitPtr = waitPtr->nextPtr) {
161	    if (waitPtr->pid == result) {
162		waitPtr->status = status;
163		goto waitAgain;
164	    }
165	}
166	waitPtr = (WaitInfo *) ckalloc(sizeof(WaitInfo));
167	waitPtr->pid = result;
168	waitPtr->status = status;
169	waitPtr->nextPtr = deadList;
170	deadList = waitPtr;
171
172	waitAgain: continue;
173    }
174}
175