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