nsprpub/pr/src/md/unix/uxproces.c

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

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

mercurial