summaryrefslogtreecommitdiffstats
path: root/compat/waitpid.c
blob: e41361b5db45adc79270dc630158ddf7b199c4d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*
 * waitpid.c --
 *
 *	This procedure emulates the POSIX waitpid kernel call on BSD systems
 *	that don't have waitpid but do have wait3. This code is based on a
 *	prototype version written by Mark Diekhans and Karl Lehenbauer.
 *
 * Copyright (c) 1993 The Regents of the University of California.
 * Copyright (c) 1994 Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tclPort.h"

#ifndef pid_t
#define pid_t int
#endif

/*
 * A linked list of the following structures is used to keep track of
 * processes for which we received notification from the kernel, but the
 * application hasn't waited for them yet (this can happen because wait may
 * not return the process we really want). We save the information here until
 * the application finally does wait for the process.
 */

typedef struct WaitInfo {
    pid_t pid;			/* Pid of process that exited. */
    WAIT_STATUS_TYPE status;	/* Status returned when child exited or
				 * suspended. */
    struct WaitInfo *nextPtr;	/* Next in list of exited processes. */
} WaitInfo;

static WaitInfo *deadList = NULL;
				/* First in list of all dead processes. */

/*
 *----------------------------------------------------------------------
 *
 * waitpid --
 *
 *	This procedure emulates the functionality of the POSIX waitpid kernel
 *	call, using the BSD wait3 kernel call. Note: it doesn't emulate
 *	absolutely all of the waitpid functionality, in that it doesn't
 *	support pid's of 0 or < -1.
 *
 * Results:
 *	-1 is returned if there is an error in the wait kernel call. Otherwise
 *	the pid of an exited or suspended process is returned and *statusPtr
 *	is set to the status value of the process.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

#ifdef waitpid
#   undef waitpid
#endif

pid_t
waitpid(
    pid_t pid,			/* The pid to wait on. Must be -1 or greater
				 * than zero. */
    int *statusPtr,		/* Where to store wait status for the
				 * process. */
    int options)		/* OR'ed combination of WNOHANG and
				 * WUNTRACED. */
{
    WaitInfo *waitPtr, *prevPtr;
    pid_t result;
    WAIT_STATUS_TYPE status;

    if ((pid < -1) || (pid == 0)) {
	errno = EINVAL;
	return -1;
    }

    /*
     * See if there's a suitable process that has already stopped or exited.
     * If so, remove it from the list of exited processes and return its
     * information.
     */

    for (waitPtr = deadList, prevPtr = NULL; waitPtr != NULL;
	    prevPtr = waitPtr, waitPtr = waitPtr->nextPtr) {
	if ((pid != waitPtr->pid) && (pid != -1)) {
	    continue;
	}
	if (!(options & WUNTRACED) && (WIFSTOPPED(waitPtr->status))) {
	    continue;
	}
	result = waitPtr->pid;
	*statusPtr = *((int *) &waitPtr->status);
	if (prevPtr == NULL) {
	    deadList = waitPtr->nextPtr;
	} else {
	    prevPtr->nextPtr = waitPtr->nextPtr;
	}
	ckfree((char *) waitPtr);
	return result;
    }

    /*
     * Wait for any process to stop or exit. If it's an acceptable one then
     * return it to the caller; otherwise store information about it in the
     * list of exited processes and try again. On systems that have only wait
     * but not wait3, there are several situations we can't handle, but we do
     * the best we can (e.g. can still handle some combinations of options by
     * invoking wait instead of wait3).
     */

    while (1) {
#if NO_WAIT3
	if (options & WNOHANG) {
	    return 0;
	}
	if (options != 0) {
	    errno = EINVAL;
	    return -1;
	}
	result = wait(&status);
#else
	result = wait3(&status, options, 0);
#endif
	if ((result == -1) && (errno == EINTR)) {
	    continue;
	}
	if (result <= 0) {
	    return result;
	}

	if ((pid != result) && (pid != -1)) {
	    goto saveInfo;
	}
	if (!(options & WUNTRACED) && (WIFSTOPPED(status))) {
	    goto saveInfo;
	}
	*statusPtr = *((int *) &status);
	return result;

	/*
	 * Can't return this info to caller. Save it in the list of stopped or
	 * exited processes. Tricky point: first check for an existing entry
	 * for the process and overwrite it if it exists (e.g. a previously
	 * stopped process might now be dead).
	 */

    saveInfo:
	for (waitPtr = deadList; waitPtr != NULL; waitPtr = waitPtr->nextPtr) {
	    if (waitPtr->pid == result) {
		waitPtr->status = status;
		goto waitAgain;
	    }
	}
	waitPtr = (WaitInfo *) attemptckalloc(sizeof(WaitInfo));
	if (!waitPtr) {
	    errno = ENOMEM;
	    return -1;
	}
	waitPtr->pid = result;
	waitPtr->status = status;
	waitPtr->nextPtr = deadList;
	deadList = waitPtr;

    waitAgain:
	continue;
    }
}