tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

uxproces.c (21007B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "primpl.h"
      7 
      8 #include <sys/types.h>
      9 #include <unistd.h>
     10 #include <fcntl.h>
     11 #include <signal.h>
     12 #include <sys/wait.h>
     13 #include <string.h>
     14 #if defined(AIX)
     15 #  include <dlfcn.h> /* For dlopen, dlsym, dlclose */
     16 #endif
     17 
     18 #if defined(DARWIN)
     19 #  if defined(HAVE_CRT_EXTERNS_H)
     20 #    include <crt_externs.h>
     21 #  endif
     22 #else
     23 PR_IMPORT_DATA(char**) environ;
     24 #endif
     25 
     26 /*
     27 * HP-UX 9 doesn't have the SA_RESTART flag.
     28 */
     29 #ifndef SA_RESTART
     30 #  define SA_RESTART 0
     31 #endif
     32 
     33 /*
     34 **********************************************************************
     35 *
     36 * The Unix process routines
     37 *
     38 **********************************************************************
     39 */
     40 
     41 #define _PR_SIGNALED_EXITSTATUS 256
     42 
     43 typedef enum pr_PidState {
     44  _PR_PID_DETACHED,
     45  _PR_PID_REAPED,
     46  _PR_PID_WAITING
     47 } pr_PidState;
     48 
     49 typedef struct pr_PidRecord {
     50  pid_t pid;
     51  int exitStatus;
     52  pr_PidState state;
     53  PRCondVar* reapedCV;
     54  struct pr_PidRecord* next;
     55 } pr_PidRecord;
     56 
     57 /*
     58 * LinuxThreads are actually a kind of processes
     59 * that can share the virtual address space and file descriptors.
     60 */
     61 #if ((defined(LINUX) || defined(__GNU__) || defined(__GLIBC__)) && \
     62     defined(_PR_PTHREADS))
     63 #  define _PR_SHARE_CLONES
     64 #endif
     65 
     66 /*
     67 * The macro _PR_NATIVE_THREADS indicates that we are
     68 * using native threads only, so waitpid() blocks just the
     69 * calling thread, not the process.  In this case, the waitpid
     70 * daemon thread can safely block in waitpid().  So we don't
     71 * need to catch SIGCHLD, and the pipe to unblock PR_Poll() is
     72 * also not necessary.
     73 */
     74 
     75 #if defined(_PR_GLOBAL_THREADS_ONLY) ||                               \
     76    (defined(_PR_PTHREADS) && !defined(LINUX) && !defined(__GNU__) && \
     77     !defined(__GLIBC__))
     78 #  define _PR_NATIVE_THREADS
     79 #endif
     80 
     81 /*
     82 * All the static variables used by the Unix process routines are
     83 * collected in this structure.
     84 */
     85 
     86 static struct {
     87  PRCallOnceType once;
     88  PRThread* thread;
     89  PRLock* ml;
     90 #if defined(_PR_NATIVE_THREADS)
     91  PRInt32 numProcs;
     92  PRCondVar* cv;
     93 #else
     94  int pipefd[2];
     95 #endif
     96  pr_PidRecord** pidTable;
     97 
     98 #ifdef _PR_SHARE_CLONES
     99  struct pr_CreateProcOp *opHead, *opTail;
    100 #endif
    101 
    102 #ifdef AIX
    103  pid_t (*forkptr)(void); /* Newer versions of AIX (starting in 4.3.2)
    104                           * have f_fork, which is faster than the
    105                           * regular fork in a multithreaded process
    106                           * because it skips calling the fork handlers.
    107                           * So we look up the f_fork symbol to see if
    108                           * it's available and fall back on fork.
    109                           */
    110 #endif                    /* AIX */
    111 } pr_wp;
    112 
    113 #ifdef _PR_SHARE_CLONES
    114 static int pr_waitpid_daemon_exit;
    115 
    116 void _MD_unix_terminate_waitpid_daemon(void) {
    117  if (pr_wp.thread) {
    118    pr_waitpid_daemon_exit = 1;
    119    write(pr_wp.pipefd[1], "", 1);
    120    PR_JoinThread(pr_wp.thread);
    121  }
    122 }
    123 #endif
    124 
    125 static PRStatus _MD_InitProcesses(void);
    126 #if !defined(_PR_NATIVE_THREADS)
    127 static void pr_InstallSigchldHandler(void);
    128 #endif
    129 
    130 static PRProcess* ForkAndExec(const char* path, char* const* argv,
    131                              char* const* envp, const PRProcessAttr* attr) {
    132  PRProcess* process;
    133  int nEnv, idx;
    134  char* const* childEnvp;
    135  char** newEnvp = NULL;
    136  int flags;
    137 
    138  process = PR_NEW(PRProcess);
    139  if (!process) {
    140    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    141    return NULL;
    142  }
    143 
    144  childEnvp = envp;
    145  if (attr && attr->fdInheritBuffer) {
    146    PRBool found = PR_FALSE;
    147 
    148    if (NULL == childEnvp) {
    149 #ifdef DARWIN
    150 #  ifdef HAVE_CRT_EXTERNS_H
    151      childEnvp = *(_NSGetEnviron());
    152 #  else
    153      /* _NSGetEnviron() is not available on iOS. */
    154      PR_DELETE(process);
    155      PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
    156      return NULL;
    157 #  endif
    158 #else
    159      childEnvp = environ;
    160 #endif
    161    }
    162 
    163    for (nEnv = 0; childEnvp[nEnv]; nEnv++) {
    164    }
    165    newEnvp = (char**)PR_MALLOC((nEnv + 2) * sizeof(char*));
    166    if (NULL == newEnvp) {
    167      PR_DELETE(process);
    168      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    169      return NULL;
    170    }
    171    for (idx = 0; idx < nEnv; idx++) {
    172      newEnvp[idx] = childEnvp[idx];
    173      if (!found && !strncmp(newEnvp[idx], "NSPR_INHERIT_FDS=", 17)) {
    174        newEnvp[idx] = attr->fdInheritBuffer;
    175        found = PR_TRUE;
    176      }
    177    }
    178    if (!found) {
    179      newEnvp[idx++] = attr->fdInheritBuffer;
    180    }
    181    newEnvp[idx] = NULL;
    182    childEnvp = newEnvp;
    183  }
    184 
    185 #ifdef AIX
    186  process->md.pid = (*pr_wp.forkptr)();
    187 #elif defined(NTO)
    188  /*
    189   * fork() & exec() does not work in a multithreaded process.
    190   * Use spawn() instead.
    191   */
    192  {
    193    int fd_map[3] = {0, 1, 2};
    194 
    195    if (attr) {
    196      if (attr->stdinFd && attr->stdinFd->secret->md.osfd != 0) {
    197        fd_map[0] = dup(attr->stdinFd->secret->md.osfd);
    198        flags = fcntl(fd_map[0], F_GETFL, 0);
    199        if (flags & O_NONBLOCK) {
    200          fcntl(fd_map[0], F_SETFL, flags & ~O_NONBLOCK);
    201        }
    202      }
    203      if (attr->stdoutFd && attr->stdoutFd->secret->md.osfd != 1) {
    204        fd_map[1] = dup(attr->stdoutFd->secret->md.osfd);
    205        flags = fcntl(fd_map[1], F_GETFL, 0);
    206        if (flags & O_NONBLOCK) {
    207          fcntl(fd_map[1], F_SETFL, flags & ~O_NONBLOCK);
    208        }
    209      }
    210      if (attr->stderrFd && attr->stderrFd->secret->md.osfd != 2) {
    211        fd_map[2] = dup(attr->stderrFd->secret->md.osfd);
    212        flags = fcntl(fd_map[2], F_GETFL, 0);
    213        if (flags & O_NONBLOCK) {
    214          fcntl(fd_map[2], F_SETFL, flags & ~O_NONBLOCK);
    215        }
    216      }
    217 
    218      PR_ASSERT(attr->currentDirectory == NULL); /* not implemented */
    219    }
    220 
    221    process->md.pid = spawn(path, 3, fd_map, NULL, argv, childEnvp);
    222 
    223    if (fd_map[0] != 0) {
    224      close(fd_map[0]);
    225    }
    226    if (fd_map[1] != 1) {
    227      close(fd_map[1]);
    228    }
    229    if (fd_map[2] != 2) {
    230      close(fd_map[2]);
    231    }
    232  }
    233 #else
    234  process->md.pid = fork();
    235 #endif
    236  if ((pid_t)-1 == process->md.pid) {
    237    PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, errno);
    238    PR_DELETE(process);
    239    if (newEnvp) {
    240      PR_DELETE(newEnvp);
    241    }
    242    return NULL;
    243  }
    244  if (0 == process->md.pid) { /* the child process */
    245                              /*
    246                               * If the child process needs to exit, it must call _exit().
    247                               * Do not call exit(), because exit() will flush and close
    248                               * the standard I/O file descriptors, and hence corrupt
    249                               * the parent process's standard I/O data structures.
    250                               */
    251 
    252 #if !defined(NTO)
    253    if (attr) {
    254      /* the osfd's to redirect stdin, stdout, and stderr to */
    255      int in_osfd = -1, out_osfd = -1, err_osfd = -1;
    256 
    257      if (attr->stdinFd && attr->stdinFd->secret->md.osfd != 0) {
    258        in_osfd = attr->stdinFd->secret->md.osfd;
    259        if (dup2(in_osfd, 0) != 0) {
    260          _exit(1); /* failed */
    261        }
    262        flags = fcntl(0, F_GETFL, 0);
    263        if (flags & O_NONBLOCK) {
    264          fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
    265        }
    266      }
    267      if (attr->stdoutFd && attr->stdoutFd->secret->md.osfd != 1) {
    268        out_osfd = attr->stdoutFd->secret->md.osfd;
    269        if (dup2(out_osfd, 1) != 1) {
    270          _exit(1); /* failed */
    271        }
    272        flags = fcntl(1, F_GETFL, 0);
    273        if (flags & O_NONBLOCK) {
    274          fcntl(1, F_SETFL, flags & ~O_NONBLOCK);
    275        }
    276      }
    277      if (attr->stderrFd && attr->stderrFd->secret->md.osfd != 2) {
    278        err_osfd = attr->stderrFd->secret->md.osfd;
    279        if (dup2(err_osfd, 2) != 2) {
    280          _exit(1); /* failed */
    281        }
    282        flags = fcntl(2, F_GETFL, 0);
    283        if (flags & O_NONBLOCK) {
    284          fcntl(2, F_SETFL, flags & ~O_NONBLOCK);
    285        }
    286      }
    287      if (in_osfd != -1) {
    288        close(in_osfd);
    289      }
    290      if (out_osfd != -1 && out_osfd != in_osfd) {
    291        close(out_osfd);
    292      }
    293      if (err_osfd != -1 && err_osfd != in_osfd && err_osfd != out_osfd) {
    294        close(err_osfd);
    295      }
    296      if (attr->currentDirectory) {
    297        if (chdir(attr->currentDirectory) < 0) {
    298          _exit(1); /* failed */
    299        }
    300      }
    301    }
    302 
    303    if (childEnvp) {
    304      (void)execve(path, argv, childEnvp);
    305    } else {
    306      /* Inherit the environment of the parent. */
    307      (void)execv(path, argv);
    308    }
    309    /* Whoops! It returned. That's a bad sign. */
    310    _exit(1);
    311 #endif /* !NTO */
    312  }
    313 
    314  if (newEnvp) {
    315    PR_DELETE(newEnvp);
    316  }
    317 
    318 #if defined(_PR_NATIVE_THREADS)
    319  PR_Lock(pr_wp.ml);
    320  if (0 == pr_wp.numProcs++) {
    321    PR_NotifyCondVar(pr_wp.cv);
    322  }
    323  PR_Unlock(pr_wp.ml);
    324 #endif
    325  return process;
    326 }
    327 
    328 #ifdef _PR_SHARE_CLONES
    329 
    330 struct pr_CreateProcOp {
    331  const char* path;
    332  char* const* argv;
    333  char* const* envp;
    334  const PRProcessAttr* attr;
    335  PRProcess* process;
    336  PRErrorCode prerror;
    337  PRInt32 oserror;
    338  PRBool done;
    339  PRCondVar* doneCV;
    340  struct pr_CreateProcOp* next;
    341 };
    342 
    343 PRProcess* _MD_CreateUnixProcess(const char* path, char* const* argv,
    344                                 char* const* envp, const PRProcessAttr* attr) {
    345  struct pr_CreateProcOp* op;
    346  PRProcess* proc;
    347  int rv;
    348 
    349  if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) {
    350    return NULL;
    351  }
    352 
    353  op = PR_NEW(struct pr_CreateProcOp);
    354  if (NULL == op) {
    355    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    356    return NULL;
    357  }
    358  op->path = path;
    359  op->argv = argv;
    360  op->envp = envp;
    361  op->attr = attr;
    362  op->done = PR_FALSE;
    363  op->doneCV = PR_NewCondVar(pr_wp.ml);
    364  if (NULL == op->doneCV) {
    365    PR_DELETE(op);
    366    return NULL;
    367  }
    368  PR_Lock(pr_wp.ml);
    369 
    370  /* add to the tail of op queue */
    371  op->next = NULL;
    372  if (pr_wp.opTail) {
    373    pr_wp.opTail->next = op;
    374    pr_wp.opTail = op;
    375  } else {
    376    PR_ASSERT(NULL == pr_wp.opHead);
    377    pr_wp.opHead = pr_wp.opTail = op;
    378  }
    379 
    380  /* wake up the daemon thread */
    381  do {
    382    rv = write(pr_wp.pipefd[1], "", 1);
    383  } while (-1 == rv && EINTR == errno);
    384 
    385  while (op->done == PR_FALSE) {
    386    PR_WaitCondVar(op->doneCV, PR_INTERVAL_NO_TIMEOUT);
    387  }
    388  PR_Unlock(pr_wp.ml);
    389  PR_DestroyCondVar(op->doneCV);
    390  proc = op->process;
    391  if (!proc) {
    392    PR_SetError(op->prerror, op->oserror);
    393  }
    394  PR_DELETE(op);
    395  return proc;
    396 }
    397 
    398 #else /* ! _PR_SHARE_CLONES */
    399 
    400 PRProcess* _MD_CreateUnixProcess(const char* path, char* const* argv,
    401                                 char* const* envp, const PRProcessAttr* attr) {
    402  if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) {
    403    return NULL;
    404  }
    405  return ForkAndExec(path, argv, envp, attr);
    406 } /* _MD_CreateUnixProcess */
    407 
    408 #endif /* _PR_SHARE_CLONES */
    409 
    410 /*
    411 * The pid table is a hashtable.
    412 *
    413 * The number of buckets in the hashtable (NBUCKETS) must be a power of 2.
    414 */
    415 #define NBUCKETS_LOG2 6
    416 #define NBUCKETS (1 << NBUCKETS_LOG2)
    417 #define PID_HASH_MASK ((pid_t)(NBUCKETS - 1))
    418 
    419 static pr_PidRecord* FindPidTable(pid_t pid) {
    420  pr_PidRecord* pRec;
    421  int keyHash = (int)(pid & PID_HASH_MASK);
    422 
    423  pRec = pr_wp.pidTable[keyHash];
    424  while (pRec) {
    425    if (pRec->pid == pid) {
    426      break;
    427    }
    428    pRec = pRec->next;
    429  }
    430  return pRec;
    431 }
    432 
    433 static void InsertPidTable(pr_PidRecord* pRec) {
    434  int keyHash = (int)(pRec->pid & PID_HASH_MASK);
    435 
    436  pRec->next = pr_wp.pidTable[keyHash];
    437  pr_wp.pidTable[keyHash] = pRec;
    438 }
    439 
    440 static void DeletePidTable(pr_PidRecord* pRec) {
    441  int keyHash = (int)(pRec->pid & PID_HASH_MASK);
    442 
    443  if (pr_wp.pidTable[keyHash] == pRec) {
    444    pr_wp.pidTable[keyHash] = pRec->next;
    445  } else {
    446    pr_PidRecord *pred, *cur; /* predecessor and current */
    447 
    448    pred = pr_wp.pidTable[keyHash];
    449    cur = pred->next;
    450    while (cur) {
    451      if (cur == pRec) {
    452        pred->next = cur->next;
    453        break;
    454      }
    455      pred = cur;
    456      cur = cur->next;
    457    }
    458    PR_ASSERT(cur != NULL);
    459  }
    460 }
    461 
    462 static int ExtractExitStatus(int rawExitStatus) {
    463  /*
    464   * We did not specify the WCONTINUED and WUNTRACED options
    465   * for waitpid, so these two events should not be reported.
    466   */
    467  PR_ASSERT(!WIFSTOPPED(rawExitStatus));
    468 #ifdef WIFCONTINUED
    469  PR_ASSERT(!WIFCONTINUED(rawExitStatus));
    470 #endif
    471  if (WIFEXITED(rawExitStatus)) {
    472    return WEXITSTATUS(rawExitStatus);
    473  }
    474  PR_ASSERT(WIFSIGNALED(rawExitStatus));
    475  return _PR_SIGNALED_EXITSTATUS;
    476 }
    477 
    478 static void ProcessReapedChildInternal(pid_t pid, int status) {
    479  pr_PidRecord* pRec;
    480 
    481  pRec = FindPidTable(pid);
    482  if (NULL == pRec) {
    483    pRec = PR_NEW(pr_PidRecord);
    484    pRec->pid = pid;
    485    pRec->state = _PR_PID_REAPED;
    486    pRec->exitStatus = ExtractExitStatus(status);
    487    pRec->reapedCV = NULL;
    488    InsertPidTable(pRec);
    489  } else {
    490    PR_ASSERT(pRec->state != _PR_PID_REAPED);
    491    if (_PR_PID_DETACHED == pRec->state) {
    492      PR_ASSERT(NULL == pRec->reapedCV);
    493      DeletePidTable(pRec);
    494      PR_DELETE(pRec);
    495    } else {
    496      PR_ASSERT(_PR_PID_WAITING == pRec->state);
    497      PR_ASSERT(NULL != pRec->reapedCV);
    498      pRec->exitStatus = ExtractExitStatus(status);
    499      pRec->state = _PR_PID_REAPED;
    500      PR_NotifyCondVar(pRec->reapedCV);
    501    }
    502  }
    503 }
    504 
    505 #if defined(_PR_NATIVE_THREADS)
    506 
    507 /*
    508 * If all the threads are native threads, the daemon thread is
    509 * simpler.  We don't need to catch the SIGCHLD signal.  We can
    510 * just have the daemon thread block in waitpid().
    511 */
    512 
    513 static void WaitPidDaemonThread(void* unused) {
    514  pid_t pid;
    515  int status;
    516 
    517  while (1) {
    518    PR_Lock(pr_wp.ml);
    519    while (0 == pr_wp.numProcs) {
    520      PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT);
    521    }
    522    PR_Unlock(pr_wp.ml);
    523 
    524    while (1) {
    525      do {
    526        pid = waitpid((pid_t)-1, &status, 0);
    527      } while ((pid_t)-1 == pid && EINTR == errno);
    528 
    529      /*
    530       * waitpid() cannot return 0 because we did not invoke it
    531       * with the WNOHANG option.
    532       */
    533      PR_ASSERT(0 != pid);
    534 
    535      /*
    536       * The only possible error code is ECHILD.  But if we do
    537       * our accounting correctly, we should only call waitpid()
    538       * when there is a child process to wait for.
    539       */
    540      PR_ASSERT((pid_t)-1 != pid);
    541      if ((pid_t)-1 == pid) {
    542        break;
    543      }
    544 
    545      PR_Lock(pr_wp.ml);
    546      ProcessReapedChildInternal(pid, status);
    547      pr_wp.numProcs--;
    548      while (0 == pr_wp.numProcs) {
    549        PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT);
    550      }
    551      PR_Unlock(pr_wp.ml);
    552    }
    553  }
    554 }
    555 
    556 #else /* _PR_NATIVE_THREADS */
    557 
    558 static void WaitPidDaemonThread(void* unused) {
    559  PRPollDesc pd;
    560  PRFileDesc* fd;
    561  int rv;
    562  char buf[128];
    563  pid_t pid;
    564  int status;
    565 #  ifdef _PR_SHARE_CLONES
    566  struct pr_CreateProcOp* op;
    567 #  endif
    568 
    569 #  ifdef _PR_SHARE_CLONES
    570  pr_InstallSigchldHandler();
    571 #  endif
    572 
    573  fd = PR_ImportFile(pr_wp.pipefd[0]);
    574  PR_ASSERT(NULL != fd);
    575  pd.fd = fd;
    576  pd.in_flags = PR_POLL_READ;
    577 
    578  while (1) {
    579    rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
    580    PR_ASSERT(1 == rv);
    581 
    582 #  ifdef _PR_SHARE_CLONES
    583    if (pr_waitpid_daemon_exit) {
    584      return;
    585    }
    586    PR_Lock(pr_wp.ml);
    587 #  endif
    588 
    589    do {
    590      rv = read(pr_wp.pipefd[0], buf, sizeof(buf));
    591    } while (sizeof(buf) == rv || (-1 == rv && EINTR == errno));
    592 
    593 #  ifdef _PR_SHARE_CLONES
    594    while ((op = pr_wp.opHead) != NULL) {
    595      PR_Unlock(pr_wp.ml);
    596      op->process = ForkAndExec(op->path, op->argv, op->envp, op->attr);
    597      if (NULL == op->process) {
    598        op->prerror = PR_GetError();
    599        op->oserror = PR_GetOSError();
    600      }
    601      PR_Lock(pr_wp.ml);
    602      pr_wp.opHead = op->next;
    603      if (NULL == pr_wp.opHead) {
    604        pr_wp.opTail = NULL;
    605      }
    606      op->done = PR_TRUE;
    607      PR_NotifyCondVar(op->doneCV);
    608    }
    609    PR_Unlock(pr_wp.ml);
    610 #  endif
    611 
    612    while (1) {
    613      do {
    614        pid = waitpid((pid_t)-1, &status, WNOHANG);
    615      } while ((pid_t)-1 == pid && EINTR == errno);
    616      if (0 == pid) {
    617        break;
    618      }
    619      if ((pid_t)-1 == pid) {
    620        /* must be because we have no child processes */
    621        PR_ASSERT(ECHILD == errno);
    622        break;
    623      }
    624 
    625      PR_Lock(pr_wp.ml);
    626      ProcessReapedChildInternal(pid, status);
    627      PR_Unlock(pr_wp.ml);
    628    }
    629  }
    630 }
    631 
    632 static void pr_SigchldHandler(int sig) {
    633  int errnoCopy;
    634  int rv;
    635 
    636  errnoCopy = errno;
    637 
    638  do {
    639    rv = write(pr_wp.pipefd[1], "", 1);
    640  } while (-1 == rv && EINTR == errno);
    641 
    642 #  ifdef DEBUG
    643  if (-1 == rv && EAGAIN != errno && EWOULDBLOCK != errno) {
    644    char* msg = "cannot write to pipe\n";
    645    write(2, msg, strlen(msg) + 1);
    646    _exit(1);
    647  }
    648 #  endif
    649 
    650  errno = errnoCopy;
    651 }
    652 
    653 static void pr_InstallSigchldHandler() {
    654  struct sigaction act, oact;
    655  int rv;
    656 
    657  act.sa_handler = pr_SigchldHandler;
    658  sigemptyset(&act.sa_mask);
    659  act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
    660  rv = sigaction(SIGCHLD, &act, &oact);
    661  PR_ASSERT(0 == rv);
    662  /* Make sure we are not overriding someone else's SIGCHLD handler */
    663 #  ifndef _PR_SHARE_CLONES
    664  PR_ASSERT(oact.sa_handler == SIG_DFL);
    665 #  endif
    666 }
    667 
    668 #endif /* !defined(_PR_NATIVE_THREADS) */
    669 
    670 static PRStatus _MD_InitProcesses(void) {
    671 #if !defined(_PR_NATIVE_THREADS)
    672  int rv;
    673  int flags;
    674 #endif
    675 
    676 #ifdef AIX
    677  {
    678    void* handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
    679    pr_wp.forkptr = (pid_t(*)(void))dlsym(handle, "f_fork");
    680    if (!pr_wp.forkptr) {
    681      pr_wp.forkptr = fork;
    682    }
    683    dlclose(handle);
    684  }
    685 #endif /* AIX */
    686 
    687  pr_wp.ml = PR_NewLock();
    688  PR_ASSERT(NULL != pr_wp.ml);
    689 
    690 #if defined(_PR_NATIVE_THREADS)
    691  pr_wp.numProcs = 0;
    692  pr_wp.cv = PR_NewCondVar(pr_wp.ml);
    693  PR_ASSERT(NULL != pr_wp.cv);
    694 #else
    695  rv = pipe(pr_wp.pipefd);
    696  PR_ASSERT(0 == rv);
    697  flags = fcntl(pr_wp.pipefd[0], F_GETFL, 0);
    698  fcntl(pr_wp.pipefd[0], F_SETFL, flags | O_NONBLOCK);
    699  flags = fcntl(pr_wp.pipefd[1], F_GETFL, 0);
    700  fcntl(pr_wp.pipefd[1], F_SETFL, flags | O_NONBLOCK);
    701 
    702 #  ifndef _PR_SHARE_CLONES
    703  pr_InstallSigchldHandler();
    704 #  endif
    705 #endif /* !_PR_NATIVE_THREADS */
    706 
    707  pr_wp.thread = PR_CreateThread(PR_SYSTEM_THREAD, WaitPidDaemonThread, NULL,
    708                                 PR_PRIORITY_NORMAL,
    709 #ifdef _PR_SHARE_CLONES
    710                                 PR_GLOBAL_THREAD,
    711 #else
    712                                 PR_LOCAL_THREAD,
    713 #endif
    714                                 PR_JOINABLE_THREAD, 0);
    715  PR_ASSERT(NULL != pr_wp.thread);
    716 
    717  pr_wp.pidTable = (pr_PidRecord**)PR_CALLOC(NBUCKETS * sizeof(pr_PidRecord*));
    718  PR_ASSERT(NULL != pr_wp.pidTable);
    719  return PR_SUCCESS;
    720 }
    721 
    722 PRStatus _MD_DetachUnixProcess(PRProcess* process) {
    723  PRStatus retVal = PR_SUCCESS;
    724  pr_PidRecord* pRec;
    725 
    726  PR_Lock(pr_wp.ml);
    727  pRec = FindPidTable(process->md.pid);
    728  if (NULL == pRec) {
    729    pRec = PR_NEW(pr_PidRecord);
    730    if (NULL == pRec) {
    731      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    732      retVal = PR_FAILURE;
    733      goto done;
    734    }
    735    pRec->pid = process->md.pid;
    736    pRec->state = _PR_PID_DETACHED;
    737    pRec->reapedCV = NULL;
    738    InsertPidTable(pRec);
    739  } else {
    740    PR_ASSERT(_PR_PID_REAPED == pRec->state);
    741    if (_PR_PID_REAPED != pRec->state) {
    742      PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    743      retVal = PR_FAILURE;
    744    } else {
    745      DeletePidTable(pRec);
    746      PR_ASSERT(NULL == pRec->reapedCV);
    747      PR_DELETE(pRec);
    748    }
    749  }
    750  PR_DELETE(process);
    751 
    752 done:
    753  PR_Unlock(pr_wp.ml);
    754  return retVal;
    755 }
    756 
    757 PRStatus _MD_WaitUnixProcess(PRProcess* process, PRInt32* exitCode) {
    758  pr_PidRecord* pRec;
    759  PRStatus retVal = PR_SUCCESS;
    760  PRBool interrupted = PR_FALSE;
    761 
    762  PR_Lock(pr_wp.ml);
    763  pRec = FindPidTable(process->md.pid);
    764  if (NULL == pRec) {
    765    pRec = PR_NEW(pr_PidRecord);
    766    if (NULL == pRec) {
    767      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    768      retVal = PR_FAILURE;
    769      goto done;
    770    }
    771    pRec->pid = process->md.pid;
    772    pRec->state = _PR_PID_WAITING;
    773    pRec->reapedCV = PR_NewCondVar(pr_wp.ml);
    774    if (NULL == pRec->reapedCV) {
    775      PR_DELETE(pRec);
    776      retVal = PR_FAILURE;
    777      goto done;
    778    }
    779    InsertPidTable(pRec);
    780    while (!interrupted && _PR_PID_REAPED != pRec->state) {
    781      if (PR_WaitCondVar(pRec->reapedCV, PR_INTERVAL_NO_TIMEOUT) ==
    782              PR_FAILURE &&
    783          PR_GetError() == PR_PENDING_INTERRUPT_ERROR) {
    784        interrupted = PR_TRUE;
    785      }
    786    }
    787    if (_PR_PID_REAPED == pRec->state) {
    788      if (exitCode) {
    789        *exitCode = pRec->exitStatus;
    790      }
    791    } else {
    792      PR_ASSERT(interrupted);
    793      retVal = PR_FAILURE;
    794    }
    795    DeletePidTable(pRec);
    796    PR_DestroyCondVar(pRec->reapedCV);
    797    PR_DELETE(pRec);
    798  } else {
    799    PR_ASSERT(_PR_PID_REAPED == pRec->state);
    800    PR_ASSERT(NULL == pRec->reapedCV);
    801    DeletePidTable(pRec);
    802    if (exitCode) {
    803      *exitCode = pRec->exitStatus;
    804    }
    805    PR_DELETE(pRec);
    806  }
    807  PR_DELETE(process);
    808 
    809 done:
    810  PR_Unlock(pr_wp.ml);
    811  return retVal;
    812 } /* _MD_WaitUnixProcess */
    813 
    814 PRStatus _MD_KillUnixProcess(PRProcess* process) {
    815  PRErrorCode prerror;
    816  PRInt32 oserror;
    817 
    818  if (kill(process->md.pid, SIGKILL) == 0) {
    819    return PR_SUCCESS;
    820  }
    821  oserror = errno;
    822  switch (oserror) {
    823    case EPERM:
    824      prerror = PR_NO_ACCESS_RIGHTS_ERROR;
    825      break;
    826    case ESRCH:
    827      prerror = PR_INVALID_ARGUMENT_ERROR;
    828      break;
    829    default:
    830      prerror = PR_UNKNOWN_ERROR;
    831      break;
    832  }
    833  PR_SetError(prerror, oserror);
    834  return PR_FAILURE;
    835 } /* _MD_KillUnixProcess */