summaryrefslogtreecommitdiffstats
path: root/compat/waitpid.c
blob: 00113cfe1f2a378ef82784bd430e26177f39e6b1 (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
173
174
/* 
 * 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.
 *
 * RCS: @(#) $Id: waitpid.c,v 1.3 2000/01/11 22:08:50 hobbs Exp $
 */

#include "tclInt.h"
#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, statusPtr, options)
    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. */
{
    register 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 *) ckalloc(sizeof(WaitInfo));
	waitPtr->pid = result;
	waitPtr->status = status;
	waitPtr->nextPtr = deadList;
	deadList = waitPtr;

	waitAgain: continue;
    }
}