Back to index

lightning-sunbird  0.9+nobinonly
uxproces.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "primpl.h"
00039 
00040 #include <sys/types.h>
00041 #include <unistd.h>
00042 #include <fcntl.h>
00043 #include <signal.h>
00044 #include <sys/wait.h>
00045 #if defined(AIX)
00046 #include <dlfcn.h>  /* For dlopen, dlsym, dlclose */
00047 #endif
00048 
00049 #if defined(DARWIN)
00050 #include <crt_externs.h>
00051 #else
00052 PR_IMPORT_DATA(char **) environ;
00053 #endif
00054 
00055 /*
00056  * HP-UX 9 doesn't have the SA_RESTART flag.
00057  */
00058 #ifndef SA_RESTART
00059 #define SA_RESTART 0
00060 #endif
00061 
00062 #if defined(VMS)
00063 static PRLock *_pr_vms_fork_lock = NULL;
00064 #endif
00065 
00066 /*
00067  **********************************************************************
00068  *
00069  * The Unix process routines
00070  *
00071  **********************************************************************
00072  */
00073 
00074 #define _PR_SIGNALED_EXITSTATUS 256
00075 
00076 typedef enum pr_PidState {
00077     _PR_PID_DETACHED,
00078     _PR_PID_REAPED,
00079     _PR_PID_WAITING
00080 } pr_PidState;
00081 
00082 typedef struct pr_PidRecord {
00083     pid_t pid;
00084     int exitStatus;
00085     pr_PidState state;
00086     PRCondVar *reapedCV;
00087     struct pr_PidRecord *next;
00088 } pr_PidRecord;
00089 
00090 /*
00091  * Irix sprocs and LinuxThreads are actually a kind of processes
00092  * that can share the virtual address space and file descriptors.
00093  */
00094 #if (defined(IRIX) && !defined(_PR_PTHREADS)) \
00095         || (defined(LINUX) && defined(_PR_PTHREADS))
00096 #define _PR_SHARE_CLONES
00097 #endif
00098 
00099 /*
00100  * The macro _PR_NATIVE_THREADS indicates that we are
00101  * using native threads only, so waitpid() blocks just the
00102  * calling thread, not the process.  In this case, the waitpid
00103  * daemon thread can safely block in waitpid().  So we don't
00104  * need to catch SIGCHLD, and the pipe to unblock PR_Poll() is
00105  * also not necessary.
00106  */
00107 
00108 #if defined(_PR_GLOBAL_THREADS_ONLY) \
00109        || (defined(_PR_PTHREADS) && !defined(LINUX))
00110 #define _PR_NATIVE_THREADS
00111 #endif
00112 
00113 /*
00114  * All the static variables used by the Unix process routines are
00115  * collected in this structure.
00116  */
00117 
00118 static struct {
00119     PRCallOnceType once;
00120     PRThread *thread;
00121     PRLock *ml;
00122 #if defined(_PR_NATIVE_THREADS)
00123     PRInt32 numProcs;
00124     PRCondVar *cv;
00125 #else
00126     int pipefd[2];
00127 #endif
00128     pr_PidRecord **pidTable;
00129 
00130 #ifdef _PR_SHARE_CLONES
00131     struct pr_CreateProcOp *opHead, *opTail;
00132 #endif
00133 
00134 #ifdef AIX
00135     pid_t (*forkptr)(void);  /* Newer versions of AIX (starting in 4.3.2)
00136                               * have f_fork, which is faster than the
00137                               * regular fork in a multithreaded process
00138                               * because it skips calling the fork handlers.
00139                               * So we look up the f_fork symbol to see if
00140                               * it's available and fall back on fork.
00141                               */
00142 #endif /* AIX */
00143 } pr_wp;
00144 
00145 #ifdef _PR_SHARE_CLONES
00146 static int pr_waitpid_daemon_exit;
00147 
00148 void
00149 _MD_unix_terminate_waitpid_daemon(void)
00150 {
00151     if (pr_wp.thread) {
00152         pr_waitpid_daemon_exit = 1;
00153         write(pr_wp.pipefd[1], "", 1);
00154         PR_JoinThread(pr_wp.thread);
00155     }
00156 }
00157 #endif
00158 
00159 static PRStatus _MD_InitProcesses(void);
00160 #if !defined(_PR_NATIVE_THREADS)
00161 static void pr_InstallSigchldHandler(void);
00162 #endif
00163 
00164 static PRProcess *
00165 ForkAndExec(
00166     const char *path,
00167     char *const *argv,
00168     char *const *envp,
00169     const PRProcessAttr *attr)
00170 {
00171     PRProcess *process;
00172     int nEnv, idx;
00173     char *const *childEnvp;
00174     char **newEnvp = NULL;
00175     int flags;
00176 #ifdef VMS
00177     char VMScurdir[FILENAME_MAX+1] = { '\0' } ;
00178 #endif 
00179 
00180     process = PR_NEW(PRProcess);
00181     if (!process) {
00182         PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00183         return NULL;
00184     }
00185 
00186     childEnvp = envp;
00187     if (attr && attr->fdInheritBuffer) {
00188         if (NULL == childEnvp) {
00189 #ifdef DARWIN
00190             childEnvp = *(_NSGetEnviron());
00191 #else
00192             childEnvp = environ;
00193 #endif
00194         }
00195         for (nEnv = 0; childEnvp[nEnv]; nEnv++) {
00196         }
00197         newEnvp = (char **) PR_MALLOC((nEnv + 2) * sizeof(char *));
00198         if (NULL == newEnvp) {
00199             PR_DELETE(process);
00200             PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00201             return NULL;
00202         }
00203         for (idx = 0; idx < nEnv; idx++) {
00204             newEnvp[idx] = childEnvp[idx];
00205         }
00206         newEnvp[idx++] = attr->fdInheritBuffer;
00207         newEnvp[idx] = NULL;
00208         childEnvp = newEnvp;
00209     }
00210 
00211 #ifdef VMS
00212 /*
00213 ** Since vfork/exec is implemented VERY differently on OpenVMS, we have to
00214 ** handle the setting up of the standard streams very differently. And since
00215 ** none of this code can ever execute in the context of the child, we have
00216 ** to perform the chdir in the parent so the child is born into the correct
00217 ** directory (and then switch the parent back again).
00218 */
00219 {
00220     int decc$set_child_standard_streams(int,int,int);
00221     int n, fd_stdin=0, fd_stdout=1, fd_stderr=2;
00222 
00223     /* Set up any standard streams we are given, assuming defaults */
00224     if (attr) {
00225        if (attr->stdinFd)
00226            fd_stdin = attr->stdinFd->secret->md.osfd;
00227        if (attr->stdoutFd)
00228            fd_stdout = attr->stdoutFd->secret->md.osfd;
00229        if (attr->stderrFd)
00230            fd_stderr = attr->stderrFd->secret->md.osfd;
00231     }
00232 
00233     /*
00234     ** Put a lock around anything that isn't going to be thread-safe.
00235     */
00236     PR_Lock(_pr_vms_fork_lock);
00237 
00238     /*
00239     ** Prepare the child's streams. We always do this in case a previous fork
00240     ** has left the stream assignments in some non-standard way.
00241     */
00242     n = decc$set_child_standard_streams(fd_stdin,fd_stdout,fd_stderr);
00243     if (n == -1) {
00244        PR_SetError(PR_BAD_DESCRIPTOR_ERROR, errno);
00245        PR_DELETE(process);
00246        if (newEnvp) {
00247            PR_DELETE(newEnvp);
00248        }
00249        PR_Unlock(_pr_vms_fork_lock);
00250        return NULL;
00251     }
00252 
00253     /* Switch directory if we have to */
00254     if (attr) {
00255        if (attr->currentDirectory) {
00256            if ( (getcwd(VMScurdir,sizeof(VMScurdir)) == NULL) ||
00257                 (chdir(attr->currentDirectory) < 0) ) {
00258                PR_SetError(PR_DIRECTORY_OPEN_ERROR, errno);
00259                PR_DELETE(process);
00260                if (newEnvp) {
00261                    PR_DELETE(newEnvp);
00262                }
00263                PR_Unlock(_pr_vms_fork_lock);
00264                return NULL;
00265            }
00266        }
00267     }
00268 }
00269 #endif /* VMS */
00270 
00271 #ifdef AIX
00272     process->md.pid = (*pr_wp.forkptr)();
00273 #elif defined(NTO)
00274     /*
00275      * fork() & exec() does not work in a multithreaded process.
00276      * Use spawn() instead.
00277      */
00278     {
00279         int fd_map[3] = { 0, 1, 2 };
00280 
00281         if (attr) {
00282             if (attr->stdinFd && attr->stdinFd->secret->md.osfd != 0) {
00283                 fd_map[0] = dup(attr->stdinFd->secret->md.osfd);
00284                 flags = fcntl(fd_map[0], F_GETFL, 0);
00285                 if (flags & O_NONBLOCK)
00286                     fcntl(fd_map[0], F_SETFL, flags & ~O_NONBLOCK);
00287             }
00288             if (attr->stdoutFd && attr->stdoutFd->secret->md.osfd != 1) {
00289                 fd_map[1] = dup(attr->stdoutFd->secret->md.osfd);
00290                 flags = fcntl(fd_map[1], F_GETFL, 0);
00291                 if (flags & O_NONBLOCK)
00292                     fcntl(fd_map[1], F_SETFL, flags & ~O_NONBLOCK);
00293             }
00294             if (attr->stderrFd && attr->stderrFd->secret->md.osfd != 2) {
00295                 fd_map[2] = dup(attr->stderrFd->secret->md.osfd);
00296                 flags = fcntl(fd_map[2], F_GETFL, 0);
00297                 if (flags & O_NONBLOCK)
00298                     fcntl(fd_map[2], F_SETFL, flags & ~O_NONBLOCK);
00299             }
00300 
00301             PR_ASSERT(attr->currentDirectory == NULL);  /* not implemented */
00302         }
00303 
00304         process->md.pid = spawn(path, 3, fd_map, NULL, argv, childEnvp);
00305 
00306         if (fd_map[0] != 0)
00307             close(fd_map[0]);
00308         if (fd_map[1] != 1)
00309             close(fd_map[1]);
00310         if (fd_map[2] != 2)
00311             close(fd_map[2]);
00312     }
00313 #else
00314     process->md.pid = fork();
00315 #endif
00316     if ((pid_t) -1 == process->md.pid) {
00317         PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, errno);
00318         PR_DELETE(process);
00319         if (newEnvp) {
00320             PR_DELETE(newEnvp);
00321         }
00322         return NULL;
00323     } else if (0 == process->md.pid) {  /* the child process */
00324         /*
00325          * If the child process needs to exit, it must call _exit().
00326          * Do not call exit(), because exit() will flush and close
00327          * the standard I/O file descriptors, and hence corrupt
00328          * the parent process's standard I/O data structures.
00329          */
00330 
00331 #if !defined(NTO)
00332 #ifdef VMS
00333        /* OpenVMS has already handled all this above */
00334 #else
00335         if (attr) {
00336             /* the osfd's to redirect stdin, stdout, and stderr to */
00337             int in_osfd = -1, out_osfd = -1, err_osfd = -1;
00338 
00339             if (attr->stdinFd
00340                     && attr->stdinFd->secret->md.osfd != 0) {
00341                 in_osfd = attr->stdinFd->secret->md.osfd;
00342                 if (dup2(in_osfd, 0) != 0) {
00343                     _exit(1);  /* failed */
00344                 }
00345                 flags = fcntl(0, F_GETFL, 0);
00346                 if (flags & O_NONBLOCK) {
00347                     fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
00348                 }
00349             }
00350             if (attr->stdoutFd
00351                     && attr->stdoutFd->secret->md.osfd != 1) {
00352                 out_osfd = attr->stdoutFd->secret->md.osfd;
00353                 if (dup2(out_osfd, 1) != 1) {
00354                     _exit(1);  /* failed */
00355                 }
00356                 flags = fcntl(1, F_GETFL, 0);
00357                 if (flags & O_NONBLOCK) {
00358                     fcntl(1, F_SETFL, flags & ~O_NONBLOCK);
00359                 }
00360             }
00361             if (attr->stderrFd
00362                     && attr->stderrFd->secret->md.osfd != 2) {
00363                 err_osfd = attr->stderrFd->secret->md.osfd;
00364                 if (dup2(err_osfd, 2) != 2) {
00365                     _exit(1);  /* failed */
00366                 }
00367                 flags = fcntl(2, F_GETFL, 0);
00368                 if (flags & O_NONBLOCK) {
00369                     fcntl(2, F_SETFL, flags & ~O_NONBLOCK);
00370                 }
00371             }
00372             if (in_osfd != -1) {
00373                 close(in_osfd);
00374             }
00375             if (out_osfd != -1 && out_osfd != in_osfd) {
00376                 close(out_osfd);
00377             }
00378             if (err_osfd != -1 && err_osfd != in_osfd
00379                     && err_osfd != out_osfd) {
00380                 close(err_osfd);
00381             }
00382             if (attr->currentDirectory) {
00383                 if (chdir(attr->currentDirectory) < 0) {
00384                     _exit(1);  /* failed */
00385                 }
00386             }
00387         }
00388 #endif /* !VMS */
00389 
00390         if (childEnvp) {
00391             (void)execve(path, argv, childEnvp);
00392         } else {
00393             /* Inherit the environment of the parent. */
00394             (void)execv(path, argv);
00395         }
00396         /* Whoops! It returned. That's a bad sign. */
00397 #ifdef VMS
00398        /*
00399        ** On OpenVMS we are still in the context of the parent, and so we
00400        ** can (and should!) perform normal error handling.
00401        */
00402        PR_SetError(PR_UNKNOWN_ERROR, errno);
00403        PR_DELETE(process);
00404        if (newEnvp) {
00405            PR_DELETE(newEnvp);
00406        }
00407        if (VMScurdir[0] != '\0')
00408            chdir(VMScurdir);
00409        PR_Unlock(_pr_vms_fork_lock);
00410        return NULL;
00411 #else
00412         _exit(1);
00413 #endif /* VMS */
00414 #endif /* !NTO */
00415     }
00416 
00417     if (newEnvp) {
00418         PR_DELETE(newEnvp);
00419     }
00420 #ifdef VMS
00421     /* If we switched directories, then remember to switch back */
00422     if (VMScurdir[0] != '\0') {
00423        chdir(VMScurdir); /* can't do much if it fails */
00424     }
00425     PR_Unlock(_pr_vms_fork_lock);
00426 #endif /* VMS */
00427 
00428 #if defined(_PR_NATIVE_THREADS)
00429     PR_Lock(pr_wp.ml);
00430     if (0 == pr_wp.numProcs++) {
00431         PR_NotifyCondVar(pr_wp.cv);
00432     }
00433     PR_Unlock(pr_wp.ml);
00434 #endif
00435     return process;
00436 }
00437 
00438 #ifdef _PR_SHARE_CLONES
00439 
00440 struct pr_CreateProcOp {
00441     const char *path;
00442     char *const *argv;
00443     char *const *envp;
00444     const PRProcessAttr *attr;
00445     PRProcess *process;
00446     PRErrorCode prerror;
00447     PRInt32 oserror;
00448     PRBool done;
00449     PRCondVar *doneCV;
00450     struct pr_CreateProcOp *next;
00451 };
00452 
00453 PRProcess *
00454 _MD_CreateUnixProcess(
00455     const char *path,
00456     char *const *argv,
00457     char *const *envp,
00458     const PRProcessAttr *attr)
00459 {
00460     struct pr_CreateProcOp *op;
00461     PRProcess *proc;
00462     int rv;
00463 
00464     if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) {
00465        return NULL;
00466     }
00467 
00468     op = PR_NEW(struct pr_CreateProcOp);
00469     if (NULL == op) {
00470        PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00471        return NULL;
00472     }
00473     op->path = path;
00474     op->argv = argv;
00475     op->envp = envp;
00476     op->attr = attr;
00477     op->done = PR_FALSE;
00478     op->doneCV = PR_NewCondVar(pr_wp.ml);
00479     if (NULL == op->doneCV) {
00480        PR_DELETE(op);
00481        return NULL;
00482     }
00483     PR_Lock(pr_wp.ml);
00484 
00485     /* add to the tail of op queue */
00486     op->next = NULL;
00487     if (pr_wp.opTail) {
00488        pr_wp.opTail->next = op;
00489        pr_wp.opTail = op;
00490     } else {
00491        PR_ASSERT(NULL == pr_wp.opHead);
00492        pr_wp.opHead = pr_wp.opTail = op;
00493     }
00494 
00495     /* wake up the daemon thread */
00496     do {
00497         rv = write(pr_wp.pipefd[1], "", 1);
00498     } while (-1 == rv && EINTR == errno);
00499 
00500     while (op->done == PR_FALSE) {
00501        PR_WaitCondVar(op->doneCV, PR_INTERVAL_NO_TIMEOUT);
00502     }
00503     PR_Unlock(pr_wp.ml);
00504     PR_DestroyCondVar(op->doneCV);
00505     proc = op->process;
00506     if (!proc) {
00507        PR_SetError(op->prerror, op->oserror);
00508     }
00509     PR_DELETE(op);
00510     return proc;
00511 }
00512 
00513 #else  /* ! _PR_SHARE_CLONES */
00514 
00515 PRProcess *
00516 _MD_CreateUnixProcess(
00517     const char *path,
00518     char *const *argv,
00519     char *const *envp,
00520     const PRProcessAttr *attr)
00521 {
00522     if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) {
00523        return NULL;
00524     }
00525     return ForkAndExec(path, argv, envp, attr);
00526 }  /* _MD_CreateUnixProcess */
00527 
00528 #endif  /* _PR_SHARE_CLONES */
00529 
00530 /*
00531  * The pid table is a hashtable.
00532  *
00533  * The number of buckets in the hashtable (NBUCKETS) must be a power of 2.
00534  */
00535 #define NBUCKETS_LOG2 6
00536 #define NBUCKETS (1 << NBUCKETS_LOG2)
00537 #define PID_HASH_MASK ((pid_t) (NBUCKETS - 1))
00538 
00539 static pr_PidRecord *
00540 FindPidTable(pid_t pid)
00541 {
00542     pr_PidRecord *pRec;
00543     int keyHash = (int) (pid & PID_HASH_MASK);
00544 
00545     pRec =  pr_wp.pidTable[keyHash];
00546     while (pRec) {
00547        if (pRec->pid == pid) {
00548            break;
00549        }
00550        pRec = pRec->next;
00551     }
00552     return pRec;
00553 }
00554 
00555 static void
00556 InsertPidTable(pr_PidRecord *pRec)
00557 {
00558     int keyHash = (int) (pRec->pid & PID_HASH_MASK);
00559 
00560     pRec->next = pr_wp.pidTable[keyHash];
00561     pr_wp.pidTable[keyHash] = pRec;
00562 }
00563 
00564 static void
00565 DeletePidTable(pr_PidRecord *pRec)
00566 {
00567     int keyHash = (int) (pRec->pid & PID_HASH_MASK);
00568 
00569     if (pr_wp.pidTable[keyHash] == pRec) {
00570        pr_wp.pidTable[keyHash] = pRec->next;
00571     } else {
00572        pr_PidRecord *pred, *cur;  /* predecessor and current */
00573 
00574        pred = pr_wp.pidTable[keyHash];
00575        cur = pred->next;
00576        while (cur) {
00577            if (cur == pRec) {
00578               pred->next = cur->next;
00579               break;
00580             }
00581            pred = cur;
00582            cur = cur->next;
00583         }
00584        PR_ASSERT(cur != NULL);
00585     }
00586 }
00587 
00588 static int
00589 ExtractExitStatus(int rawExitStatus)
00590 {
00591     /*
00592      * We did not specify the WCONTINUED and WUNTRACED options
00593      * for waitpid, so these two events should not be reported.
00594      */
00595     PR_ASSERT(!WIFSTOPPED(rawExitStatus));
00596 #ifdef WIFCONTINUED
00597     PR_ASSERT(!WIFCONTINUED(rawExitStatus));
00598 #endif
00599     if (WIFEXITED(rawExitStatus)) {
00600        return WEXITSTATUS(rawExitStatus);
00601     } else {
00602        PR_ASSERT(WIFSIGNALED(rawExitStatus));
00603        return _PR_SIGNALED_EXITSTATUS;
00604     }
00605 }
00606 
00607 static void
00608 ProcessReapedChildInternal(pid_t pid, int status)
00609 {
00610     pr_PidRecord *pRec;
00611 
00612     pRec = FindPidTable(pid);
00613     if (NULL == pRec) {
00614         pRec = PR_NEW(pr_PidRecord);
00615         pRec->pid = pid;
00616         pRec->state = _PR_PID_REAPED;
00617         pRec->exitStatus = ExtractExitStatus(status);
00618         pRec->reapedCV = NULL;
00619         InsertPidTable(pRec);
00620     } else {
00621         PR_ASSERT(pRec->state != _PR_PID_REAPED);
00622         if (_PR_PID_DETACHED == pRec->state) {
00623             PR_ASSERT(NULL == pRec->reapedCV);
00624             DeletePidTable(pRec);
00625             PR_DELETE(pRec);
00626         } else {
00627             PR_ASSERT(_PR_PID_WAITING == pRec->state);
00628             PR_ASSERT(NULL != pRec->reapedCV);
00629             pRec->exitStatus = ExtractExitStatus(status);
00630             pRec->state = _PR_PID_REAPED;
00631             PR_NotifyCondVar(pRec->reapedCV);
00632         }
00633     }
00634 }
00635 
00636 #if defined(_PR_NATIVE_THREADS)
00637 
00638 /*
00639  * If all the threads are native threads, the daemon thread is
00640  * simpler.  We don't need to catch the SIGCHLD signal.  We can
00641  * just have the daemon thread block in waitpid().
00642  */
00643 
00644 static void WaitPidDaemonThread(void *unused)
00645 {
00646     pid_t pid;
00647     int status;
00648 
00649     while (1) {
00650         PR_Lock(pr_wp.ml);
00651         while (0 == pr_wp.numProcs) {
00652             PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT);
00653         }
00654         PR_Unlock(pr_wp.ml);
00655 
00656        while (1) {
00657            do {
00658                pid = waitpid((pid_t) -1, &status, 0);
00659            } while ((pid_t) -1 == pid && EINTR == errno);
00660 
00661             /*
00662              * waitpid() cannot return 0 because we did not invoke it
00663              * with the WNOHANG option.
00664              */ 
00665            PR_ASSERT(0 != pid);
00666 
00667             /*
00668              * The only possible error code is ECHILD.  But if we do
00669              * our accounting correctly, we should only call waitpid()
00670              * when there is a child process to wait for.
00671              */
00672             PR_ASSERT((pid_t) -1 != pid);
00673            if ((pid_t) -1 == pid) {
00674                 break;
00675             }
00676 
00677            PR_Lock(pr_wp.ml);
00678             ProcessReapedChildInternal(pid, status);
00679             pr_wp.numProcs--;
00680             while (0 == pr_wp.numProcs) {
00681                 PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT);
00682             }
00683            PR_Unlock(pr_wp.ml);
00684        }
00685     }
00686 }
00687 
00688 #else /* _PR_NATIVE_THREADS */
00689 
00690 static void WaitPidDaemonThread(void *unused)
00691 {
00692     PRPollDesc pd;
00693     PRFileDesc *fd;
00694     int rv;
00695     char buf[128];
00696     pid_t pid;
00697     int status;
00698 #ifdef _PR_SHARE_CLONES
00699     struct pr_CreateProcOp *op;
00700 #endif
00701 
00702 #ifdef _PR_SHARE_CLONES
00703     pr_InstallSigchldHandler();
00704 #endif
00705 
00706     fd = PR_ImportFile(pr_wp.pipefd[0]);
00707     PR_ASSERT(NULL != fd);
00708     pd.fd = fd;
00709     pd.in_flags = PR_POLL_READ;
00710 
00711     while (1) {
00712         rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
00713         PR_ASSERT(1 == rv);
00714 
00715 #ifdef _PR_SHARE_CLONES
00716         if (pr_waitpid_daemon_exit) {
00717             return;
00718         }
00719        PR_Lock(pr_wp.ml);
00720 #endif
00721            
00722         do {
00723             rv = read(pr_wp.pipefd[0], buf, sizeof(buf));
00724         } while (sizeof(buf) == rv || (-1 == rv && EINTR == errno));
00725 
00726 #ifdef _PR_SHARE_CLONES
00727        PR_Unlock(pr_wp.ml);
00728        while ((op = pr_wp.opHead) != NULL) {
00729            op->process = ForkAndExec(op->path, op->argv,
00730                   op->envp, op->attr);
00731            if (NULL == op->process) {
00732               op->prerror = PR_GetError();
00733               op->oserror = PR_GetOSError();
00734            }
00735            PR_Lock(pr_wp.ml);
00736            pr_wp.opHead = op->next;
00737            if (NULL == pr_wp.opHead) {
00738               pr_wp.opTail = NULL;
00739            }
00740            op->done = PR_TRUE;
00741            PR_NotifyCondVar(op->doneCV);
00742            PR_Unlock(pr_wp.ml);
00743        }
00744 #endif
00745 
00746        while (1) {
00747            do {
00748                pid = waitpid((pid_t) -1, &status, WNOHANG);
00749            } while ((pid_t) -1 == pid && EINTR == errno);
00750            if (0 == pid) break;
00751            if ((pid_t) -1 == pid) {
00752               /* must be because we have no child processes */
00753               PR_ASSERT(ECHILD == errno);
00754               break;
00755             }
00756 
00757            PR_Lock(pr_wp.ml);
00758             ProcessReapedChildInternal(pid, status);
00759            PR_Unlock(pr_wp.ml);
00760        }
00761     }
00762 }
00763 
00764 static void pr_SigchldHandler(int sig)
00765 {
00766     int errnoCopy;
00767     int rv;
00768 
00769     errnoCopy = errno;
00770 
00771     do {
00772         rv = write(pr_wp.pipefd[1], "", 1);
00773     } while (-1 == rv && EINTR == errno);
00774 
00775 #ifdef DEBUG
00776     if (-1 == rv && EAGAIN != errno && EWOULDBLOCK != errno) {
00777         char *msg = "cannot write to pipe\n";
00778         write(2, msg, strlen(msg) + 1);
00779         _exit(1);
00780     }
00781 #endif
00782 
00783     errno = errnoCopy;
00784 }
00785 
00786 static void pr_InstallSigchldHandler()
00787 {
00788 #if defined(HPUX) && defined(_PR_DCETHREADS)
00789 #error "HP-UX DCE threads have their own SIGCHLD handler"
00790 #endif
00791 
00792     struct sigaction act, oact;
00793     int rv;
00794 
00795     act.sa_handler = pr_SigchldHandler;
00796     sigemptyset(&act.sa_mask);
00797     act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
00798     rv = sigaction(SIGCHLD, &act, &oact);
00799     PR_ASSERT(0 == rv);
00800     /* Make sure we are not overriding someone else's SIGCHLD handler */
00801 #ifndef _PR_SHARE_CLONES
00802     PR_ASSERT(oact.sa_handler == SIG_DFL);
00803 #endif
00804 }
00805 
00806 #endif  /* !defined(_PR_NATIVE_THREADS) */
00807 
00808 static PRStatus _MD_InitProcesses(void)
00809 {
00810 #if !defined(_PR_NATIVE_THREADS)
00811     int rv;
00812     int flags;
00813 #endif
00814 #ifdef SUNOS4
00815 #define _PR_NBIO_FLAG FNDELAY
00816 #else
00817 #define _PR_NBIO_FLAG O_NONBLOCK
00818 #endif
00819 
00820 #ifdef AIX
00821     {
00822         void *handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
00823         pr_wp.forkptr = (pid_t (*)(void)) dlsym(handle, "f_fork");
00824         if (!pr_wp.forkptr) {
00825             pr_wp.forkptr = fork;
00826         }
00827         dlclose(handle);
00828     }
00829 #endif /* AIX */
00830 
00831     pr_wp.ml = PR_NewLock();
00832     PR_ASSERT(NULL != pr_wp.ml);
00833 
00834 #if defined(VMS)
00835     _pr_vms_fork_lock = PR_NewLock();
00836     PR_ASSERT(NULL != _pr_vms_fork_lock);
00837 #endif
00838 
00839 #if defined(_PR_NATIVE_THREADS)
00840     pr_wp.numProcs = 0;
00841     pr_wp.cv = PR_NewCondVar(pr_wp.ml);
00842     PR_ASSERT(NULL != pr_wp.cv);
00843 #else
00844     rv = pipe(pr_wp.pipefd);
00845     PR_ASSERT(0 == rv);
00846     flags = fcntl(pr_wp.pipefd[0], F_GETFL, 0);
00847     fcntl(pr_wp.pipefd[0], F_SETFL, flags | _PR_NBIO_FLAG);
00848     flags = fcntl(pr_wp.pipefd[1], F_GETFL, 0);
00849     fcntl(pr_wp.pipefd[1], F_SETFL, flags | _PR_NBIO_FLAG);
00850 
00851 #ifndef _PR_SHARE_CLONES
00852     pr_InstallSigchldHandler();
00853 #endif
00854 #endif  /* !_PR_NATIVE_THREADS */
00855 
00856     pr_wp.thread = PR_CreateThread(PR_SYSTEM_THREAD,
00857            WaitPidDaemonThread, NULL, PR_PRIORITY_NORMAL,
00858 #ifdef _PR_SHARE_CLONES
00859             PR_GLOBAL_THREAD,
00860 #else
00861            PR_LOCAL_THREAD,
00862 #endif
00863            PR_JOINABLE_THREAD, 0);
00864     PR_ASSERT(NULL != pr_wp.thread);
00865 
00866     pr_wp.pidTable = (pr_PidRecord**)PR_CALLOC(NBUCKETS * sizeof(pr_PidRecord *));
00867     PR_ASSERT(NULL != pr_wp.pidTable);
00868     return PR_SUCCESS;
00869 }
00870 
00871 PRStatus _MD_DetachUnixProcess(PRProcess *process)
00872 {
00873     PRStatus retVal = PR_SUCCESS;
00874     pr_PidRecord *pRec;
00875 
00876     PR_Lock(pr_wp.ml);
00877     pRec = FindPidTable(process->md.pid);
00878     if (NULL == pRec) {
00879        pRec = PR_NEW(pr_PidRecord);
00880        if (NULL == pRec) {
00881            PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00882            retVal = PR_FAILURE;
00883            goto done;
00884        }
00885        pRec->pid = process->md.pid;
00886        pRec->state = _PR_PID_DETACHED;
00887        pRec->reapedCV = NULL;
00888        InsertPidTable(pRec);
00889     } else {
00890        PR_ASSERT(_PR_PID_REAPED == pRec->state);
00891        if (_PR_PID_REAPED != pRec->state) {
00892            PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
00893            retVal = PR_FAILURE;
00894        } else {
00895            DeletePidTable(pRec);
00896            PR_ASSERT(NULL == pRec->reapedCV);
00897            PR_DELETE(pRec);
00898        }
00899     }
00900     PR_DELETE(process);
00901 
00902 done:
00903     PR_Unlock(pr_wp.ml);
00904     return retVal;
00905 }
00906 
00907 PRStatus _MD_WaitUnixProcess(
00908     PRProcess *process,
00909     PRInt32 *exitCode)
00910 {
00911     pr_PidRecord *pRec;
00912     PRStatus retVal = PR_SUCCESS;
00913     PRBool interrupted = PR_FALSE;
00914 
00915     PR_Lock(pr_wp.ml);
00916     pRec = FindPidTable(process->md.pid);
00917     if (NULL == pRec) {
00918        pRec = PR_NEW(pr_PidRecord);
00919        if (NULL == pRec) {
00920            PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
00921            retVal = PR_FAILURE;
00922            goto done;
00923        }
00924        pRec->pid = process->md.pid;
00925        pRec->state = _PR_PID_WAITING;
00926        pRec->reapedCV = PR_NewCondVar(pr_wp.ml);
00927        if (NULL == pRec->reapedCV) {
00928            PR_DELETE(pRec);
00929            retVal = PR_FAILURE;
00930            goto done;
00931        }
00932        InsertPidTable(pRec);
00933        while (!interrupted && _PR_PID_REAPED != pRec->state) {
00934            if (PR_WaitCondVar(pRec->reapedCV,
00935                   PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE
00936                   && PR_GetError() == PR_PENDING_INTERRUPT_ERROR) {
00937               interrupted = PR_TRUE;
00938             }
00939        }
00940        if (_PR_PID_REAPED == pRec->state) {
00941             if (exitCode) {
00942                 *exitCode = pRec->exitStatus;
00943             }
00944        } else {
00945            PR_ASSERT(interrupted);
00946            retVal = PR_FAILURE;
00947        }
00948        DeletePidTable(pRec);
00949        PR_DestroyCondVar(pRec->reapedCV);
00950        PR_DELETE(pRec);
00951     } else {
00952        PR_ASSERT(_PR_PID_REAPED == pRec->state);
00953        PR_ASSERT(NULL == pRec->reapedCV);
00954        DeletePidTable(pRec);
00955         if (exitCode) {
00956             *exitCode = pRec->exitStatus;
00957         }
00958        PR_DELETE(pRec);
00959     }
00960     PR_DELETE(process);
00961 
00962 done:
00963     PR_Unlock(pr_wp.ml);
00964     return retVal;
00965 }  /* _MD_WaitUnixProcess */
00966 
00967 PRStatus _MD_KillUnixProcess(PRProcess *process)
00968 {
00969     PRErrorCode prerror;
00970     PRInt32 oserror;
00971 
00972     if (kill(process->md.pid, SIGKILL) == 0) {
00973        return PR_SUCCESS;
00974     }
00975     oserror = errno;
00976     switch (oserror) {
00977         case EPERM:
00978            prerror = PR_NO_ACCESS_RIGHTS_ERROR;
00979            break;
00980         case ESRCH:
00981            prerror = PR_INVALID_ARGUMENT_ERROR;
00982            break;
00983         default:
00984            prerror = PR_UNKNOWN_ERROR;
00985            break;
00986     }
00987     PR_SetError(prerror, oserror);
00988     return PR_FAILURE;
00989 }  /* _MD_KillUnixProcess */