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