Coverage Report

Created: 2024-11-21 07:03

/src/nss-nspr/nspr/pr/src/md/unix/uxproces.c
Line
Count
Source (jump to first uncovered line)
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
0
#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
0
void _MD_unix_terminate_waitpid_daemon(void) {
117
0
  if (pr_wp.thread) {
118
0
    pr_waitpid_daemon_exit = 1;
119
0
    write(pr_wp.pipefd[1], "", 1);
120
0
    PR_JoinThread(pr_wp.thread);
121
0
  }
122
0
}
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
0
                              char* const* envp, const PRProcessAttr* attr) {
132
0
  PRProcess* process;
133
0
  int nEnv, idx;
134
0
  char* const* childEnvp;
135
0
  char** newEnvp = NULL;
136
0
  int flags;
137
138
0
  process = PR_NEW(PRProcess);
139
0
  if (!process) {
140
0
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
141
0
    return NULL;
142
0
  }
143
144
0
  childEnvp = envp;
145
0
  if (attr && attr->fdInheritBuffer) {
146
0
    PRBool found = PR_FALSE;
147
148
0
    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
0
      childEnvp = environ;
160
0
#endif
161
0
    }
162
163
0
    for (nEnv = 0; childEnvp[nEnv]; nEnv++) {
164
0
    }
165
0
    newEnvp = (char**)PR_MALLOC((nEnv + 2) * sizeof(char*));
166
0
    if (NULL == newEnvp) {
167
0
      PR_DELETE(process);
168
0
      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
169
0
      return NULL;
170
0
    }
171
0
    for (idx = 0; idx < nEnv; idx++) {
172
0
      newEnvp[idx] = childEnvp[idx];
173
0
      if (!found && !strncmp(newEnvp[idx], "NSPR_INHERIT_FDS=", 17)) {
174
0
        newEnvp[idx] = attr->fdInheritBuffer;
175
0
        found = PR_TRUE;
176
0
      }
177
0
    }
178
0
    if (!found) {
179
0
      newEnvp[idx++] = attr->fdInheritBuffer;
180
0
    }
181
0
    newEnvp[idx] = NULL;
182
0
    childEnvp = newEnvp;
183
0
  }
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
0
  process->md.pid = fork();
235
0
#endif
236
0
  if ((pid_t)-1 == process->md.pid) {
237
0
    PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, errno);
238
0
    PR_DELETE(process);
239
0
    if (newEnvp) {
240
0
      PR_DELETE(newEnvp);
241
0
    }
242
0
    return NULL;
243
0
  }
244
0
  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
0
#if !defined(NTO)
253
0
    if (attr) {
254
      /* the osfd's to redirect stdin, stdout, and stderr to */
255
0
      int in_osfd = -1, out_osfd = -1, err_osfd = -1;
256
257
0
      if (attr->stdinFd && attr->stdinFd->secret->md.osfd != 0) {
258
0
        in_osfd = attr->stdinFd->secret->md.osfd;
259
0
        if (dup2(in_osfd, 0) != 0) {
260
0
          _exit(1); /* failed */
261
0
        }
262
0
        flags = fcntl(0, F_GETFL, 0);
263
0
        if (flags & O_NONBLOCK) {
264
0
          fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
265
0
        }
266
0
      }
267
0
      if (attr->stdoutFd && attr->stdoutFd->secret->md.osfd != 1) {
268
0
        out_osfd = attr->stdoutFd->secret->md.osfd;
269
0
        if (dup2(out_osfd, 1) != 1) {
270
0
          _exit(1); /* failed */
271
0
        }
272
0
        flags = fcntl(1, F_GETFL, 0);
273
0
        if (flags & O_NONBLOCK) {
274
0
          fcntl(1, F_SETFL, flags & ~O_NONBLOCK);
275
0
        }
276
0
      }
277
0
      if (attr->stderrFd && attr->stderrFd->secret->md.osfd != 2) {
278
0
        err_osfd = attr->stderrFd->secret->md.osfd;
279
0
        if (dup2(err_osfd, 2) != 2) {
280
0
          _exit(1); /* failed */
281
0
        }
282
0
        flags = fcntl(2, F_GETFL, 0);
283
0
        if (flags & O_NONBLOCK) {
284
0
          fcntl(2, F_SETFL, flags & ~O_NONBLOCK);
285
0
        }
286
0
      }
287
0
      if (in_osfd != -1) {
288
0
        close(in_osfd);
289
0
      }
290
0
      if (out_osfd != -1 && out_osfd != in_osfd) {
291
0
        close(out_osfd);
292
0
      }
293
0
      if (err_osfd != -1 && err_osfd != in_osfd && err_osfd != out_osfd) {
294
0
        close(err_osfd);
295
0
      }
296
0
      if (attr->currentDirectory) {
297
0
        if (chdir(attr->currentDirectory) < 0) {
298
0
          _exit(1); /* failed */
299
0
        }
300
0
      }
301
0
    }
