1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/src/md/unix/uxproces.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,885 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "primpl.h" 1.10 + 1.11 +#include <sys/types.h> 1.12 +#include <unistd.h> 1.13 +#include <fcntl.h> 1.14 +#include <signal.h> 1.15 +#include <sys/wait.h> 1.16 +#include <string.h> 1.17 +#if defined(AIX) 1.18 +#include <dlfcn.h> /* For dlopen, dlsym, dlclose */ 1.19 +#endif 1.20 + 1.21 +#if defined(DARWIN) 1.22 +#if defined(HAVE_CRT_EXTERNS_H) 1.23 +#include <crt_externs.h> 1.24 +#endif 1.25 +#else 1.26 +PR_IMPORT_DATA(char **) environ; 1.27 +#endif 1.28 + 1.29 +/* 1.30 + * HP-UX 9 doesn't have the SA_RESTART flag. 1.31 + */ 1.32 +#ifndef SA_RESTART 1.33 +#define SA_RESTART 0 1.34 +#endif 1.35 + 1.36 +/* 1.37 + ********************************************************************** 1.38 + * 1.39 + * The Unix process routines 1.40 + * 1.41 + ********************************************************************** 1.42 + */ 1.43 + 1.44 +#define _PR_SIGNALED_EXITSTATUS 256 1.45 + 1.46 +typedef enum pr_PidState { 1.47 + _PR_PID_DETACHED, 1.48 + _PR_PID_REAPED, 1.49 + _PR_PID_WAITING 1.50 +} pr_PidState; 1.51 + 1.52 +typedef struct pr_PidRecord { 1.53 + pid_t pid; 1.54 + int exitStatus; 1.55 + pr_PidState state; 1.56 + PRCondVar *reapedCV; 1.57 + struct pr_PidRecord *next; 1.58 +} pr_PidRecord; 1.59 + 1.60 +/* 1.61 + * Irix sprocs and LinuxThreads are actually a kind of processes 1.62 + * that can share the virtual address space and file descriptors. 1.63 + */ 1.64 +#if (defined(IRIX) && !defined(_PR_PTHREADS)) \ 1.65 + || ((defined(LINUX) || defined(__GNU__) || defined(__GLIBC__)) \ 1.66 + && defined(_PR_PTHREADS)) 1.67 +#define _PR_SHARE_CLONES 1.68 +#endif 1.69 + 1.70 +/* 1.71 + * The macro _PR_NATIVE_THREADS indicates that we are 1.72 + * using native threads only, so waitpid() blocks just the 1.73 + * calling thread, not the process. In this case, the waitpid 1.74 + * daemon thread can safely block in waitpid(). So we don't 1.75 + * need to catch SIGCHLD, and the pipe to unblock PR_Poll() is 1.76 + * also not necessary. 1.77 + */ 1.78 + 1.79 +#if defined(_PR_GLOBAL_THREADS_ONLY) \ 1.80 + || (defined(_PR_PTHREADS) \ 1.81 + && !defined(LINUX) && !defined(__GNU__) && !defined(__GLIBC__)) 1.82 +#define _PR_NATIVE_THREADS 1.83 +#endif 1.84 + 1.85 +/* 1.86 + * All the static variables used by the Unix process routines are 1.87 + * collected in this structure. 1.88 + */ 1.89 + 1.90 +static struct { 1.91 + PRCallOnceType once; 1.92 + PRThread *thread; 1.93 + PRLock *ml; 1.94 +#if defined(_PR_NATIVE_THREADS) 1.95 + PRInt32 numProcs; 1.96 + PRCondVar *cv; 1.97 +#else 1.98 + int pipefd[2]; 1.99 +#endif 1.100 + pr_PidRecord **pidTable; 1.101 + 1.102 +#ifdef _PR_SHARE_CLONES 1.103 + struct pr_CreateProcOp *opHead, *opTail; 1.104 +#endif 1.105 + 1.106 +#ifdef AIX 1.107 + pid_t (*forkptr)(void); /* Newer versions of AIX (starting in 4.3.2) 1.108 + * have f_fork, which is faster than the 1.109 + * regular fork in a multithreaded process 1.110 + * because it skips calling the fork handlers. 1.111 + * So we look up the f_fork symbol to see if 1.112 + * it's available and fall back on fork. 1.113 + */ 1.114 +#endif /* AIX */ 1.115 +} pr_wp; 1.116 + 1.117 +#ifdef _PR_SHARE_CLONES 1.118 +static int pr_waitpid_daemon_exit; 1.119 + 1.120 +void 1.121 +_MD_unix_terminate_waitpid_daemon(void) 1.122 +{ 1.123 + if (pr_wp.thread) { 1.124 + pr_waitpid_daemon_exit = 1; 1.125 + write(pr_wp.pipefd[1], "", 1); 1.126 + PR_JoinThread(pr_wp.thread); 1.127 + } 1.128 +} 1.129 +#endif 1.130 + 1.131 +static PRStatus _MD_InitProcesses(void); 1.132 +#if !defined(_PR_NATIVE_THREADS) 1.133 +static void pr_InstallSigchldHandler(void); 1.134 +#endif 1.135 + 1.136 +static PRProcess * 1.137 +ForkAndExec( 1.138 + const char *path, 1.139 + char *const *argv, 1.140 + char *const *envp, 1.141 + const PRProcessAttr *attr) 1.142 +{ 1.143 + PRProcess *process; 1.144 + int nEnv, idx; 1.145 + char *const *childEnvp; 1.146 + char **newEnvp = NULL; 1.147 + int flags; 1.148 + 1.149 + process = PR_NEW(PRProcess); 1.150 + if (!process) { 1.151 + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 1.152 + return NULL; 1.153 + } 1.154 + 1.155 + childEnvp = envp; 1.156 + if (attr && attr->fdInheritBuffer) { 1.157 + PRBool found = PR_FALSE; 1.158 + 1.159 + if (NULL == childEnvp) { 1.160 +#ifdef DARWIN 1.161 +#ifdef HAVE_CRT_EXTERNS_H 1.162 + childEnvp = *(_NSGetEnviron()); 1.163 +#else 1.164 + /* _NSGetEnviron() is not available on iOS. */ 1.165 + PR_DELETE(process); 1.166 + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); 1.167 + return NULL; 1.168 +#endif 1.169 +#else 1.170 + childEnvp = environ; 1.171 +#endif 1.172 + } 1.173 + 1.174 + for (nEnv = 0; childEnvp[nEnv]; nEnv++) { 1.175 + } 1.176 + newEnvp = (char **) PR_MALLOC((nEnv + 2) * sizeof(char *)); 1.177 + if (NULL == newEnvp) { 1.178 + PR_DELETE(process); 1.179 + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 1.180 + return NULL; 1.181 + } 1.182 + for (idx = 0; idx < nEnv; idx++) { 1.183 + newEnvp[idx] = childEnvp[idx]; 1.184 + if (!found && !strncmp(newEnvp[idx], "NSPR_INHERIT_FDS=", 17)) { 1.185 + newEnvp[idx] = attr->fdInheritBuffer; 1.186 + found = PR_TRUE; 1.187 + } 1.188 + } 1.189 + if (!found) { 1.190 + newEnvp[idx++] = attr->fdInheritBuffer; 1.191 + } 1.192 + newEnvp[idx] = NULL; 1.193 + childEnvp = newEnvp; 1.194 + } 1.195 + 1.196 +#ifdef AIX 1.197 + process->md.pid = (*pr_wp.forkptr)(); 1.198 +#elif defined(NTO) || defined(SYMBIAN) 1.199 + /* 1.200 + * fork() & exec() does not work in a multithreaded process. 1.201 + * Use spawn() instead. 1.202 + */ 1.203 + { 1.204 + int fd_map[3] = { 0, 1, 2 }; 1.205 + 1.206 + if (attr) { 1.207 + if (attr->stdinFd && attr->stdinFd->secret->md.osfd != 0) { 1.208 + fd_map[0] = dup(attr->stdinFd->secret->md.osfd); 1.209 + flags = fcntl(fd_map[0], F_GETFL, 0); 1.210 + if (flags & O_NONBLOCK) 1.211 + fcntl(fd_map[0], F_SETFL, flags & ~O_NONBLOCK); 1.212 + } 1.213 + if (attr->stdoutFd && attr->stdoutFd->secret->md.osfd != 1) { 1.214 + fd_map[1] = dup(attr->stdoutFd->secret->md.osfd); 1.215 + flags = fcntl(fd_map[1], F_GETFL, 0); 1.216 + if (flags & O_NONBLOCK) 1.217 + fcntl(fd_map[1], F_SETFL, flags & ~O_NONBLOCK); 1.218 + } 1.219 + if (attr->stderrFd && attr->stderrFd->secret->md.osfd != 2) { 1.220 + fd_map[2] = dup(attr->stderrFd->secret->md.osfd); 1.221 + flags = fcntl(fd_map[2], F_GETFL, 0); 1.222 + if (flags & O_NONBLOCK) 1.223 + fcntl(fd_map[2], F_SETFL, flags & ~O_NONBLOCK); 1.224 + } 1.225 + 1.226 + PR_ASSERT(attr->currentDirectory == NULL); /* not implemented */ 1.227 + } 1.228 + 1.229 +#ifdef SYMBIAN 1.230 + /* In Symbian OS, we use posix_spawn instead of fork() and exec() */ 1.231 + posix_spawn(&(process->md.pid), path, NULL, NULL, argv, childEnvp); 1.232 +#else 1.233 + process->md.pid = spawn(path, 3, fd_map, NULL, argv, childEnvp); 1.234 +#endif 1.235 + 1.236 + if (fd_map[0] != 0) 1.237 + close(fd_map[0]); 1.238 + if (fd_map[1] != 1) 1.239 + close(fd_map[1]); 1.240 + if (fd_map[2] != 2) 1.241 + close(fd_map[2]); 1.242 + } 1.243 +#else 1.244 + process->md.pid = fork(); 1.245 +#endif 1.246 + if ((pid_t) -1 == process->md.pid) { 1.247 + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, errno); 1.248 + PR_DELETE(process); 1.249 + if (newEnvp) { 1.250 + PR_DELETE(newEnvp); 1.251 + } 1.252 + return NULL; 1.253 + } else if (0 == process->md.pid) { /* the child process */ 1.254 + /* 1.255 + * If the child process needs to exit, it must call _exit(). 1.256 + * Do not call exit(), because exit() will flush and close 1.257 + * the standard I/O file descriptors, and hence corrupt 1.258 + * the parent process's standard I/O data structures. 1.259 + */ 1.260 + 1.261 +#if !defined(NTO) && !defined(SYMBIAN) 1.262 + if (attr) { 1.263 + /* the osfd's to redirect stdin, stdout, and stderr to */ 1.264 + int in_osfd = -1, out_osfd = -1, err_osfd = -1; 1.265 + 1.266 + if (attr->stdinFd 1.267 + && attr->stdinFd->secret->md.osfd != 0) { 1.268 + in_osfd = attr->stdinFd->secret->md.osfd; 1.269 + if (dup2(in_osfd, 0) != 0) { 1.270 + _exit(1); /* failed */ 1.271 + } 1.272 + flags = fcntl(0, F_GETFL, 0); 1.273 + if (flags & O_NONBLOCK) { 1.274 + fcntl(0, F_SETFL, flags & ~O_NONBLOCK); 1.275 + } 1.276 + } 1.277 + if (attr->stdoutFd 1.278 + && attr->stdoutFd->secret->md.osfd != 1) { 1.279 + out_osfd = attr->stdoutFd->secret->md.osfd; 1.280 + if (dup2(out_osfd, 1) != 1) { 1.281 + _exit(1); /* failed */ 1.282 + } 1.283 + flags = fcntl(1, F_GETFL, 0); 1.284 + if (flags & O_NONBLOCK) { 1.285 + fcntl(1, F_SETFL, flags & ~O_NONBLOCK); 1.286 + } 1.287 + } 1.288 + if (attr->stderrFd 1.289 + && attr->stderrFd->secret->md.osfd != 2) { 1.290 + err_osfd = attr->stderrFd->secret->md.osfd; 1.291 + if (dup2(err_osfd, 2) != 2) { 1.292 + _exit(1); /* failed */ 1.293 + } 1.294 + flags = fcntl(2, F_GETFL, 0); 1.295 + if (flags & O_NONBLOCK) { 1.296 + fcntl(2, F_SETFL, flags & ~O_NONBLOCK); 1.297 + } 1.298 + } 1.299 + if (in_osfd != -1) { 1.300 + close(in_osfd); 1.301 + } 1.302 + if (out_osfd != -1 && out_osfd != in_osfd) { 1.303 + close(out_osfd); 1.304 + } 1.305 + if (err_osfd != -1 && err_osfd != in_osfd 1.306 + && err_osfd != out_osfd) { 1.307 + close(err_osfd); 1.308 + } 1.309 + if (attr->currentDirectory) { 1.310 + if (chdir(attr->currentDirectory) < 0) { 1.311 + _exit(1); /* failed */ 1.312 + } 1.313 + } 1.314 + } 1.315 + 1.316 + if (childEnvp) { 1.317 + (void)execve(path, argv, childEnvp); 1.318 + } else { 1.319 + /* Inherit the environment of the parent. */ 1.320 + (void)execv(path, argv); 1.321 + } 1.322 + /* Whoops! It returned. That's a bad sign. */ 1.323 + _exit(1); 1.324 +#endif /* !NTO */ 1.325 + } 1.326 + 1.327 + if (newEnvp) { 1.328 + PR_DELETE(newEnvp); 1.329 + } 1.330 + 1.331 +#if defined(_PR_NATIVE_THREADS) 1.332 + PR_Lock(pr_wp.ml); 1.333 + if (0 == pr_wp.numProcs++) { 1.334 + PR_NotifyCondVar(pr_wp.cv); 1.335 + } 1.336 + PR_Unlock(pr_wp.ml); 1.337 +#endif 1.338 + return process; 1.339 +} 1.340 + 1.341 +#ifdef _PR_SHARE_CLONES 1.342 + 1.343 +struct pr_CreateProcOp { 1.344 + const char *path; 1.345 + char *const *argv; 1.346 + char *const *envp; 1.347 + const PRProcessAttr *attr; 1.348 + PRProcess *process; 1.349 + PRErrorCode prerror; 1.350 + PRInt32 oserror; 1.351 + PRBool done; 1.352 + PRCondVar *doneCV; 1.353 + struct pr_CreateProcOp *next; 1.354 +}; 1.355 + 1.356 +PRProcess * 1.357 +_MD_CreateUnixProcess( 1.358 + const char *path, 1.359 + char *const *argv, 1.360 + char *const *envp, 1.361 + const PRProcessAttr *attr) 1.362 +{ 1.363 + struct pr_CreateProcOp *op; 1.364 + PRProcess *proc; 1.365 + int rv; 1.366 + 1.367 + if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) { 1.368 + return NULL; 1.369 + } 1.370 + 1.371 + op = PR_NEW(struct pr_CreateProcOp); 1.372 + if (NULL == op) { 1.373 + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 1.374 + return NULL; 1.375 + } 1.376 + op->path = path; 1.377 + op->argv = argv; 1.378 + op->envp = envp; 1.379 + op->attr = attr; 1.380 + op->done = PR_FALSE; 1.381 + op->doneCV = PR_NewCondVar(pr_wp.ml); 1.382 + if (NULL == op->doneCV) { 1.383 + PR_DELETE(op); 1.384 + return NULL; 1.385 + } 1.386 + PR_Lock(pr_wp.ml); 1.387 + 1.388 + /* add to the tail of op queue */ 1.389 + op->next = NULL; 1.390 + if (pr_wp.opTail) { 1.391 + pr_wp.opTail->next = op; 1.392 + pr_wp.opTail = op; 1.393 + } else { 1.394 + PR_ASSERT(NULL == pr_wp.opHead); 1.395 + pr_wp.opHead = pr_wp.opTail = op; 1.396 + } 1.397 + 1.398 + /* wake up the daemon thread */ 1.399 + do { 1.400 + rv = write(pr_wp.pipefd[1], "", 1); 1.401 + } while (-1 == rv && EINTR == errno); 1.402 + 1.403 + while (op->done == PR_FALSE) { 1.404 + PR_WaitCondVar(op->doneCV, PR_INTERVAL_NO_TIMEOUT); 1.405 + } 1.406 + PR_Unlock(pr_wp.ml); 1.407 + PR_DestroyCondVar(op->doneCV); 1.408 + proc = op->process; 1.409 + if (!proc) { 1.410 + PR_SetError(op->prerror, op->oserror); 1.411 + } 1.412 + PR_DELETE(op); 1.413 + return proc; 1.414 +} 1.415 + 1.416 +#else /* ! _PR_SHARE_CLONES */ 1.417 + 1.418 +PRProcess * 1.419 +_MD_CreateUnixProcess( 1.420 + const char *path, 1.421 + char *const *argv, 1.422 + char *const *envp, 1.423 + const PRProcessAttr *attr) 1.424 +{ 1.425 + if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) { 1.426 + return NULL; 1.427 + } 1.428 + return ForkAndExec(path, argv, envp, attr); 1.429 +} /* _MD_CreateUnixProcess */ 1.430 + 1.431 +#endif /* _PR_SHARE_CLONES */ 1.432 + 1.433 +/* 1.434 + * The pid table is a hashtable. 1.435 + * 1.436 + * The number of buckets in the hashtable (NBUCKETS) must be a power of 2. 1.437 + */ 1.438 +#define NBUCKETS_LOG2 6 1.439 +#define NBUCKETS (1 << NBUCKETS_LOG2) 1.440 +#define PID_HASH_MASK ((pid_t) (NBUCKETS - 1)) 1.441 + 1.442 +static pr_PidRecord * 1.443 +FindPidTable(pid_t pid) 1.444 +{ 1.445 + pr_PidRecord *pRec; 1.446 + int keyHash = (int) (pid & PID_HASH_MASK); 1.447 + 1.448 + pRec = pr_wp.pidTable[keyHash]; 1.449 + while (pRec) { 1.450 + if (pRec->pid == pid) { 1.451 + break; 1.452 + } 1.453 + pRec = pRec->next; 1.454 + } 1.455 + return pRec; 1.456 +} 1.457 + 1.458 +static void 1.459 +InsertPidTable(pr_PidRecord *pRec) 1.460 +{ 1.461 + int keyHash = (int) (pRec->pid & PID_HASH_MASK); 1.462 + 1.463 + pRec->next = pr_wp.pidTable[keyHash]; 1.464 + pr_wp.pidTable[keyHash] = pRec; 1.465 +} 1.466 + 1.467 +static void 1.468 +DeletePidTable(pr_PidRecord *pRec) 1.469 +{ 1.470 + int keyHash = (int) (pRec->pid & PID_HASH_MASK); 1.471 + 1.472 + if (pr_wp.pidTable[keyHash] == pRec) { 1.473 + pr_wp.pidTable[keyHash] = pRec->next; 1.474 + } else { 1.475 + pr_PidRecord *pred, *cur; /* predecessor and current */ 1.476 + 1.477 + pred = pr_wp.pidTable[keyHash]; 1.478 + cur = pred->next; 1.479 + while (cur) { 1.480 + if (cur == pRec) { 1.481 + pred->next = cur->next; 1.482 + break; 1.483 + } 1.484 + pred = cur; 1.485 + cur = cur->next; 1.486 + } 1.487 + PR_ASSERT(cur != NULL); 1.488 + } 1.489 +} 1.490 + 1.491 +static int 1.492 +ExtractExitStatus(int rawExitStatus) 1.493 +{ 1.494 + /* 1.495 + * We did not specify the WCONTINUED and WUNTRACED options 1.496 + * for waitpid, so these two events should not be reported. 1.497 + */ 1.498 + PR_ASSERT(!WIFSTOPPED(rawExitStatus)); 1.499 +#ifdef WIFCONTINUED 1.500 + PR_ASSERT(!WIFCONTINUED(rawExitStatus)); 1.501 +#endif 1.502 + if (WIFEXITED(rawExitStatus)) { 1.503 + return WEXITSTATUS(rawExitStatus); 1.504 + } else { 1.505 + PR_ASSERT(WIFSIGNALED(rawExitStatus)); 1.506 + return _PR_SIGNALED_EXITSTATUS; 1.507 + } 1.508 +} 1.509 + 1.510 +static void 1.511 +ProcessReapedChildInternal(pid_t pid, int status) 1.512 +{ 1.513 + pr_PidRecord *pRec; 1.514 + 1.515 + pRec = FindPidTable(pid); 1.516 + if (NULL == pRec) { 1.517 + pRec = PR_NEW(pr_PidRecord); 1.518 + pRec->pid = pid; 1.519 + pRec->state = _PR_PID_REAPED; 1.520 + pRec->exitStatus = ExtractExitStatus(status); 1.521 + pRec->reapedCV = NULL; 1.522 + InsertPidTable(pRec); 1.523 + } else { 1.524 + PR_ASSERT(pRec->state != _PR_PID_REAPED); 1.525 + if (_PR_PID_DETACHED == pRec->state) { 1.526 + PR_ASSERT(NULL == pRec->reapedCV); 1.527 + DeletePidTable(pRec); 1.528 + PR_DELETE(pRec); 1.529 + } else { 1.530 + PR_ASSERT(_PR_PID_WAITING == pRec->state); 1.531 + PR_ASSERT(NULL != pRec->reapedCV); 1.532 + pRec->exitStatus = ExtractExitStatus(status); 1.533 + pRec->state = _PR_PID_REAPED; 1.534 + PR_NotifyCondVar(pRec->reapedCV); 1.535 + } 1.536 + } 1.537 +} 1.538 + 1.539 +#if defined(_PR_NATIVE_THREADS) 1.540 + 1.541 +/* 1.542 + * If all the threads are native threads, the daemon thread is 1.543 + * simpler. We don't need to catch the SIGCHLD signal. We can 1.544 + * just have the daemon thread block in waitpid(). 1.545 + */ 1.546 + 1.547 +static void WaitPidDaemonThread(void *unused) 1.548 +{ 1.549 + pid_t pid; 1.550 + int status; 1.551 + 1.552 + while (1) { 1.553 + PR_Lock(pr_wp.ml); 1.554 + while (0 == pr_wp.numProcs) { 1.555 + PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT); 1.556 + } 1.557 + PR_Unlock(pr_wp.ml); 1.558 + 1.559 + while (1) { 1.560 + do { 1.561 + pid = waitpid((pid_t) -1, &status, 0); 1.562 + } while ((pid_t) -1 == pid && EINTR == errno); 1.563 + 1.564 + /* 1.565 + * waitpid() cannot return 0 because we did not invoke it 1.566 + * with the WNOHANG option. 1.567 + */ 1.568 + PR_ASSERT(0 != pid); 1.569 + 1.570 + /* 1.571 + * The only possible error code is ECHILD. But if we do 1.572 + * our accounting correctly, we should only call waitpid() 1.573 + * when there is a child process to wait for. 1.574 + */ 1.575 + PR_ASSERT((pid_t) -1 != pid); 1.576 + if ((pid_t) -1 == pid) { 1.577 + break; 1.578 + } 1.579 + 1.580 + PR_Lock(pr_wp.ml); 1.581 + ProcessReapedChildInternal(pid, status); 1.582 + pr_wp.numProcs--; 1.583 + while (0 == pr_wp.numProcs) { 1.584 + PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT); 1.585 + } 1.586 + PR_Unlock(pr_wp.ml); 1.587 + } 1.588 + } 1.589 +} 1.590 + 1.591 +#else /* _PR_NATIVE_THREADS */ 1.592 + 1.593 +static void WaitPidDaemonThread(void *unused) 1.594 +{ 1.595 + PRPollDesc pd; 1.596 + PRFileDesc *fd; 1.597 + int rv; 1.598 + char buf[128]; 1.599 + pid_t pid; 1.600 + int status; 1.601 +#ifdef _PR_SHARE_CLONES 1.602 + struct pr_CreateProcOp *op; 1.603 +#endif 1.604 + 1.605 +#ifdef _PR_SHARE_CLONES 1.606 + pr_InstallSigchldHandler(); 1.607 +#endif 1.608 + 1.609 + fd = PR_ImportFile(pr_wp.pipefd[0]); 1.610 + PR_ASSERT(NULL != fd); 1.611 + pd.fd = fd; 1.612 + pd.in_flags = PR_POLL_READ; 1.613 + 1.614 + while (1) { 1.615 + rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); 1.616 + PR_ASSERT(1 == rv); 1.617 + 1.618 +#ifdef _PR_SHARE_CLONES 1.619 + if (pr_waitpid_daemon_exit) { 1.620 + return; 1.621 + } 1.622 + PR_Lock(pr_wp.ml); 1.623 +#endif 1.624 + 1.625 + do { 1.626 + rv = read(pr_wp.pipefd[0], buf, sizeof(buf)); 1.627 + } while (sizeof(buf) == rv || (-1 == rv && EINTR == errno)); 1.628 + 1.629 +#ifdef _PR_SHARE_CLONES 1.630 + PR_Unlock(pr_wp.ml); 1.631 + while ((op = pr_wp.opHead) != NULL) { 1.632 + op->process = ForkAndExec(op->path, op->argv, 1.633 + op->envp, op->attr); 1.634 + if (NULL == op->process) { 1.635 + op->prerror = PR_GetError(); 1.636 + op->oserror = PR_GetOSError(); 1.637 + } 1.638 + PR_Lock(pr_wp.ml); 1.639 + pr_wp.opHead = op->next; 1.640 + if (NULL == pr_wp.opHead) { 1.641 + pr_wp.opTail = NULL; 1.642 + } 1.643 + op->done = PR_TRUE; 1.644 + PR_NotifyCondVar(op->doneCV); 1.645 + PR_Unlock(pr_wp.ml); 1.646 + } 1.647 +#endif 1.648 + 1.649 + while (1) { 1.650 + do { 1.651 + pid = waitpid((pid_t) -1, &status, WNOHANG); 1.652 + } while ((pid_t) -1 == pid && EINTR == errno); 1.653 + if (0 == pid) break; 1.654 + if ((pid_t) -1 == pid) { 1.655 + /* must be because we have no child processes */ 1.656 + PR_ASSERT(ECHILD == errno); 1.657 + break; 1.658 + } 1.659 + 1.660 + PR_Lock(pr_wp.ml); 1.661 + ProcessReapedChildInternal(pid, status); 1.662 + PR_Unlock(pr_wp.ml); 1.663 + } 1.664 + } 1.665 +} 1.666 + 1.667 +static void pr_SigchldHandler(int sig) 1.668 +{ 1.669 + int errnoCopy; 1.670 + int rv; 1.671 + 1.672 + errnoCopy = errno; 1.673 + 1.674 + do { 1.675 + rv = write(pr_wp.pipefd[1], "", 1); 1.676 + } while (-1 == rv && EINTR == errno); 1.677 + 1.678 +#ifdef DEBUG 1.679 + if (-1 == rv && EAGAIN != errno && EWOULDBLOCK != errno) { 1.680 + char *msg = "cannot write to pipe\n"; 1.681 + write(2, msg, strlen(msg) + 1); 1.682 + _exit(1); 1.683 + } 1.684 +#endif 1.685 + 1.686 + errno = errnoCopy; 1.687 +} 1.688 + 1.689 +static void pr_InstallSigchldHandler() 1.690 +{ 1.691 +#if defined(HPUX) && defined(_PR_DCETHREADS) 1.692 +#error "HP-UX DCE threads have their own SIGCHLD handler" 1.693 +#endif 1.694 + 1.695 + struct sigaction act, oact; 1.696 + int rv; 1.697 + 1.698 + act.sa_handler = pr_SigchldHandler; 1.699 + sigemptyset(&act.sa_mask); 1.700 + act.sa_flags = SA_NOCLDSTOP | SA_RESTART; 1.701 + rv = sigaction(SIGCHLD, &act, &oact); 1.702 + PR_ASSERT(0 == rv); 1.703 + /* Make sure we are not overriding someone else's SIGCHLD handler */ 1.704 +#ifndef _PR_SHARE_CLONES 1.705 + PR_ASSERT(oact.sa_handler == SIG_DFL); 1.706 +#endif 1.707 +} 1.708 + 1.709 +#endif /* !defined(_PR_NATIVE_THREADS) */ 1.710 + 1.711 +static PRStatus _MD_InitProcesses(void) 1.712 +{ 1.713 +#if !defined(_PR_NATIVE_THREADS) 1.714 + int rv; 1.715 + int flags; 1.716 +#endif 1.717 + 1.718 +#ifdef AIX 1.719 + { 1.720 + void *handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL); 1.721 + pr_wp.forkptr = (pid_t (*)(void)) dlsym(handle, "f_fork"); 1.722 + if (!pr_wp.forkptr) { 1.723 + pr_wp.forkptr = fork; 1.724 + } 1.725 + dlclose(handle); 1.726 + } 1.727 +#endif /* AIX */ 1.728 + 1.729 + pr_wp.ml = PR_NewLock(); 1.730 + PR_ASSERT(NULL != pr_wp.ml); 1.731 + 1.732 +#if defined(_PR_NATIVE_THREADS) 1.733 + pr_wp.numProcs = 0; 1.734 + pr_wp.cv = PR_NewCondVar(pr_wp.ml); 1.735 + PR_ASSERT(NULL != pr_wp.cv); 1.736 +#else 1.737 + rv = pipe(pr_wp.pipefd); 1.738 + PR_ASSERT(0 == rv); 1.739 + flags = fcntl(pr_wp.pipefd[0], F_GETFL, 0); 1.740 + fcntl(pr_wp.pipefd[0], F_SETFL, flags | O_NONBLOCK); 1.741 + flags = fcntl(pr_wp.pipefd[1], F_GETFL, 0); 1.742 + fcntl(pr_wp.pipefd[1], F_SETFL, flags | O_NONBLOCK); 1.743 + 1.744 +#ifndef _PR_SHARE_CLONES 1.745 + pr_InstallSigchldHandler(); 1.746 +#endif 1.747 +#endif /* !_PR_NATIVE_THREADS */ 1.748 + 1.749 + pr_wp.thread = PR_CreateThread(PR_SYSTEM_THREAD, 1.750 + WaitPidDaemonThread, NULL, PR_PRIORITY_NORMAL, 1.751 +#ifdef _PR_SHARE_CLONES 1.752 + PR_GLOBAL_THREAD, 1.753 +#else 1.754 + PR_LOCAL_THREAD, 1.755 +#endif 1.756 + PR_JOINABLE_THREAD, 0); 1.757 + PR_ASSERT(NULL != pr_wp.thread); 1.758 + 1.759 + pr_wp.pidTable = (pr_PidRecord**)PR_CALLOC(NBUCKETS * sizeof(pr_PidRecord *)); 1.760 + PR_ASSERT(NULL != pr_wp.pidTable); 1.761 + return PR_SUCCESS; 1.762 +} 1.763 + 1.764 +PRStatus _MD_DetachUnixProcess(PRProcess *process) 1.765 +{ 1.766 + PRStatus retVal = PR_SUCCESS; 1.767 + pr_PidRecord *pRec; 1.768 + 1.769 + PR_Lock(pr_wp.ml); 1.770 + pRec = FindPidTable(process->md.pid); 1.771 + if (NULL == pRec) { 1.772 + pRec = PR_NEW(pr_PidRecord); 1.773 + if (NULL == pRec) { 1.774 + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 1.775 + retVal = PR_FAILURE; 1.776 + goto done; 1.777 + } 1.778 + pRec->pid = process->md.pid; 1.779 + pRec->state = _PR_PID_DETACHED; 1.780 + pRec->reapedCV = NULL; 1.781 + InsertPidTable(pRec); 1.782 + } else { 1.783 + PR_ASSERT(_PR_PID_REAPED == pRec->state); 1.784 + if (_PR_PID_REAPED != pRec->state) { 1.785 + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); 1.786 + retVal = PR_FAILURE; 1.787 + } else { 1.788 + DeletePidTable(pRec); 1.789 + PR_ASSERT(NULL == pRec->reapedCV); 1.790 + PR_DELETE(pRec); 1.791 + } 1.792 + } 1.793 + PR_DELETE(process); 1.794 + 1.795 +done: 1.796 + PR_Unlock(pr_wp.ml); 1.797 + return retVal; 1.798 +} 1.799 + 1.800 +PRStatus _MD_WaitUnixProcess( 1.801 + PRProcess *process, 1.802 + PRInt32 *exitCode) 1.803 +{ 1.804 + pr_PidRecord *pRec; 1.805 + PRStatus retVal = PR_SUCCESS; 1.806 + PRBool interrupted = PR_FALSE; 1.807 + 1.808 + PR_Lock(pr_wp.ml); 1.809 + pRec = FindPidTable(process->md.pid); 1.810 + if (NULL == pRec) { 1.811 + pRec = PR_NEW(pr_PidRecord); 1.812 + if (NULL == pRec) { 1.813 + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); 1.814 + retVal = PR_FAILURE; 1.815 + goto done; 1.816 + } 1.817 + pRec->pid = process->md.pid; 1.818 + pRec->state = _PR_PID_WAITING; 1.819 + pRec->reapedCV = PR_NewCondVar(pr_wp.ml); 1.820 + if (NULL == pRec->reapedCV) { 1.821 + PR_DELETE(pRec); 1.822 + retVal = PR_FAILURE; 1.823 + goto done; 1.824 + } 1.825 + InsertPidTable(pRec); 1.826 + while (!interrupted && _PR_PID_REAPED != pRec->state) { 1.827 + if (PR_WaitCondVar(pRec->reapedCV, 1.828 + PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE 1.829 + && PR_GetError() == PR_PENDING_INTERRUPT_ERROR) { 1.830 + interrupted = PR_TRUE; 1.831 + } 1.832 + } 1.833 + if (_PR_PID_REAPED == pRec->state) { 1.834 + if (exitCode) { 1.835 + *exitCode = pRec->exitStatus; 1.836 + } 1.837 + } else { 1.838 + PR_ASSERT(interrupted); 1.839 + retVal = PR_FAILURE; 1.840 + } 1.841 + DeletePidTable(pRec); 1.842 + PR_DestroyCondVar(pRec->reapedCV); 1.843 + PR_DELETE(pRec); 1.844 + } else { 1.845 + PR_ASSERT(_PR_PID_REAPED == pRec->state); 1.846 + PR_ASSERT(NULL == pRec->reapedCV); 1.847 + DeletePidTable(pRec); 1.848 + if (exitCode) { 1.849 + *exitCode = pRec->exitStatus; 1.850 + } 1.851 + PR_DELETE(pRec); 1.852 + } 1.853 + PR_DELETE(process); 1.854 + 1.855 +done: 1.856 + PR_Unlock(pr_wp.ml); 1.857 + return retVal; 1.858 +} /* _MD_WaitUnixProcess */ 1.859 + 1.860 +PRStatus _MD_KillUnixProcess(PRProcess *process) 1.861 +{ 1.862 + PRErrorCode prerror; 1.863 + PRInt32 oserror; 1.864 + 1.865 +#ifdef SYMBIAN 1.866 + /* In Symbian OS, we can not kill other process with Open C */ 1.867 + PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, oserror); 1.868 + return PR_FAILURE; 1.869 +#else 1.870 + if (kill(process->md.pid, SIGKILL) == 0) { 1.871 + return PR_SUCCESS; 1.872 + } 1.873 + oserror = errno; 1.874 + switch (oserror) { 1.875 + case EPERM: 1.876 + prerror = PR_NO_ACCESS_RIGHTS_ERROR; 1.877 + break; 1.878 + case ESRCH: 1.879 + prerror = PR_INVALID_ARGUMENT_ERROR; 1.880 + break; 1.881 + default: 1.882 + prerror = PR_UNKNOWN_ERROR; 1.883 + break; 1.884 + } 1.885 + PR_SetError(prerror, oserror); 1.886 + return PR_FAILURE; 1.887 +#endif 1.888 +} /* _MD_KillUnixProcess */