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