302
303
0
    if (childEnvp) {
304
0
      (void)execve(path, argv, childEnvp);
305
0
    } else {
306
      /* Inherit the environment of the parent. */
307
0
      (void)execv(path, argv);
308
0
    }
309
    /* Whoops! It returned. That's a bad sign. */
310
0
    _exit(1);
311
0
#endif /* !NTO */
312
0
  }
313
314
0
  if (newEnvp) {
315
0
    PR_DELETE(newEnvp);
316
0
  }
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
0
  return process;
326
0
}
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
0
                                 char* const* envp, const PRProcessAttr* attr) {
345
0
  struct pr_CreateProcOp* op;
346
0
  PRProcess* proc;
347
0
  int rv;
348
349
0
  if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) {
350
0
    return NULL;
351
0
  }
352
353
0
  op = PR_NEW(struct pr_CreateProcOp);
354
0
  if (NULL == op) {
355
0
    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
356
0
    return NULL;
357
0
  }
358
0
  op->path = path;
359
0
  op->argv = argv;
360
0
  op->envp = envp;
361
0
  op->attr = attr;
362
0
  op->done = PR_FALSE;
363
0
  op->doneCV = PR_NewCondVar(pr_wp.ml);
364
0
  if (NULL == op->doneCV) {
365
0
    PR_DELETE(op);
366
0
    return NULL;
367
0
  }
368
0
  PR_Lock(pr_wp.ml);
369
370
  /* add to the tail of op queue */
371
0
  op->next = NULL;
372
0
  if (pr_wp.opTail) {
373
0
    pr_wp.opTail->next = op;
374
0
    pr_wp.opTail = op;
375
0
  } else {
376
0
    PR_ASSERT(NULL == pr_wp.opHead);
377
0
    pr_wp.opHead = pr_wp.opTail = op;
378
0
  }
379
380
  /* wake up the daemon thread */
381
0
  do {
382
0
    rv = write(pr_wp.pipefd[1], "", 1);
383
0
  } while (-1 == rv && EINTR == errno);
384
385
0
  while (op->done == PR_FALSE) {
386
0
    PR_WaitCondVar(op->doneCV, PR_INTERVAL_NO_TIMEOUT);
387
0
  }
388
0
  PR_Unlock(pr_wp.ml);
389
0
  PR_DestroyCondVar(op->doneCV);
390
0
  proc = op->process;
391
0
  if (!proc) {
392
0
    PR_SetError(op->prerror, op->oserror);
393
0
  }
394
0
  PR_DELETE(op);
395
0
  return proc;
396
0
}
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
0
#define NBUCKETS_LOG2 6
416
0
#define NBUCKETS (1 << NBUCKETS_LOG2)
417
0
#define PID_HASH_MASK ((pid_t)(NBUCKETS - 1))
418
419
0
static pr_PidRecord* FindPidTable(pid_t pid) {
420
0
  pr_PidRecord* pRec;
421
0
  int keyHash = (int)(pid & PID_HASH_MASK);
422
423
0
  pRec = pr_wp.pidTable[keyHash];
424
0
  while (pRec) {
425
0
    if (pRec->pid == pid) {
426
0
      break;
427
0
    }
428
0
    pRec = pRec->next;
429
0
  }
430
0
  return pRec;
431
0
}
432
433
0
static void InsertPidTable(pr_PidRecord* pRec) {
434
0
  int keyHash = (int)(pRec->pid & PID_HASH_MASK);
435
436
0
  pRec->next = pr_wp.pidTable[keyHash];
437
0
  pr_wp.pidTable[keyHash] = pRec;
438
0
}
439
440
0
static void DeletePidTable(pr_PidRecord* pRec) {
441
0
  int keyHash = (int)(pRec->pid & PID_HASH_MASK);
442
443
0
  if (pr_wp.pidTable[keyHash] == pRec) {
444
0
    pr_wp.pidTable[keyHash] = pRec->next;
445
0
  } else {
446
0
    pr_PidRecord *pred, *cur; /* predecessor and current */
447
448
0
    pred = pr_wp.pidTable[keyHash];
449
0
    cur = pred->next;
450
0
    while (cur) {
451
0
      if (cur == pRec) {
452
0
        pred->next = cur->next;
453
0
        break;
454
0
      }
455
0
      pred = cur;
456
0
      cur = cur->next;
457
0
    }
458
0
    PR_ASSERT(cur != NULL);
459
0
  }
460
0
}
461
462
0
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
0
  PR_ASSERT(!WIFSTOPPED(rawExitStatus));
