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