|
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 |
|
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 * |
|
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 } else if (0 == process->md.pid) { /* the child process */ |
|
251 /* |
|
252 * If the child process needs to exit, it must call _exit(). |
|
253 * Do not call exit(), because exit() will flush and close |
|
254 * the standard I/O file descriptors, and hence corrupt |
|
255 * the parent process's standard I/O data structures. |
|
256 */ |
|
257 |
|
258 #if !defined(NTO) && !defined(SYMBIAN) |
|
259 if (attr) { |
|
260 /* the osfd's to redirect stdin, stdout, and stderr to */ |
|
261 int in_osfd = -1, out_osfd = -1, err_osfd = -1; |
|
262 |
|
263 if (attr->stdinFd |
|
264 && attr->stdinFd->secret->md.osfd != 0) { |
|
265 in_osfd = attr->stdinFd->secret->md.osfd; |
|
266 if (dup2(in_osfd, 0) != 0) { |
|
267 _exit(1); /* failed */ |
|
268 } |
|
269 flags = fcntl(0, F_GETFL, 0); |
|
270 if (flags & O_NONBLOCK) { |
|
271 fcntl(0, F_SETFL, flags & ~O_NONBLOCK); |
|
272 } |
|
273 } |
|
274 if (attr->stdoutFd |
|
275 && attr->stdoutFd->secret->md.osfd != 1) { |
|
276 out_osfd = attr->stdoutFd->secret->md.osfd; |
|
277 if (dup2(out_osfd, 1) != 1) { |
|
278 _exit(1); /* failed */ |
|
279 } |
|
280 flags = fcntl(1, F_GETFL, 0); |
|
281 if (flags & O_NONBLOCK) { |
|
282 fcntl(1, F_SETFL, flags & ~O_NONBLOCK); |
|
283 } |
|
284 } |
|
285 if (attr->stderrFd |
|
286 && attr->stderrFd->secret->md.osfd != 2) { |
|
287 err_osfd = attr->stderrFd->secret->md.osfd; |
|
288 if (dup2(err_osfd, 2) != 2) { |
|
289 _exit(1); /* failed */ |
|
290 } |
|
291 flags = fcntl(2, F_GETFL, 0); |
|
292 if (flags & O_NONBLOCK) { |
|
293 fcntl(2, F_SETFL, flags & ~O_NONBLOCK); |
|
294 } |
|
295 } |
|
296 if (in_osfd != -1) { |
|
297 close(in_osfd); |
|
298 } |
|
299 if (out_osfd != -1 && out_osfd != in_osfd) { |
|
300 close(out_osfd); |
|
301 } |
|
302 if (err_osfd != -1 && err_osfd != in_osfd |
|
303 && err_osfd != out_osfd) { |
|
304 close(err_osfd); |
|
305 } |
|
306 if (attr->currentDirectory) { |
|
307 if (chdir(attr->currentDirectory) < 0) { |
|
308 _exit(1); /* failed */ |
|
309 } |
|
310 } |
|
311 } |
|
312 |
|
313 if (childEnvp) { |
|
314 (void)execve(path, argv, childEnvp); |
|
315 } else { |
|
316 /* Inherit the environment of the parent. */ |
|
317 (void)execv(path, argv); |
|
318 } |
|
319 /* Whoops! It returned. That's a bad sign. */ |
|
320 _exit(1); |
|
321 #endif /* !NTO */ |
|
322 } |
|
323 |
|
324 if (newEnvp) { |
|
325 PR_DELETE(newEnvp); |
|
326 } |
|
327 |
|
328 #if defined(_PR_NATIVE_THREADS) |
|
329 PR_Lock(pr_wp.ml); |
|
330 if (0 == pr_wp.numProcs++) { |
|
331 PR_NotifyCondVar(pr_wp.cv); |
|
332 } |
|
333 PR_Unlock(pr_wp.ml); |
|
334 #endif |
|
335 return process; |
|
336 } |
|
337 |
|
338 #ifdef _PR_SHARE_CLONES |
|
339 |
|
340 struct pr_CreateProcOp { |
|
341 const char *path; |
|
342 char *const *argv; |
|
343 char *const *envp; |
|
344 const PRProcessAttr *attr; |
|
345 PRProcess *process; |
|
346 PRErrorCode prerror; |
|
347 PRInt32 oserror; |
|
348 PRBool done; |
|
349 PRCondVar *doneCV; |
|
350 struct pr_CreateProcOp *next; |
|
351 }; |
|
352 |
|
353 PRProcess * |
|
354 _MD_CreateUnixProcess( |
|
355 const char *path, |
|
356 char *const *argv, |
|
357 char *const *envp, |
|
358 const PRProcessAttr *attr) |
|
359 { |
|
360 struct pr_CreateProcOp *op; |
|
361 PRProcess *proc; |
|
362 int rv; |
|
363 |
|
364 if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) { |
|
365 return NULL; |
|
366 } |
|
367 |
|
368 op = PR_NEW(struct pr_CreateProcOp); |
|
369 if (NULL == op) { |
|
370 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
|
371 return NULL; |
|
372 } |
|
373 op->path = path; |
|
374 op->argv = argv; |
|
375 op->envp = envp; |
|
376 op->attr = attr; |
|
377 op->done = PR_FALSE; |
|
378 op->doneCV = PR_NewCondVar(pr_wp.ml); |
|
379 if (NULL == op->doneCV) { |
|
380 PR_DELETE(op); |
|
381 return NULL; |
|
382 } |
|
383 PR_Lock(pr_wp.ml); |
|
384 |
|
385 /* add to the tail of op queue */ |
|
386 op->next = NULL; |
|
387 if (pr_wp.opTail) { |
|
388 pr_wp.opTail->next = op; |
|
389 pr_wp.opTail = op; |
|
390 } else { |
|
391 PR_ASSERT(NULL == pr_wp.opHead); |
|
392 pr_wp.opHead = pr_wp.opTail = op; |
|
393 } |
|
394 |
|
395 /* wake up the daemon thread */ |
|
396 do { |
|
397 rv = write(pr_wp.pipefd[1], "", 1); |
|
398 } while (-1 == rv && EINTR == errno); |
|
399 |
|
400 while (op->done == PR_FALSE) { |
|
401 PR_WaitCondVar(op->doneCV, PR_INTERVAL_NO_TIMEOUT); |
|
402 } |
|
403 PR_Unlock(pr_wp.ml); |
|
404 PR_DestroyCondVar(op->doneCV); |
|
405 proc = op->process; |
|
406 if (!proc) { |
|
407 PR_SetError(op->prerror, op->oserror); |
|
408 } |
|
409 PR_DELETE(op); |
|
410 return proc; |
|
411 } |
|
412 |
|
413 #else /* ! _PR_SHARE_CLONES */ |
|
414 |
|
415 PRProcess * |
|
416 _MD_CreateUnixProcess( |
|
417 const char *path, |
|
418 char *const *argv, |
|
419 char *const *envp, |
|
420 const PRProcessAttr *attr) |
|
421 { |
|
422 if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) { |
|
423 return NULL; |
|
424 } |
|
425 return ForkAndExec(path, argv, envp, attr); |
|
426 } /* _MD_CreateUnixProcess */ |
|
427 |
|
428 #endif /* _PR_SHARE_CLONES */ |
|
429 |
|
430 /* |
|
431 * The pid table is a hashtable. |
|
432 * |
|
433 * The number of buckets in the hashtable (NBUCKETS) must be a power of 2. |
|
434 */ |
|
435 #define NBUCKETS_LOG2 6 |
|
436 #define NBUCKETS (1 << NBUCKETS_LOG2) |
|
437 #define PID_HASH_MASK ((pid_t) (NBUCKETS - 1)) |
|
438 |
|
439 static pr_PidRecord * |
|
440 FindPidTable(pid_t pid) |
|
441 { |
|
442 pr_PidRecord *pRec; |
|
443 int keyHash = (int) (pid & PID_HASH_MASK); |
|
444 |
|
445 pRec = pr_wp.pidTable[keyHash]; |
|
446 while (pRec) { |
|
447 if (pRec->pid == pid) { |
|
448 break; |
|
449 } |
|
450 pRec = pRec->next; |
|
451 } |
|
452 return pRec; |
|
453 } |
|
454 |
|
455 static void |
|
456 InsertPidTable(pr_PidRecord *pRec) |
|
457 { |
|
458 int keyHash = (int) (pRec->pid & PID_HASH_MASK); |
|
459 |
|
460 pRec->next = pr_wp.pidTable[keyHash]; |
|
461 pr_wp.pidTable[keyHash] = pRec; |
|
462 } |
|
463 |
|
464 static void |
|
465 DeletePidTable(pr_PidRecord *pRec) |
|
466 { |
|
467 int keyHash = (int) (pRec->pid & PID_HASH_MASK); |
|
468 |
|
469 if (pr_wp.pidTable[keyHash] == pRec) { |
|
470 pr_wp.pidTable[keyHash] = pRec->next; |
|
471 } else { |
|
472 pr_PidRecord *pred, *cur; /* predecessor and current */ |
|
473 |
|
474 pred = pr_wp.pidTable[keyHash]; |
|
475 cur = pred->next; |
|
476 while (cur) { |
|
477 if (cur == pRec) { |
|
478 pred->next = cur->next; |
|
479 break; |
|
480 } |
|
481 pred = cur; |
|
482 cur = cur->next; |
|
483 } |
|
484 PR_ASSERT(cur != NULL); |
|
485 } |
|
486 } |
|
487 |
|
488 static int |
|
489 ExtractExitStatus(int rawExitStatus) |
|
490 { |
|
491 /* |
|
492 * We did not specify the WCONTINUED and WUNTRACED options |
|
493 * for waitpid, so these two events should not be reported. |
|
494 */ |
|
495 PR_ASSERT(!WIFSTOPPED(rawExitStatus)); |
|
496 #ifdef WIFCONTINUED |
|
497 PR_ASSERT(!WIFCONTINUED(rawExitStatus)); |
|
498 #endif |
|
499 if (WIFEXITED(rawExitStatus)) { |
|
500 return WEXITSTATUS(rawExitStatus); |
|
501 } else { |
|
502 PR_ASSERT(WIFSIGNALED(rawExitStatus)); |
|
503 return _PR_SIGNALED_EXITSTATUS; |
|
504 } |
|
505 } |
|
506 |
|
507 static void |
|
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 |
|
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 |
|
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 PR_Unlock(pr_wp.ml); |
|
628 while ((op = pr_wp.opHead) != NULL) { |
|
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 PR_Unlock(pr_wp.ml); |
|
643 } |
|
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 |
|
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 |
|
686 static void pr_InstallSigchldHandler() |
|
687 { |
|
688 #if defined(HPUX) && defined(_PR_DCETHREADS) |
|
689 #error "HP-UX DCE threads have their own SIGCHLD handler" |
|
690 #endif |
|
691 |
|
692 struct sigaction act, oact; |
|
693 int rv; |
|
694 |
|
695 act.sa_handler = pr_SigchldHandler; |
|
696 sigemptyset(&act.sa_mask); |
|
697 act.sa_flags = SA_NOCLDSTOP | SA_RESTART; |
|
698 rv = sigaction(SIGCHLD, &act, &oact); |
|
699 PR_ASSERT(0 == rv); |
|
700 /* Make sure we are not overriding someone else's SIGCHLD handler */ |
|
701 #ifndef _PR_SHARE_CLONES |
|
702 PR_ASSERT(oact.sa_handler == SIG_DFL); |
|
703 #endif |
|
704 } |
|
705 |
|
706 #endif /* !defined(_PR_NATIVE_THREADS) */ |
|
707 |
|
708 static PRStatus _MD_InitProcesses(void) |
|
709 { |
|
710 #if !defined(_PR_NATIVE_THREADS) |
|
711 int rv; |
|
712 int flags; |
|
713 #endif |
|
714 |
|
715 #ifdef AIX |
|
716 { |
|
717 void *handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL); |
|
718 pr_wp.forkptr = (pid_t (*)(void)) dlsym(handle, "f_fork"); |
|
719 if (!pr_wp.forkptr) { |
|
720 pr_wp.forkptr = fork; |
|
721 } |
|
722 dlclose(handle); |
|
723 } |
|
724 #endif /* AIX */ |
|
725 |
|
726 pr_wp.ml = PR_NewLock(); |
|
727 PR_ASSERT(NULL != pr_wp.ml); |
|
728 |
|
729 #if defined(_PR_NATIVE_THREADS) |
|
730 pr_wp.numProcs = 0; |
|
731 pr_wp.cv = PR_NewCondVar(pr_wp.ml); |
|
732 PR_ASSERT(NULL != pr_wp.cv); |
|
733 #else |
|
734 rv = pipe(pr_wp.pipefd); |
|
735 PR_ASSERT(0 == rv); |
|
736 flags = fcntl(pr_wp.pipefd[0], F_GETFL, 0); |
|
737 fcntl(pr_wp.pipefd[0], F_SETFL, flags | O_NONBLOCK); |
|
738 flags = fcntl(pr_wp.pipefd[1], F_GETFL, 0); |
|
739 fcntl(pr_wp.pipefd[1], F_SETFL, flags | O_NONBLOCK); |
|
740 |
|
741 #ifndef _PR_SHARE_CLONES |
|
742 pr_InstallSigchldHandler(); |
|
743 #endif |
|
744 #endif /* !_PR_NATIVE_THREADS */ |
|
745 |
|
746 pr_wp.thread = PR_CreateThread(PR_SYSTEM_THREAD, |
|
747 WaitPidDaemonThread, NULL, PR_PRIORITY_NORMAL, |
|
748 #ifdef _PR_SHARE_CLONES |
|
749 PR_GLOBAL_THREAD, |
|
750 #else |
|
751 PR_LOCAL_THREAD, |
|
752 #endif |
|
753 PR_JOINABLE_THREAD, 0); |
|
754 PR_ASSERT(NULL != pr_wp.thread); |
|
755 |
|
756 pr_wp.pidTable = (pr_PidRecord**)PR_CALLOC(NBUCKETS * sizeof(pr_PidRecord *)); |
|
757 PR_ASSERT(NULL != pr_wp.pidTable); |
|
758 return PR_SUCCESS; |
|
759 } |
|
760 |
|
761 PRStatus _MD_DetachUnixProcess(PRProcess *process) |
|
762 { |
|
763 PRStatus retVal = PR_SUCCESS; |
|
764 pr_PidRecord *pRec; |
|
765 |
|
766 PR_Lock(pr_wp.ml); |
|
767 pRec = FindPidTable(process->md.pid); |
|
768 if (NULL == pRec) { |
|
769 pRec = PR_NEW(pr_PidRecord); |
|
770 if (NULL == pRec) { |
|
771 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
|
772 retVal = PR_FAILURE; |
|
773 goto done; |
|
774 } |
|
775 pRec->pid = process->md.pid; |
|
776 pRec->state = _PR_PID_DETACHED; |
|
777 pRec->reapedCV = NULL; |
|
778 InsertPidTable(pRec); |
|
779 } else { |
|
780 PR_ASSERT(_PR_PID_REAPED == pRec->state); |
|
781 if (_PR_PID_REAPED != pRec->state) { |
|
782 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
|
783 retVal = PR_FAILURE; |
|
784 } else { |
|
785 DeletePidTable(pRec); |
|
786 PR_ASSERT(NULL == pRec->reapedCV); |
|
787 PR_DELETE(pRec); |
|
788 } |
|
789 } |
|
790 PR_DELETE(process); |
|
791 |
|
792 done: |
|
793 PR_Unlock(pr_wp.ml); |
|
794 return retVal; |
|
795 } |
|
796 |
|
797 PRStatus _MD_WaitUnixProcess( |
|
798 PRProcess *process, |
|
799 PRInt32 *exitCode) |
|
800 { |
|
801 pr_PidRecord *pRec; |
|
802 PRStatus retVal = PR_SUCCESS; |
|
803 PRBool interrupted = PR_FALSE; |
|
804 |
|
805 PR_Lock(pr_wp.ml); |
|
806 pRec = FindPidTable(process->md.pid); |
|
807 if (NULL == pRec) { |
|
808 pRec = PR_NEW(pr_PidRecord); |
|
809 if (NULL == pRec) { |
|
810 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
|
811 retVal = PR_FAILURE; |
|
812 goto done; |
|
813 } |
|
814 pRec->pid = process->md.pid; |
|
815 pRec->state = _PR_PID_WAITING; |
|
816 pRec->reapedCV = PR_NewCondVar(pr_wp.ml); |
|
817 if (NULL == pRec->reapedCV) { |
|
818 PR_DELETE(pRec); |
|
819 retVal = PR_FAILURE; |
|
820 goto done; |
|
821 } |
|
822 InsertPidTable(pRec); |
|
823 while (!interrupted && _PR_PID_REAPED != pRec->state) { |
|
824 if (PR_WaitCondVar(pRec->reapedCV, |
|
825 PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE |
|
826 && PR_GetError() == PR_PENDING_INTERRUPT_ERROR) { |
|
827 interrupted = PR_TRUE; |
|
828 } |
|
829 } |
|
830 if (_PR_PID_REAPED == pRec->state) { |
|
831 if (exitCode) { |
|
832 *exitCode = pRec->exitStatus; |
|
833 } |
|
834 } else { |
|
835 PR_ASSERT(interrupted); |
|
836 retVal = PR_FAILURE; |
|
837 } |
|
838 DeletePidTable(pRec); |
|
839 PR_DestroyCondVar(pRec->reapedCV); |
|
840 PR_DELETE(pRec); |
|
841 } else { |
|
842 PR_ASSERT(_PR_PID_REAPED == pRec->state); |
|
843 PR_ASSERT(NULL == pRec->reapedCV); |
|
844 DeletePidTable(pRec); |
|
845 if (exitCode) { |
|
846 *exitCode = pRec->exitStatus; |
|
847 } |
|
848 PR_DELETE(pRec); |
|
849 } |
|
850 PR_DELETE(process); |
|
851 |
|
852 done: |
|
853 PR_Unlock(pr_wp.ml); |
|
854 return retVal; |
|
855 } /* _MD_WaitUnixProcess */ |
|
856 |
|
857 PRStatus _MD_KillUnixProcess(PRProcess *process) |
|
858 { |
|
859 PRErrorCode prerror; |
|
860 PRInt32 oserror; |
|
861 |
|
862 #ifdef SYMBIAN |
|
863 /* In Symbian OS, we can not kill other process with Open C */ |
|
864 PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, oserror); |
|
865 return PR_FAILURE; |
|
866 #else |
|
867 if (kill(process->md.pid, SIGKILL) == 0) { |
|
868 return PR_SUCCESS; |
|
869 } |
|
870 oserror = errno; |
|
871 switch (oserror) { |
|
872 case EPERM: |
|
873 prerror = PR_NO_ACCESS_RIGHTS_ERROR; |
|
874 break; |
|
875 case ESRCH: |
|
876 prerror = PR_INVALID_ARGUMENT_ERROR; |
|
877 break; |
|
878 default: |
|
879 prerror = PR_UNKNOWN_ERROR; |
|
880 break; |
|
881 } |
|
882 PR_SetError(prerror, oserror); |
|
883 return PR_FAILURE; |
|
884 #endif |
|
885 } /* _MD_KillUnixProcess */ |