blob: 364301c8d53b84fdc0d0c8183957d5e97447f05e [file] [log] [blame] [edit]
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Netscape Portable Runtime (NSPR).
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "primpl.h"
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#include <string.h>
#if defined(AIX)
#include <dlfcn.h> /* For dlopen, dlsym, dlclose */
#endif
#if defined(DARWIN)
#include <crt_externs.h>
#else
PR_IMPORT_DATA(char **) environ;
#endif
/*
* HP-UX 9 doesn't have the SA_RESTART flag.
*/
#ifndef SA_RESTART
#define SA_RESTART 0
#endif
/*
**********************************************************************
*
* The Unix process routines
*
**********************************************************************
*/
#define _PR_SIGNALED_EXITSTATUS 256
typedef enum pr_PidState {
_PR_PID_DETACHED,
_PR_PID_REAPED,
_PR_PID_WAITING
} pr_PidState;
typedef struct pr_PidRecord {
pid_t pid;
int exitStatus;
pr_PidState state;
PRCondVar *reapedCV;
struct pr_PidRecord *next;
} pr_PidRecord;
/*
* Irix sprocs and LinuxThreads are actually a kind of processes
* that can share the virtual address space and file descriptors.
*/
#if (defined(IRIX) && !defined(_PR_PTHREADS)) \
|| ((defined(LINUX) || defined(__GNU__) || defined(__GLIBC__)) \
&& defined(_PR_PTHREADS))
#define _PR_SHARE_CLONES
#endif
/*
* The macro _PR_NATIVE_THREADS indicates that we are
* using native threads only, so waitpid() blocks just the
* calling thread, not the process. In this case, the waitpid
* daemon thread can safely block in waitpid(). So we don't
* need to catch SIGCHLD, and the pipe to unblock PR_Poll() is
* also not necessary.
*/
#if defined(_PR_GLOBAL_THREADS_ONLY) \
|| (defined(_PR_PTHREADS) \
&& !defined(LINUX) && !defined(__GNU__) && !defined(__GLIBC__))
#define _PR_NATIVE_THREADS
#endif
/*
* All the static variables used by the Unix process routines are
* collected in this structure.
*/
static struct {
PRCallOnceType once;
PRThread *thread;
PRLock *ml;
#if defined(_PR_NATIVE_THREADS)
PRInt32 numProcs;
PRCondVar *cv;
#else
int pipefd[2];
#endif
pr_PidRecord **pidTable;
#ifdef _PR_SHARE_CLONES
struct pr_CreateProcOp *opHead, *opTail;
#endif
#ifdef AIX
pid_t (*forkptr)(void); /* Newer versions of AIX (starting in 4.3.2)
* have f_fork, which is faster than the
* regular fork in a multithreaded process
* because it skips calling the fork handlers.
* So we look up the f_fork symbol to see if
* it's available and fall back on fork.
*/
#endif /* AIX */
} pr_wp;
#ifdef _PR_SHARE_CLONES
static int pr_waitpid_daemon_exit;
void
_MD_unix_terminate_waitpid_daemon(void)
{
if (pr_wp.thread) {
pr_waitpid_daemon_exit = 1;
write(pr_wp.pipefd[1], "", 1);
PR_JoinThread(pr_wp.thread);
}
}
#endif
static PRStatus _MD_InitProcesses(void);
#if !defined(_PR_NATIVE_THREADS)
static void pr_InstallSigchldHandler(void);
#endif
static PRProcess *
ForkAndExec(
const char *path,
char *const *argv,
char *const *envp,
const PRProcessAttr *attr)
{
PRProcess *process;
int nEnv, idx;
char *const *childEnvp;
char **newEnvp = NULL;
int flags;
process = PR_NEW(PRProcess);
if (!process) {
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
return NULL;
}
childEnvp = envp;
if (attr && attr->fdInheritBuffer) {
PRBool found = PR_FALSE;
if (NULL == childEnvp) {
#ifdef DARWIN
childEnvp = *(_NSGetEnviron());
#else
childEnvp = environ;
#endif
}
for (nEnv = 0; childEnvp[nEnv]; nEnv++) {
}
newEnvp = (char **) PR_MALLOC((nEnv + 2) * sizeof(char *));
if (NULL == newEnvp) {
PR_DELETE(process);
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
return NULL;
}
for (idx = 0; idx < nEnv; idx++) {
newEnvp[idx] = childEnvp[idx];
if (!found && !strncmp(newEnvp[idx], "NSPR_INHERIT_FDS=", 17)) {
newEnvp[idx] = attr->fdInheritBuffer;
found = PR_TRUE;
}
}
if (!found) {
newEnvp[idx++] = attr->fdInheritBuffer;
}
newEnvp[idx] = NULL;
childEnvp = newEnvp;
}
#ifdef AIX
process->md.pid = (*pr_wp.forkptr)();
#elif defined(NTO) || defined(SYMBIAN)
/*
* fork() & exec() does not work in a multithreaded process.
* Use spawn() instead.
*/
{
int fd_map[3] = { 0, 1, 2 };
if (attr) {
if (attr->stdinFd && attr->stdinFd->secret->md.osfd != 0) {
fd_map[0] = dup(attr->stdinFd->secret->md.osfd);
flags = fcntl(fd_map[0], F_GETFL, 0);
if (flags & O_NONBLOCK)
fcntl(fd_map[0], F_SETFL, flags & ~O_NONBLOCK);
}
if (attr->stdoutFd && attr->stdoutFd->secret->md.osfd != 1) {
fd_map[1] = dup(attr->stdoutFd->secret->md.osfd);
flags = fcntl(fd_map[1], F_GETFL, 0);
if (flags & O_NONBLOCK)
fcntl(fd_map[1], F_SETFL, flags & ~O_NONBLOCK);
}
if (attr->stderrFd && attr->stderrFd->secret->md.osfd != 2) {
fd_map[2] = dup(attr->stderrFd->secret->md.osfd);
flags = fcntl(fd_map[2], F_GETFL, 0);
if (flags & O_NONBLOCK)
fcntl(fd_map[2], F_SETFL, flags & ~O_NONBLOCK);
}
PR_ASSERT(attr->currentDirectory == NULL); /* not implemented */
}
#ifdef SYMBIAN
/* In Symbian OS, we use posix_spawn instead of fork() and exec() */
posix_spawn(&(process->md.pid), path, NULL, NULL, argv, childEnvp);
#else
process->md.pid = spawn(path, 3, fd_map, NULL, argv, childEnvp);
#endif
if (fd_map[0] != 0)
close(fd_map[0]);
if (fd_map[1] != 1)
close(fd_map[1]);
if (fd_map[2] != 2)
close(fd_map[2]);
}
#else
process->md.pid = fork();
#endif
if ((pid_t) -1 == process->md.pid) {
PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, errno);
PR_DELETE(process);
if (newEnvp) {
PR_DELETE(newEnvp);
}
return NULL;
} else if (0 == process->md.pid) { /* the child process */
/*
* If the child process needs to exit, it must call _exit().
* Do not call exit(), because exit() will flush and close
* the standard I/O file descriptors, and hence corrupt
* the parent process's standard I/O data structures.
*/
#if !defined(NTO) && !defined(SYMBIAN)
if (attr) {
/* the osfd's to redirect stdin, stdout, and stderr to */
int in_osfd = -1, out_osfd = -1, err_osfd = -1;
if (attr->stdinFd
&& attr->stdinFd->secret->md.osfd != 0) {
in_osfd = attr->stdinFd->secret->md.osfd;
if (dup2(in_osfd, 0) != 0) {
_exit(1); /* failed */
}
flags = fcntl(0, F_GETFL, 0);
if (flags & O_NONBLOCK) {
fcntl(0, F_SETFL, flags & ~O_NONBLOCK);
}
}
if (attr->stdoutFd
&& attr->stdoutFd->secret->md.osfd != 1) {
out_osfd = attr->stdoutFd->secret->md.osfd;
if (dup2(out_osfd, 1) != 1) {
_exit(1); /* failed */
}
flags = fcntl(1, F_GETFL, 0);
if (flags & O_NONBLOCK) {
fcntl(1, F_SETFL, flags & ~O_NONBLOCK);
}
}
if (attr->stderrFd
&& attr->stderrFd->secret->md.osfd != 2) {
err_osfd = attr->stderrFd->secret->md.osfd;
if (dup2(err_osfd, 2) != 2) {
_exit(1); /* failed */
}
flags = fcntl(2, F_GETFL, 0);
if (flags & O_NONBLOCK) {
fcntl(2, F_SETFL, flags & ~O_NONBLOCK);
}
}
if (in_osfd != -1) {
close(in_osfd);
}
if (out_osfd != -1 && out_osfd != in_osfd) {
close(out_osfd);
}
if (err_osfd != -1 && err_osfd != in_osfd
&& err_osfd != out_osfd) {
close(err_osfd);
}
if (attr->currentDirectory) {
if (chdir(attr->currentDirectory) < 0) {
_exit(1); /* failed */
}
}
}
if (childEnvp) {
(void)execve(path, argv, childEnvp);
} else {
/* Inherit the environment of the parent. */
(void)execv(path, argv);
}
/* Whoops! It returned. That's a bad sign. */
_exit(1);
#endif /* !NTO */
}
if (newEnvp) {
PR_DELETE(newEnvp);
}
#if defined(_PR_NATIVE_THREADS)
PR_Lock(pr_wp.ml);
if (0 == pr_wp.numProcs++) {
PR_NotifyCondVar(pr_wp.cv);
}
PR_Unlock(pr_wp.ml);
#endif
return process;
}
#ifdef _PR_SHARE_CLONES
struct pr_CreateProcOp {
const char *path;
char *const *argv;
char *const *envp;
const PRProcessAttr *attr;
PRProcess *process;
PRErrorCode prerror;
PRInt32 oserror;
PRBool done;
PRCondVar *doneCV;
struct pr_CreateProcOp *next;
};
PRProcess *
_MD_CreateUnixProcess(
const char *path,
char *const *argv,
char *const *envp,
const PRProcessAttr *attr)
{
struct pr_CreateProcOp *op;
PRProcess *proc;
int rv;
if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) {
return NULL;
}
op = PR_NEW(struct pr_CreateProcOp);
if (NULL == op) {
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
return NULL;
}
op->path = path;
op->argv = argv;
op->envp = envp;
op->attr = attr;
op->done = PR_FALSE;
op->doneCV = PR_NewCondVar(pr_wp.ml);
if (NULL == op->doneCV) {
PR_DELETE(op);
return NULL;
}
PR_Lock(pr_wp.ml);
/* add to the tail of op queue */
op->next = NULL;
if (pr_wp.opTail) {
pr_wp.opTail->next = op;
pr_wp.opTail = op;
} else {
PR_ASSERT(NULL == pr_wp.opHead);
pr_wp.opHead = pr_wp.opTail = op;
}
/* wake up the daemon thread */
do {
rv = write(pr_wp.pipefd[1], "", 1);
} while (-1 == rv && EINTR == errno);
while (op->done == PR_FALSE) {
PR_WaitCondVar(op->doneCV, PR_INTERVAL_NO_TIMEOUT);
}
PR_Unlock(pr_wp.ml);
PR_DestroyCondVar(op->doneCV);
proc = op->process;
if (!proc) {
PR_SetError(op->prerror, op->oserror);
}
PR_DELETE(op);
return proc;
}
#else /* ! _PR_SHARE_CLONES */
PRProcess *
_MD_CreateUnixProcess(
const char *path,
char *const *argv,
char *const *envp,
const PRProcessAttr *attr)
{
if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) {
return NULL;
}
return ForkAndExec(path, argv, envp, attr);
} /* _MD_CreateUnixProcess */
#endif /* _PR_SHARE_CLONES */
/*
* The pid table is a hashtable.
*
* The number of buckets in the hashtable (NBUCKETS) must be a power of 2.
*/
#define NBUCKETS_LOG2 6
#define NBUCKETS (1 << NBUCKETS_LOG2)
#define PID_HASH_MASK ((pid_t) (NBUCKETS - 1))
static pr_PidRecord *
FindPidTable(pid_t pid)
{
pr_PidRecord *pRec;
int keyHash = (int) (pid & PID_HASH_MASK);
pRec = pr_wp.pidTable[keyHash];
while (pRec) {
if (pRec->pid == pid) {
break;
}
pRec = pRec->next;
}
return pRec;
}
static void
InsertPidTable(pr_PidRecord *pRec)
{
int keyHash = (int) (pRec->pid & PID_HASH_MASK);
pRec->next = pr_wp.pidTable[keyHash];
pr_wp.pidTable[keyHash] = pRec;
}
static void
DeletePidTable(pr_PidRecord *pRec)
{
int keyHash = (int) (pRec->pid & PID_HASH_MASK);
if (pr_wp.pidTable[keyHash] == pRec) {
pr_wp.pidTable[keyHash] = pRec->next;
} else {
pr_PidRecord *pred, *cur; /* predecessor and current */
pred = pr_wp.pidTable[keyHash];
cur = pred->next;
while (cur) {
if (cur == pRec) {
pred->next = cur->next;
break;
}
pred = cur;
cur = cur->next;
}
PR_ASSERT(cur != NULL);
}
}
static int
ExtractExitStatus(int rawExitStatus)
{
/*
* We did not specify the WCONTINUED and WUNTRACED options
* for waitpid, so these two events should not be reported.
*/
PR_ASSERT(!WIFSTOPPED(rawExitStatus));
#ifdef WIFCONTINUED
PR_ASSERT(!WIFCONTINUED(rawExitStatus));
#endif
if (WIFEXITED(rawExitStatus)) {
return WEXITSTATUS(rawExitStatus);
} else {
PR_ASSERT(WIFSIGNALED(rawExitStatus));
return _PR_SIGNALED_EXITSTATUS;
}
}
static void
ProcessReapedChildInternal(pid_t pid, int status)
{
pr_PidRecord *pRec;
pRec = FindPidTable(pid);
if (NULL == pRec) {
pRec = PR_NEW(pr_PidRecord);
pRec->pid = pid;
pRec->state = _PR_PID_REAPED;
pRec->exitStatus = ExtractExitStatus(status);
pRec->reapedCV = NULL;
InsertPidTable(pRec);
} else {
PR_ASSERT(pRec->state != _PR_PID_REAPED);
if (_PR_PID_DETACHED == pRec->state) {
PR_ASSERT(NULL == pRec->reapedCV);
DeletePidTable(pRec);
PR_DELETE(pRec);
} else {
PR_ASSERT(_PR_PID_WAITING == pRec->state);
PR_ASSERT(NULL != pRec->reapedCV);
pRec->exitStatus = ExtractExitStatus(status);
pRec->state = _PR_PID_REAPED;
PR_NotifyCondVar(pRec->reapedCV);
}
}
}
#if defined(_PR_NATIVE_THREADS)
/*
* If all the threads are native threads, the daemon thread is
* simpler. We don't need to catch the SIGCHLD signal. We can
* just have the daemon thread block in waitpid().
*/
static void WaitPidDaemonThread(void *unused)
{
pid_t pid;
int status;
while (1) {
PR_Lock(pr_wp.ml);
while (0 == pr_wp.numProcs) {
PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT);
}
PR_Unlock(pr_wp.ml);
while (1) {
do {
pid = waitpid((pid_t) -1, &status, 0);
} while ((pid_t) -1 == pid && EINTR == errno);
/*
* waitpid() cannot return 0 because we did not invoke it
* with the WNOHANG option.
*/
PR_ASSERT(0 != pid);
/*
* The only possible error code is ECHILD. But if we do
* our accounting correctly, we should only call waitpid()
* when there is a child process to wait for.
*/
PR_ASSERT((pid_t) -1 != pid);
if ((pid_t) -1 == pid) {
break;
}
PR_Lock(pr_wp.ml);
ProcessReapedChildInternal(pid, status);
pr_wp.numProcs--;
while (0 == pr_wp.numProcs) {
PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT);
}
PR_Unlock(pr_wp.ml);
}
}
}
#else /* _PR_NATIVE_THREADS */
static void WaitPidDaemonThread(void *unused)
{
PRPollDesc pd;
PRFileDesc *fd;
int rv;
char buf[128];
pid_t pid;
int status;
#ifdef _PR_SHARE_CLONES
struct pr_CreateProcOp *op;
#endif
#ifdef _PR_SHARE_CLONES
pr_InstallSigchldHandler();
#endif
fd = PR_ImportFile(pr_wp.pipefd[0]);
PR_ASSERT(NULL != fd);
pd.fd = fd;
pd.in_flags = PR_POLL_READ;
while (1) {
rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT);
PR_ASSERT(1 == rv);
#ifdef _PR_SHARE_CLONES
if (pr_waitpid_daemon_exit) {
return;
}
PR_Lock(pr_wp.ml);
#endif
do {
rv = read(pr_wp.pipefd[0], buf, sizeof(buf));
} while (sizeof(buf) == rv || (-1 == rv && EINTR == errno));
#ifdef _PR_SHARE_CLONES
PR_Unlock(pr_wp.ml);
while ((op = pr_wp.opHead) != NULL) {
op->process = ForkAndExec(op->path, op->argv,
op->envp, op->attr);
if (NULL == op->process) {
op->prerror = PR_GetError();
op->oserror = PR_GetOSError();
}
PR_Lock(pr_wp.ml);
pr_wp.opHead = op->next;
if (NULL == pr_wp.opHead) {
pr_wp.opTail = NULL;
}
op->done = PR_TRUE;
PR_NotifyCondVar(op->doneCV);
PR_Unlock(pr_wp.ml);
}
#endif
while (1) {
do {
pid = waitpid((pid_t) -1, &status, WNOHANG);
} while ((pid_t) -1 == pid && EINTR == errno);
if (0 == pid) break;
if ((pid_t) -1 == pid) {
/* must be because we have no child processes */
PR_ASSERT(ECHILD == errno);
break;
}
PR_Lock(pr_wp.ml);
ProcessReapedChildInternal(pid, status);
PR_Unlock(pr_wp.ml);
}
}
}
static void pr_SigchldHandler(int sig)
{
int errnoCopy;
int rv;
errnoCopy = errno;
do {
rv = write(pr_wp.pipefd[1], "", 1);
} while (-1 == rv && EINTR == errno);
#ifdef DEBUG
if (-1 == rv && EAGAIN != errno && EWOULDBLOCK != errno) {
char *msg = "cannot write to pipe\n";
write(2, msg, strlen(msg) + 1);
_exit(1);
}
#endif
errno = errnoCopy;
}
static void pr_InstallSigchldHandler()
{
#if defined(HPUX) && defined(_PR_DCETHREADS)
#error "HP-UX DCE threads have their own SIGCHLD handler"
#endif
struct sigaction act, oact;
int rv;
act.sa_handler = pr_SigchldHandler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
rv = sigaction(SIGCHLD, &act, &oact);
PR_ASSERT(0 == rv);
/* Make sure we are not overriding someone else's SIGCHLD handler */
#ifndef _PR_SHARE_CLONES
PR_ASSERT(oact.sa_handler == SIG_DFL);
#endif
}
#endif /* !defined(_PR_NATIVE_THREADS) */
static PRStatus _MD_InitProcesses(void)
{
#if !defined(_PR_NATIVE_THREADS)
int rv;
int flags;
#endif
#ifdef SUNOS4
#define _PR_NBIO_FLAG FNDELAY
#else
#define _PR_NBIO_FLAG O_NONBLOCK
#endif
#ifdef AIX
{
void *handle = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
pr_wp.forkptr = (pid_t (*)(void)) dlsym(handle, "f_fork");
if (!pr_wp.forkptr) {
pr_wp.forkptr = fork;
}
dlclose(handle);
}
#endif /* AIX */
pr_wp.ml = PR_NewLock();
PR_ASSERT(NULL != pr_wp.ml);
#if defined(_PR_NATIVE_THREADS)
pr_wp.numProcs = 0;
pr_wp.cv = PR_NewCondVar(pr_wp.ml);
PR_ASSERT(NULL != pr_wp.cv);
#else
rv = pipe(pr_wp.pipefd);
PR_ASSERT(0 == rv);
flags = fcntl(pr_wp.pipefd[0], F_GETFL, 0);
fcntl(pr_wp.pipefd[0], F_SETFL, flags | _PR_NBIO_FLAG);
flags = fcntl(pr_wp.pipefd[1], F_GETFL, 0);
fcntl(pr_wp.pipefd[1], F_SETFL, flags | _PR_NBIO_FLAG);
#ifndef _PR_SHARE_CLONES
pr_InstallSigchldHandler();
#endif
#endif /* !_PR_NATIVE_THREADS */
pr_wp.thread = PR_CreateThread(PR_SYSTEM_THREAD,
WaitPidDaemonThread, NULL, PR_PRIORITY_NORMAL,
#ifdef _PR_SHARE_CLONES
PR_GLOBAL_THREAD,
#else
PR_LOCAL_THREAD,
#endif
PR_JOINABLE_THREAD, 0);
PR_ASSERT(NULL != pr_wp.thread);
pr_wp.pidTable = (pr_PidRecord**)PR_CALLOC(NBUCKETS * sizeof(pr_PidRecord *));
PR_ASSERT(NULL != pr_wp.pidTable);
return PR_SUCCESS;
}
PRStatus _MD_DetachUnixProcess(PRProcess *process)
{
PRStatus retVal = PR_SUCCESS;
pr_PidRecord *pRec;
PR_Lock(pr_wp.ml);
pRec = FindPidTable(process->md.pid);
if (NULL == pRec) {
pRec = PR_NEW(pr_PidRecord);
if (NULL == pRec) {
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
retVal = PR_FAILURE;
goto done;
}
pRec->pid = process->md.pid;
pRec->state = _PR_PID_DETACHED;
pRec->reapedCV = NULL;
InsertPidTable(pRec);
} else {
PR_ASSERT(_PR_PID_REAPED == pRec->state);
if (_PR_PID_REAPED != pRec->state) {
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
retVal = PR_FAILURE;
} else {
DeletePidTable(pRec);
PR_ASSERT(NULL == pRec->reapedCV);
PR_DELETE(pRec);
}
}
PR_DELETE(process);
done:
PR_Unlock(pr_wp.ml);
return retVal;
}
PRStatus _MD_WaitUnixProcess(
PRProcess *process,
PRInt32 *exitCode)
{
pr_PidRecord *pRec;
PRStatus retVal = PR_SUCCESS;
PRBool interrupted = PR_FALSE;
PR_Lock(pr_wp.ml);
pRec = FindPidTable(process->md.pid);
if (NULL == pRec) {
pRec = PR_NEW(pr_PidRecord);
if (NULL == pRec) {
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
retVal = PR_FAILURE;
goto done;
}
pRec->pid = process->md.pid;
pRec->state = _PR_PID_WAITING;
pRec->reapedCV = PR_NewCondVar(pr_wp.ml);
if (NULL == pRec->reapedCV) {
PR_DELETE(pRec);
retVal = PR_FAILURE;
goto done;
}
InsertPidTable(pRec);
while (!interrupted && _PR_PID_REAPED != pRec->state) {
if (PR_WaitCondVar(pRec->reapedCV,
PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE
&& PR_GetError() == PR_PENDING_INTERRUPT_ERROR) {
interrupted = PR_TRUE;
}
}
if (_PR_PID_REAPED == pRec->state) {
if (exitCode) {
*exitCode = pRec->exitStatus;
}
} else {
PR_ASSERT(interrupted);
retVal = PR_FAILURE;
}
DeletePidTable(pRec);
PR_DestroyCondVar(pRec->reapedCV);
PR_DELETE(pRec);
} else {
PR_ASSERT(_PR_PID_REAPED == pRec->state);
PR_ASSERT(NULL == pRec->reapedCV);
DeletePidTable(pRec);
if (exitCode) {
*exitCode = pRec->exitStatus;
}
PR_DELETE(pRec);
}
PR_DELETE(process);
done:
PR_Unlock(pr_wp.ml);
return retVal;
} /* _MD_WaitUnixProcess */
PRStatus _MD_KillUnixProcess(PRProcess *process)
{
PRErrorCode prerror;
PRInt32 oserror;
#ifdef SYMBIAN
/* In Symbian OS, we can not kill other process with Open C */
PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, oserror);
return PR_FAILURE;
#else
if (kill(process->md.pid, SIGKILL) == 0) {
return PR_SUCCESS;
}
oserror = errno;
switch (oserror) {
case EPERM:
prerror = PR_NO_ACCESS_RIGHTS_ERROR;
break;
case ESRCH:
prerror = PR_INVALID_ARGUMENT_ERROR;
break;
default:
prerror = PR_UNKNOWN_ERROR;
break;
}
PR_SetError(prerror, oserror);
return PR_FAILURE;
#endif
} /* _MD_KillUnixProcess */