sl@0: /* sl@0: * waitpid.c -- sl@0: * sl@0: * This procedure emulates the POSIX waitpid kernel call on sl@0: * BSD systems that don't have waitpid but do have wait3. sl@0: * This code is based on a prototype version written by sl@0: * Mark Diekhans and Karl Lehenbauer. sl@0: * sl@0: * Copyright (c) 1993 The Regents of the University of California. sl@0: * Copyright (c) 1994 Sun Microsystems, Inc. sl@0: * sl@0: * See the file "license.terms" for information on usage and redistribution sl@0: * of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: * sl@0: * RCS: @(#) $Id: waitpid.c,v 1.3 2000/01/11 22:08:50 hobbs Exp $ sl@0: */ sl@0: sl@0: #include "tclInt.h" sl@0: #include "tclPort.h" sl@0: sl@0: #ifndef pid_t sl@0: #define pid_t int sl@0: #endif sl@0: sl@0: /* sl@0: * A linked list of the following structures is used to keep track sl@0: * of processes for which we received notification from the kernel, sl@0: * but the application hasn't waited for them yet (this can happen sl@0: * because wait may not return the process we really want). We sl@0: * save the information here until the application finally does sl@0: * wait for the process. sl@0: */ sl@0: sl@0: typedef struct WaitInfo { sl@0: pid_t pid; /* Pid of process that exited. */ sl@0: WAIT_STATUS_TYPE status; /* Status returned when child exited sl@0: * or suspended. */ sl@0: struct WaitInfo *nextPtr; /* Next in list of exited processes. */ sl@0: } WaitInfo; sl@0: sl@0: static WaitInfo *deadList = NULL; /* First in list of all dead sl@0: * processes. */ sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * waitpid -- sl@0: * sl@0: * This procedure emulates the functionality of the POSIX sl@0: * waitpid kernel call, using the BSD wait3 kernel call. sl@0: * Note: it doesn't emulate absolutely all of the waitpid sl@0: * functionality, in that it doesn't support pid's of 0 sl@0: * or < -1. sl@0: * sl@0: * Results: sl@0: * -1 is returned if there is an error in the wait kernel call. sl@0: * Otherwise the pid of an exited or suspended process is sl@0: * returned and *statusPtr is set to the status value of the sl@0: * process. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: #ifdef waitpid sl@0: # undef waitpid sl@0: #endif sl@0: sl@0: pid_t sl@0: waitpid(pid, statusPtr, options) sl@0: pid_t pid; /* The pid to wait on. Must be -1 or sl@0: * greater than zero. */ sl@0: int *statusPtr; /* Where to store wait status for the sl@0: * process. */ sl@0: int options; /* OR'ed combination of WNOHANG and sl@0: * WUNTRACED. */ sl@0: { sl@0: register WaitInfo *waitPtr, *prevPtr; sl@0: pid_t result; sl@0: WAIT_STATUS_TYPE status; sl@0: sl@0: if ((pid < -1) || (pid == 0)) { sl@0: errno = EINVAL; sl@0: return -1; sl@0: } sl@0: sl@0: /* sl@0: * See if there's a suitable process that has already stopped or sl@0: * exited. If so, remove it from the list of exited processes and sl@0: * return its information. sl@0: */ sl@0: sl@0: for (waitPtr = deadList, prevPtr = NULL; waitPtr != NULL; sl@0: prevPtr = waitPtr, waitPtr = waitPtr->nextPtr) { sl@0: if ((pid != waitPtr->pid) && (pid != -1)) { sl@0: continue; sl@0: } sl@0: if (!(options & WUNTRACED) && (WIFSTOPPED(waitPtr->status))) { sl@0: continue; sl@0: } sl@0: result = waitPtr->pid; sl@0: *statusPtr = *((int *) &waitPtr->status); sl@0: if (prevPtr == NULL) { sl@0: deadList = waitPtr->nextPtr; sl@0: } else { sl@0: prevPtr->nextPtr = waitPtr->nextPtr; sl@0: } sl@0: ckfree((char *) waitPtr); sl@0: return result; sl@0: } sl@0: sl@0: /* sl@0: * Wait for any process to stop or exit. If it's an acceptable one sl@0: * then return it to the caller; otherwise store information about it sl@0: * in the list of exited processes and try again. On systems that sl@0: * have only wait but not wait3, there are several situations we can't sl@0: * handle, but we do the best we can (e.g. can still handle some sl@0: * combinations of options by invoking wait instead of wait3). sl@0: */ sl@0: sl@0: while (1) { sl@0: #if NO_WAIT3 sl@0: if (options & WNOHANG) { sl@0: return 0; sl@0: } sl@0: if (options != 0) { sl@0: errno = EINVAL; sl@0: return -1; sl@0: } sl@0: result = wait(&status); sl@0: #else sl@0: result = wait3(&status, options, 0); sl@0: #endif sl@0: if ((result == -1) && (errno == EINTR)) { sl@0: continue; sl@0: } sl@0: if (result <= 0) { sl@0: return result; sl@0: } sl@0: sl@0: if ((pid != result) && (pid != -1)) { sl@0: goto saveInfo; sl@0: } sl@0: if (!(options & WUNTRACED) && (WIFSTOPPED(status))) { sl@0: goto saveInfo; sl@0: } sl@0: *statusPtr = *((int *) &status); sl@0: return result; sl@0: sl@0: /* sl@0: * Can't return this info to caller. Save it in the list of sl@0: * stopped or exited processes. Tricky point: first check for sl@0: * an existing entry for the process and overwrite it if it sl@0: * exists (e.g. a previously stopped process might now be dead). sl@0: */ sl@0: sl@0: saveInfo: sl@0: for (waitPtr = deadList; waitPtr != NULL; waitPtr = waitPtr->nextPtr) { sl@0: if (waitPtr->pid == result) { sl@0: waitPtr->status = status; sl@0: goto waitAgain; sl@0: } sl@0: } sl@0: waitPtr = (WaitInfo *) ckalloc(sizeof(WaitInfo)); sl@0: waitPtr->pid = result; sl@0: waitPtr->status = status; sl@0: waitPtr->nextPtr = deadList; sl@0: deadList = waitPtr; sl@0: sl@0: waitAgain: continue; sl@0: } sl@0: }