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