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