468
0
#ifdef WIFCONTINUED
469
0
  PR_ASSERT(!WIFCONTINUED(rawExitStatus));
470
0
#endif
471
0
  if (WIFEXITED(rawExitStatus)) {
472
0
    return WEXITSTATUS(rawExitStatus);
473
0
  }
474
0
  PR_ASSERT(WIFSIGNALED(rawExitStatus));
475
0
  return _PR_SIGNALED_EXITSTATUS;
476
0
}
477
478
0
static void ProcessReapedChildInternal(pid_t pid, int status) {
479
0
  pr_PidRecord* pRec;
480
481
0
  pRec = FindPidTable(pid);
482
0
  if (NULL == pRec) {
483
0
    pRec = PR_NEW(pr_PidRecord);
484
0
    pRec->pid = pid;
485
0
    pRec->state = _PR_PID_REAPED;
486
0
    pRec->exitStatus = ExtractExitStatus(status);
487
0
    pRec->reapedCV = NULL;
488
0
    InsertPidTable(pRec);
489
0
  } else {
490
0
    PR_ASSERT(pRec->state != _PR_PID_REAPED);
491
0
    if (_PR_PID_DETACHED == pRec->state) {
492
0
      PR_ASSERT(NULL == pRec->reapedCV);
493
0
      DeletePidTable(pRec);
494
0
      PR_DELETE(pRec);
495
0
    } else {
496
0
      PR_ASSERT(_PR_PID_WAITING == pRec->state);
497
0
      PR_ASSERT(NULL != pRec->reapedCV);
498
0
      pRec->exitStatus = ExtractExitStatus(status);
499
0
      pRec->state = _PR_PID_REAPED;
500
0
      PR_NotifyCondVar(pRec->reapedCV);
501
0
    }
502
0
  }
503
0
}
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
0
static void WaitPidDaemonThread(void* unused) {
559
0
  PRPollDesc pd;
560
0
  PRFileDesc* fd;
561
0
  int rv;
562
0
  char buf[128];
563
0
  pid_t pid;
564
0
  int status;
565
0
#  ifdef _PR_SHARE_CLONES
566
0
  struct pr_CreateProcOp* op;
567
0
#  endif
568
569
0
#  ifdef _PR_SHARE_CLONES
570
0
  pr_InstallSigchldHandler();
571
0
#  endif
572
573
0
  fd = PR_ImportFile(pr_wp.pipefd[0]);
574
0
  PR_ASSERT(NULL != fd);
575
0
  pd.fd = fd;
576
0
  pd.in_flags = PR_POLL_READ;
577
578
0
  while (1) {
579
0
    rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
580
0
    PR_ASSERT(1 == rv);
581
582
0
#  ifdef _PR_SHARE_CLONES
583
0
    if (pr_waitpid_daemon_exit) {
584
0
      return;
585
0
    }
586
0
    PR_Lock(pr_wp.ml);
587
0
#  endif
588
589
0
    do {
590
0
      rv = read(pr_wp.pipefd[0], buf, sizeof(buf));
591
0
    } while (sizeof(buf) == rv || (-1 == rv && EINTR == errno));
592
593
0
#  ifdef _PR_SHARE_CLONES
594
0
    while ((op = pr_wp.opHead) != NULL) {
595
0
      PR_Unlock(pr_wp.ml);
596
0
      op->process = ForkAndExec(op->path, op->argv, op->envp, op->attr);
597
0
      if (NULL == op->process) {
598
0
        op->prerror = PR_GetError();
599
0
        op->oserror = PR_GetOSError();
600
0
      }
601
0
      PR_Lock(pr_wp.ml);
602
0
      pr_wp.opHead = op->next;
603
0
      if (NULL == pr_wp.opHead) {
604
0
        pr_wp.opTail = NULL;
605
0
      }
606
0
      op->done = PR_TRUE;
607
0
      PR_NotifyCondVar(op->doneCV);
608
0
    }
609
0
    PR_Unlock(pr_wp.ml);
610
0
#  endif
611
612
0
    while (1) {
613
0
      do {
614
0
        pid = waitpid((pid_t)-1, &status, WNOHANG);
615
0
      } while ((pid_t)-1 == pid && EINTR == errno);
616
0
      if (0 == pid) {
617
0
        break;
618
0
      }
619
0
      if ((pid_t)-1 == pid) {
620
        /* must be because we have no child processes */
621
0
        PR_ASSERT(ECHILD == errno);
622
0
        break;
623
0
      }
624
625
0
      PR_Lock(pr_wp.ml);
626
0
      ProcessReapedChildInternal(pid, status);
627
0
      PR_Unlock(pr_wp.ml);
628
0
    }
629
0
  }
630
0
}
631
632
0
static void pr_SigchldHandler(int sig) {
633
0
  int errnoCopy;
634
0
  int rv;
635
636
0
  errnoCopy = errno;
637
638
0
  do {
639
0
    rv = write(pr_wp.pipefd[1], "", 1);
640
0
  } while (-1 == rv && EINTR == errno);
641
642
0
#  ifdef DEBUG
643
0
  if (-1 == rv && EAGAIN != errno && EWOULDBLOCK != errno) {
644
0
    char* msg = "cannot write to pipe\n";
645
0
    write(2, msg, strlen(msg) + 1);
646
0
    _exit(1);
647
0
  }
648
0
#  endif
649
650
0
  errno = errnoCopy;
651
0
}
652
653
0
static void pr_InstallSigchldHandler() {
654
0
  struct sigaction act, oact;
655
0
  int rv;
656
657
0
  act.sa_handler = pr_SigchldHandler;
658
0
  sigemptyset(&act.sa_mask);
659
0
  act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
660
0
  rv = sigaction(SIGCHLD, &act, &oact);
661
0
  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
0
}
667
668
#endif /* !defined(_PR_NATIVE_THREADS) */
669
670
0
static PRStatus _MD_InitProcesses(void) {
671
0
#if !defined(_PR_NATIVE_THREADS)
672
0
  int rv;
673
0
  int flags;
674
0
#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
0
  pr_wp.ml = PR_NewLock();
688
0
  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
0
  rv = pipe(pr_wp.pipefd);
696
0
  PR_ASSERT(0 == rv);
697
0
  flags = fcntl(pr_wp.pipefd[0], F_GETFL, 0);
698
0
  fcntl(pr_wp.pipefd[0], F_SETFL, flags | O_NONBLOCK);
699
0
  flags = fcntl(pr_wp.pipefd[1], F_GETFL, 0);
700
0
  fcntl(pr_wp.pipefd[1], F_SETFL, flags | O_NONBLOCK);
701
702
#  ifndef _PR_SHARE_CLONES
703
  pr_InstallSigchldHandler();
704
#  endif
705
0
#endif /* !_PR_NATIVE_THREADS */
706
707
0
  pr_wp.thread = PR_CreateThread(PR_SYSTEM_THREAD, WaitPidDaemonThread, NULL,
708
0
                                 PR_PRIORITY_NORMAL,
709
0
#ifdef _PR_SHARE_CLONES
710
0
                                 PR_GLOBAL_THREAD,
711
#else
712
                                 PR_LOCAL_THREAD,
713
#endif
714
0
                                 PR_JOINABLE_THREAD, 0);
715
0
  PR_ASSERT(NULL != pr_wp.thread);
716
717
0
  pr_wp.pidTable = (pr_PidRecord**)PR_CALLOC(NBUCKETS * sizeof(pr_PidRecord*));
718
0
  PR_ASSERT(NULL != pr_wp.pidTable);
719
0
  return PR_SUCCESS;
720
0
}
721
722
0
PRStatus _MD_DetachUnixProcess(PRProcess* process) {
723
0
  PRStatus retVal = PR_SUCCESS;
724
0
  pr_PidRecord* pRec;
725
726
0
  PR_Lock(pr_wp.ml);
727
0
  pRec = FindPidTable(process->md.pid);
728
0
  if (NULL == pRec) {
729
0
    pRec = PR_NEW(pr_PidRecord);
730
0
    if (NULL == pRec) {
731
0
      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
732
0
      retVal = PR_FAILURE;
733
0
      goto done;
734
0
    }
735
0
    pRec->pid = process->md.pid;
736
0
    pRec->state = _PR_PID_DETACHED;
737
0
    pRec->reapedCV = NULL;
738
0
    InsertPidTable(pRec);
739
0
  } else {
740
0
    PR_ASSERT(_PR_PID_REAPED == pRec->state);
741
0
    if (_PR_PID_REAPED != pRec->state) {
742
0
      PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
743
0
      retVal = PR_FAILURE;
744
0
    } else {
745
0
      DeletePidTable(pRec);
746
0
      PR_ASSERT(NULL == pRec->reapedCV);
747
0
      PR_DELETE(pRec);
748
0
    }
749
0
  }
750
0
  PR_DELETE(process);
751
752
0
done:
753
0
  PR_Unlock(pr_wp.ml);
754
0
  return retVal;
755
0
}
756
757
0
PRStatus _MD_WaitUnixProcess(PRProcess* process, PRInt32* exitCode) {
758
0
  pr_PidRecord* pRec;
759
0
  PRStatus retVal = PR_SUCCESS;
760
0
  PRBool interrupted = PR_FALSE;
761
762
0
  PR_Lock(pr_wp.ml);
763
0
  pRec = FindPidTable(process->md.pid);
764
0
  if (NULL == pRec) {
765
0
    pRec = PR_NEW(pr_PidRecord);
766
0
    if (NULL == pRec) {
767
0
      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
768
0
      retVal = PR_FAILURE;
769
0
      goto done;
770
0
    }
771
0
    pRec->pid = process->md.pid;
772
0
    pRec->state = _PR_PID_WAITING;
773
0
    pRec->reapedCV = PR_NewCondVar(pr_wp.ml);
774
0
    if (NULL == pRec->reapedCV) {
775
0
      PR_DELETE(pRec);
776
0
      retVal = PR_FAILURE;
777
0
      goto done;
778
0
    }
779
0
    InsertPidTable(pRec);
780
0
    while (!interrupted && _PR_PID_REAPED != pRec->state) {
781
0
      if (PR_WaitCondVar(pRec->reapedCV, PR_INTERVAL_NO_TIMEOUT) ==
782
0
              PR_FAILURE &&
783
0
          PR_GetError() == PR_PENDING_INTERRUPT_ERROR) {
784
0
        interrupted = PR_TRUE;
785
0
      }
786
0
    }
787
0
    if (_PR_PID_REAPED == pRec->state) {
788
0
      if (exitCode) {
789
0
        *exitCode = pRec->exitStatus;
790
0
      }
791
0
    } else {
792
0
      PR_ASSERT(interrupted);
793
0
      retVal = PR_FAILURE;
794
0
    }
795
0
    DeletePidTable(pRec);
796
0
    PR_DestroyCondVar(pRec->reapedCV);
797
0
    PR_DELETE(pRec);
798
0
  } else {
799
0
    PR_ASSERT(_PR_PID_REAPED == pRec->state);
800
0
    PR_ASSERT(NULL == pRec->reapedCV);
801
0
    DeletePidTable(pRec);
802
0
    if (exitCode) {
803
0
      *exitCode = pRec->exitStatus;
804
0
    }
805
0
    PR_DELETE(pRec);
806
0
  }
807
0
  PR_DELETE(process);
808
809
0
done:
810
0
  PR_Unlock(pr_wp.ml);
811
0
  return retVal;
812
0
} /* _MD_WaitUnixProcess */
813
814
0
PRStatus _MD_KillUnixProcess(PRProcess* process) {
815
0
  PRErrorCode prerror;
816
0
  PRInt32 oserror;
817
818
0
  if (kill(process->md.pid, SIGKILL) == 0) {
819
0
    return PR_SUCCESS;
820
0
  }
821
0
  oserror = errno;
822
0
  switch (oserror) {
823
0
    case EPERM:
824
0
      prerror = PR_NO_ACCESS_RIGHTS_ERROR;
825
0
      break;
826
0
    case ESRCH:
827
0
      prerror = PR_INVALID_ARGUMENT_ERROR;
828
0
      break;
829
0
    default:
830
0
      prerror = PR_UNKNOWN_ERROR;
831
0
      break;
832
0
  }
833
0
  PR_SetError(prerror, oserror);
834
0
  return PR_FAILURE;
835
0
} /* _MD_KillUnixProcess */