Mercurial > trustbridge > nss-cmake-static
comparison nspr/pr/src/md/unix/uxproces.c @ 0:1e5118fa0cb1
This is NSS with a Cmake Buildsyste
To compile a static NSS library for Windows we've used the
Chromium-NSS fork and added a Cmake buildsystem to compile
it statically for Windows. See README.chromium for chromium
changes and README.trustbridge for our modifications.
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Mon, 28 Jul 2014 10:47:06 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:1e5118fa0cb1 |
---|---|
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 */ |