| OLD | NEW |
| (Empty) |
| 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 */ | |
| OLD | NEW |