|  | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ | 
|  | /* This Source Code Form is subject to the terms of the Mozilla Public | 
|  | * License, v. 2.0. If a copy of the MPL was not distributed with this | 
|  | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | 
|  |  | 
|  | #include "primpl.h" | 
|  | #include <ctype.h> | 
|  | #include <string.h> | 
|  |  | 
|  | PRLogModuleInfo *_pr_clock_lm; | 
|  | PRLogModuleInfo *_pr_cmon_lm; | 
|  | PRLogModuleInfo *_pr_io_lm; | 
|  | PRLogModuleInfo *_pr_cvar_lm; | 
|  | PRLogModuleInfo *_pr_mon_lm; | 
|  | PRLogModuleInfo *_pr_linker_lm; | 
|  | PRLogModuleInfo *_pr_sched_lm; | 
|  | PRLogModuleInfo *_pr_thread_lm; | 
|  | PRLogModuleInfo *_pr_gc_lm; | 
|  | PRLogModuleInfo *_pr_shm_lm; | 
|  | PRLogModuleInfo *_pr_shma_lm; | 
|  |  | 
|  | PRFileDesc *_pr_stdin; | 
|  | PRFileDesc *_pr_stdout; | 
|  | PRFileDesc *_pr_stderr; | 
|  |  | 
|  | #if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS) | 
|  |  | 
|  | PRCList _pr_active_local_threadQ = | 
|  | PR_INIT_STATIC_CLIST(&_pr_active_local_threadQ); | 
|  | PRCList _pr_active_global_threadQ = | 
|  | PR_INIT_STATIC_CLIST(&_pr_active_global_threadQ); | 
|  |  | 
|  | _MDLock  _pr_cpuLock;           /* lock for the CPU Q */ | 
|  | PRCList _pr_cpuQ = PR_INIT_STATIC_CLIST(&_pr_cpuQ); | 
|  |  | 
|  | PRUint32 _pr_utid; | 
|  |  | 
|  | PRInt32 _pr_userActive; | 
|  | PRInt32 _pr_systemActive; | 
|  | PRUintn _pr_maxPTDs; | 
|  |  | 
|  | #ifdef _PR_LOCAL_THREADS_ONLY | 
|  |  | 
|  | struct _PRCPU   *_pr_currentCPU; | 
|  | PRThread    *_pr_currentThread; | 
|  | PRThread    *_pr_lastThread; | 
|  | PRInt32     _pr_intsOff; | 
|  |  | 
|  | #endif /* _PR_LOCAL_THREADS_ONLY */ | 
|  |  | 
|  | /* Lock protecting all "termination" condition variables of all threads */ | 
|  | PRLock *_pr_terminationCVLock; | 
|  |  | 
|  | #endif /* !defined(_PR_PTHREADS) */ | 
|  |  | 
|  | PRLock *_pr_sleeplock;  /* used in PR_Sleep(), classic and pthreads */ | 
|  |  | 
|  | static void _PR_InitCallOnce(void); | 
|  |  | 
|  | PRBool _pr_initialized = PR_FALSE; | 
|  |  | 
|  |  | 
|  | PR_IMPLEMENT(PRBool) PR_VersionCheck(const char *importedVersion) | 
|  | { | 
|  | /* | 
|  | ** This is the secret handshake algorithm. | 
|  | ** | 
|  | ** This release has a simple version compatibility | 
|  | ** check algorithm.  This release is not backward | 
|  | ** compatible with previous major releases.  It is | 
|  | ** not compatible with future major, minor, or | 
|  | ** patch releases. | 
|  | */ | 
|  | int vmajor = 0, vminor = 0, vpatch = 0; | 
|  | const char *ptr = importedVersion; | 
|  |  | 
|  | while (isdigit(*ptr)) { | 
|  | vmajor = 10 * vmajor + *ptr - '0'; | 
|  | ptr++; | 
|  | } | 
|  | if (*ptr == '.') { | 
|  | ptr++; | 
|  | while (isdigit(*ptr)) { | 
|  | vminor = 10 * vminor + *ptr - '0'; | 
|  | ptr++; | 
|  | } | 
|  | if (*ptr == '.') { | 
|  | ptr++; | 
|  | while (isdigit(*ptr)) { | 
|  | vpatch = 10 * vpatch + *ptr - '0'; | 
|  | ptr++; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (vmajor != PR_VMAJOR) { | 
|  | return PR_FALSE; | 
|  | } | 
|  | if (vmajor == PR_VMAJOR && vminor > PR_VMINOR) { | 
|  | return PR_FALSE; | 
|  | } | 
|  | if (vmajor == PR_VMAJOR && vminor == PR_VMINOR && vpatch > PR_VPATCH) { | 
|  | return PR_FALSE; | 
|  | } | 
|  | return PR_TRUE; | 
|  | }  /* PR_VersionCheck */ | 
|  |  | 
|  | PR_IMPLEMENT(const char*) PR_GetVersion(void) | 
|  | { | 
|  | return PR_VERSION; | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(PRBool) PR_Initialized(void) | 
|  | { | 
|  | return _pr_initialized; | 
|  | } | 
|  |  | 
|  | PRInt32 _native_threads_only = 0; | 
|  |  | 
|  | #ifdef WINNT | 
|  | static void _pr_SetNativeThreadsOnlyMode(void) | 
|  | { | 
|  | HMODULE mainExe; | 
|  | PRBool *globalp; | 
|  | char *envp; | 
|  |  | 
|  | mainExe = GetModuleHandle(NULL); | 
|  | PR_ASSERT(NULL != mainExe); | 
|  | globalp = (PRBool *) GetProcAddress(mainExe, "nspr_native_threads_only"); | 
|  | if (globalp) { | 
|  | _native_threads_only = (*globalp != PR_FALSE); | 
|  | } else if (envp = getenv("NSPR_NATIVE_THREADS_ONLY")) { | 
|  | _native_threads_only = (atoi(envp) == 1); | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static void _PR_InitStuff(void) | 
|  | { | 
|  |  | 
|  | if (_pr_initialized) { | 
|  | return; | 
|  | } | 
|  | _pr_initialized = PR_TRUE; | 
|  | #ifdef _PR_ZONE_ALLOCATOR | 
|  | _PR_InitZones(); | 
|  | #endif | 
|  | #ifdef WINNT | 
|  | _pr_SetNativeThreadsOnlyMode(); | 
|  | #endif | 
|  |  | 
|  |  | 
|  | (void) PR_GetPageSize(); | 
|  |  | 
|  | _pr_clock_lm = PR_NewLogModule("clock"); | 
|  | _pr_cmon_lm = PR_NewLogModule("cmon"); | 
|  | _pr_io_lm = PR_NewLogModule("io"); | 
|  | _pr_mon_lm = PR_NewLogModule("mon"); | 
|  | _pr_linker_lm = PR_NewLogModule("linker"); | 
|  | _pr_cvar_lm = PR_NewLogModule("cvar"); | 
|  | _pr_sched_lm = PR_NewLogModule("sched"); | 
|  | _pr_thread_lm = PR_NewLogModule("thread"); | 
|  | _pr_gc_lm = PR_NewLogModule("gc"); | 
|  | _pr_shm_lm = PR_NewLogModule("shm"); | 
|  | _pr_shma_lm = PR_NewLogModule("shma"); | 
|  |  | 
|  | /* NOTE: These init's cannot depend on _PR_MD_CURRENT_THREAD() */ | 
|  | _PR_MD_EARLY_INIT(); | 
|  |  | 
|  | _PR_InitLocks(); | 
|  | _PR_InitAtomic(); | 
|  | _PR_InitSegs(); | 
|  | _PR_InitStacks(); | 
|  | _PR_InitTPD(); | 
|  | _PR_InitEnv(); | 
|  | _PR_InitLayerCache(); | 
|  | _PR_InitClock(); | 
|  |  | 
|  | _pr_sleeplock = PR_NewLock(); | 
|  | PR_ASSERT(NULL != _pr_sleeplock); | 
|  |  | 
|  | _PR_InitThreads(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); | 
|  |  | 
|  | #ifdef WIN16 | 
|  | { | 
|  | PRInt32 top;   /* artificial top of stack, win16 */ | 
|  | _pr_top_of_task_stack = (char *) ⊤ | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #ifndef _PR_GLOBAL_THREADS_ONLY | 
|  | _PR_InitCPUs(); | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * XXX: call _PR_InitMem only on those platforms for which nspr implements | 
|  | *  malloc, for now. | 
|  | */ | 
|  | #ifdef _PR_OVERRIDE_MALLOC | 
|  | _PR_InitMem(); | 
|  | #endif | 
|  |  | 
|  | _PR_InitCMon(); | 
|  | _PR_InitIO(); | 
|  | _PR_InitNet(); | 
|  | _PR_InitTime(); | 
|  | _PR_InitLog(); | 
|  | _PR_InitLinker(); | 
|  | _PR_InitCallOnce(); | 
|  | _PR_InitDtoa(); | 
|  | _PR_InitMW(); | 
|  | _PR_InitRWLocks(); | 
|  |  | 
|  | nspr_InitializePRErrorTable(); | 
|  |  | 
|  | _PR_MD_FINAL_INIT(); | 
|  | } | 
|  |  | 
|  | void _PR_ImplicitInitialization(void) | 
|  | { | 
|  | _PR_InitStuff(); | 
|  |  | 
|  | /* Enable interrupts */ | 
|  | #if !defined(_PR_PTHREADS) && !defined(_PR_GLOBAL_THREADS_ONLY) | 
|  | _PR_MD_START_INTERRUPTS(); | 
|  | #endif | 
|  |  | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(void) PR_DisableClockInterrupts(void) | 
|  | { | 
|  | #if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS) | 
|  | if (!_pr_initialized) { | 
|  | _PR_InitStuff(); | 
|  | } else { | 
|  | _PR_MD_DISABLE_CLOCK_INTERRUPTS(); | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(void) PR_EnableClockInterrupts(void) | 
|  | { | 
|  | #if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS) | 
|  | if (!_pr_initialized) { | 
|  | _PR_InitStuff(); | 
|  | } | 
|  | _PR_MD_ENABLE_CLOCK_INTERRUPTS(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(void) PR_BlockClockInterrupts(void) | 
|  | { | 
|  | #if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS) | 
|  | _PR_MD_BLOCK_CLOCK_INTERRUPTS(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(void) PR_UnblockClockInterrupts(void) | 
|  | { | 
|  | #if !defined(_PR_PTHREADS) && !defined(_PR_BTHREADS) | 
|  | _PR_MD_UNBLOCK_CLOCK_INTERRUPTS(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(void) PR_Init( | 
|  | PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs) | 
|  | { | 
|  | _PR_ImplicitInitialization(); | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(PRIntn) PR_Initialize( | 
|  | PRPrimordialFn prmain, PRIntn argc, char **argv, PRUintn maxPTDs) | 
|  | { | 
|  | PRIntn rv; | 
|  | _PR_ImplicitInitialization(); | 
|  | rv = prmain(argc, argv); | 
|  | PR_Cleanup(); | 
|  | return rv; | 
|  | }  /* PR_Initialize */ | 
|  |  | 
|  | /* | 
|  | *----------------------------------------------------------------------- | 
|  | * | 
|  | * _PR_CleanupBeforeExit -- | 
|  | * | 
|  | *   Perform the cleanup work before exiting the process. | 
|  | *   We first do the cleanup generic to all platforms.  Then | 
|  | *   we call _PR_MD_CLEANUP_BEFORE_EXIT(), where platform-dependent | 
|  | *   cleanup is done.  This function is used by PR_Cleanup(). | 
|  | * | 
|  | * See also: PR_Cleanup(). | 
|  | * | 
|  | *----------------------------------------------------------------------- | 
|  | */ | 
|  | #if defined(_PR_PTHREADS) || defined(_PR_BTHREADS) | 
|  | /* see ptthread.c */ | 
|  | #else | 
|  | static void | 
|  | _PR_CleanupBeforeExit(void) | 
|  | { | 
|  | /* | 
|  | Do not make any calls here other than to destroy resources.  For example, | 
|  | do not make any calls that eventually may end up in PR_Lock.  Because the | 
|  | thread is destroyed, can not access current thread any more. | 
|  | */ | 
|  | _PR_CleanupTPD(); | 
|  | if (_pr_terminationCVLock) | 
|  | /* | 
|  | * In light of the comment above, this looks real suspicious. | 
|  | * I'd go so far as to say it's just a problem waiting to happen. | 
|  | */ | 
|  | { | 
|  | PR_DestroyLock(_pr_terminationCVLock); | 
|  | } | 
|  |  | 
|  | _PR_MD_CLEANUP_BEFORE_EXIT(); | 
|  | } | 
|  | #endif /* defined(_PR_PTHREADS) */ | 
|  |  | 
|  | /* | 
|  | *---------------------------------------------------------------------- | 
|  | * | 
|  | * PR_Cleanup -- | 
|  | * | 
|  | *   Perform a graceful shutdown of the NSPR runtime.  PR_Cleanup() may | 
|  | *   only be called from the primordial thread, typically at the | 
|  | *   end of the main() function.  It returns when it has completed | 
|  | *   its platform-dependent duty and the process must not make any other | 
|  | *   NSPR library calls prior to exiting from main(). | 
|  | * | 
|  | *   PR_Cleanup() first blocks the primordial thread until all the | 
|  | *   other user (non-system) threads, if any, have terminated. | 
|  | *   Then it performs cleanup in preparation for exiting the process. | 
|  | *   PR_Cleanup() does not exit the primordial thread (which would | 
|  | *   in turn exit the process). | 
|  | * | 
|  | *   PR_Cleanup() only responds when it is called by the primordial | 
|  | *   thread. Calls by any other thread are silently ignored. | 
|  | * | 
|  | * See also: PR_ExitProcess() | 
|  | * | 
|  | *---------------------------------------------------------------------- | 
|  | */ | 
|  | #if defined(_PR_PTHREADS) || defined(_PR_BTHREADS) | 
|  | /* see ptthread.c */ | 
|  | #else | 
|  |  | 
|  | PR_IMPLEMENT(PRStatus) PR_Cleanup() | 
|  | { | 
|  | PRThread *me = PR_GetCurrentThread(); | 
|  | PR_ASSERT((NULL != me) && (me->flags & _PR_PRIMORDIAL)); | 
|  | if ((NULL != me) && (me->flags & _PR_PRIMORDIAL)) | 
|  | { | 
|  | PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR")); | 
|  |  | 
|  | /* | 
|  | * No more recycling of threads | 
|  | */ | 
|  | _pr_recycleThreads = 0; | 
|  |  | 
|  | /* | 
|  | * Wait for all other user (non-system/daemon) threads | 
|  | * to terminate. | 
|  | */ | 
|  | PR_Lock(_pr_activeLock); | 
|  | while (_pr_userActive > _pr_primordialExitCount) { | 
|  | PR_WaitCondVar(_pr_primordialExitCVar, PR_INTERVAL_NO_TIMEOUT); | 
|  | } | 
|  | if (me->flags & _PR_SYSTEM) { | 
|  | _pr_systemActive--; | 
|  | } else { | 
|  | _pr_userActive--; | 
|  | } | 
|  | PR_Unlock(_pr_activeLock); | 
|  |  | 
|  | _PR_MD_EARLY_CLEANUP(); | 
|  |  | 
|  | _PR_CleanupMW(); | 
|  | _PR_CleanupTime(); | 
|  | _PR_CleanupDtoa(); | 
|  | _PR_CleanupCallOnce(); | 
|  | _PR_ShutdownLinker(); | 
|  | _PR_CleanupNet(); | 
|  | _PR_CleanupIO(); | 
|  | /* Release the primordial thread's private data, etc. */ | 
|  | _PR_CleanupThread(me); | 
|  |  | 
|  | _PR_MD_STOP_INTERRUPTS(); | 
|  |  | 
|  | PR_LOG(_pr_thread_lm, PR_LOG_MIN, | 
|  | ("PR_Cleanup: clean up before destroying thread")); | 
|  | _PR_LogCleanup(); | 
|  |  | 
|  | /* | 
|  | * This part should look like the end of _PR_NativeRunThread | 
|  | * and _PR_UserRunThread. | 
|  | */ | 
|  | if (_PR_IS_NATIVE_THREAD(me)) { | 
|  | _PR_MD_EXIT_THREAD(me); | 
|  | _PR_NativeDestroyThread(me); | 
|  | } else { | 
|  | _PR_UserDestroyThread(me); | 
|  | PR_DELETE(me->stack); | 
|  | PR_DELETE(me); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * XXX: We are freeing the heap memory here so that Purify won't | 
|  | * complain, but we should also free other kinds of resources | 
|  | * that are allocated by the _PR_InitXXX() functions. | 
|  | * Ideally, for each _PR_InitXXX(), there should be a corresponding | 
|  | * _PR_XXXCleanup() that we can call here. | 
|  | */ | 
|  | #ifdef WINNT | 
|  | _PR_CleanupCPUs(); | 
|  | #endif | 
|  | _PR_CleanupThreads(); | 
|  | _PR_CleanupCMon(); | 
|  | PR_DestroyLock(_pr_sleeplock); | 
|  | _pr_sleeplock = NULL; | 
|  | _PR_CleanupLayerCache(); | 
|  | _PR_CleanupEnv(); | 
|  | _PR_CleanupStacks(); | 
|  | _PR_CleanupBeforeExit(); | 
|  | _pr_initialized = PR_FALSE; | 
|  | return PR_SUCCESS; | 
|  | } | 
|  | return PR_FAILURE; | 
|  | } | 
|  | #endif /* defined(_PR_PTHREADS) */ | 
|  |  | 
|  | /* | 
|  | *------------------------------------------------------------------------ | 
|  | * PR_ProcessExit -- | 
|  | * | 
|  | *   Cause an immediate, nongraceful, forced termination of the process. | 
|  | *   It takes a PRIntn argument, which is the exit status code of the | 
|  | *   process. | 
|  | * | 
|  | * See also: PR_Cleanup() | 
|  | * | 
|  | *------------------------------------------------------------------------ | 
|  | */ | 
|  |  | 
|  | #if defined(_PR_PTHREADS) || defined(_PR_BTHREADS) | 
|  | /* see ptthread.c */ | 
|  | #else | 
|  | PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status) | 
|  | { | 
|  | _PR_MD_EXIT(status); | 
|  | } | 
|  |  | 
|  | #endif /* defined(_PR_PTHREADS) */ | 
|  |  | 
|  | PR_IMPLEMENT(PRProcessAttr *) | 
|  | PR_NewProcessAttr(void) | 
|  | { | 
|  | PRProcessAttr *attr; | 
|  |  | 
|  | attr = PR_NEWZAP(PRProcessAttr); | 
|  | if (!attr) { | 
|  | PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); | 
|  | } | 
|  | return attr; | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(void) | 
|  | PR_ResetProcessAttr(PRProcessAttr *attr) | 
|  | { | 
|  | PR_FREEIF(attr->currentDirectory); | 
|  | PR_FREEIF(attr->fdInheritBuffer); | 
|  | memset(attr, 0, sizeof(*attr)); | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(void) | 
|  | PR_DestroyProcessAttr(PRProcessAttr *attr) | 
|  | { | 
|  | PR_FREEIF(attr->currentDirectory); | 
|  | PR_FREEIF(attr->fdInheritBuffer); | 
|  | PR_DELETE(attr); | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(void) | 
|  | PR_ProcessAttrSetStdioRedirect( | 
|  | PRProcessAttr *attr, | 
|  | PRSpecialFD stdioFd, | 
|  | PRFileDesc *redirectFd) | 
|  | { | 
|  | switch (stdioFd) { | 
|  | case PR_StandardInput: | 
|  | attr->stdinFd = redirectFd; | 
|  | break; | 
|  | case PR_StandardOutput: | 
|  | attr->stdoutFd = redirectFd; | 
|  | break; | 
|  | case PR_StandardError: | 
|  | attr->stderrFd = redirectFd; | 
|  | break; | 
|  | default: | 
|  | PR_ASSERT(0); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * OBSOLETE | 
|  | */ | 
|  | PR_IMPLEMENT(void) | 
|  | PR_SetStdioRedirect( | 
|  | PRProcessAttr *attr, | 
|  | PRSpecialFD stdioFd, | 
|  | PRFileDesc *redirectFd) | 
|  | { | 
|  | #if defined(DEBUG) | 
|  | static PRBool warn = PR_TRUE; | 
|  | if (warn) { | 
|  | warn = _PR_Obsolete("PR_SetStdioRedirect()", | 
|  | "PR_ProcessAttrSetStdioRedirect()"); | 
|  | } | 
|  | #endif | 
|  | PR_ProcessAttrSetStdioRedirect(attr, stdioFd, redirectFd); | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(PRStatus) | 
|  | PR_ProcessAttrSetCurrentDirectory( | 
|  | PRProcessAttr *attr, | 
|  | const char *dir) | 
|  | { | 
|  | PR_FREEIF(attr->currentDirectory); | 
|  | attr->currentDirectory = (char *) PR_MALLOC(strlen(dir) + 1); | 
|  | if (!attr->currentDirectory) { | 
|  | PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); | 
|  | return PR_FAILURE; | 
|  | } | 
|  | strcpy(attr->currentDirectory, dir); | 
|  | return PR_SUCCESS; | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(PRStatus) | 
|  | PR_ProcessAttrSetInheritableFD( | 
|  | PRProcessAttr *attr, | 
|  | PRFileDesc *fd, | 
|  | const char *name) | 
|  | { | 
|  | /* We malloc the fd inherit buffer in multiples of this number. */ | 
|  | #define FD_INHERIT_BUFFER_INCR 128 | 
|  | /* The length of "NSPR_INHERIT_FDS=" */ | 
|  | #define NSPR_INHERIT_FDS_STRLEN 17 | 
|  | /* The length of osfd (PROsfd) printed in hexadecimal with 0x prefix */ | 
|  | #ifdef _WIN64 | 
|  | #define OSFD_STRLEN 18 | 
|  | #else | 
|  | #define OSFD_STRLEN 10 | 
|  | #endif | 
|  | /* The length of fd type (PRDescType) printed in decimal */ | 
|  | #define FD_TYPE_STRLEN 1 | 
|  | PRSize newSize; | 
|  | int remainder; | 
|  | char *newBuffer; | 
|  | int nwritten; | 
|  | char *cur; | 
|  | int freeSize; | 
|  |  | 
|  | if (fd->identity != PR_NSPR_IO_LAYER) { | 
|  | PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); | 
|  | return PR_FAILURE; | 
|  | } | 
|  | if (fd->secret->inheritable == _PR_TRI_UNKNOWN) { | 
|  | _PR_MD_QUERY_FD_INHERITABLE(fd); | 
|  | } | 
|  | if (fd->secret->inheritable != _PR_TRI_TRUE) { | 
|  | PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, 0); | 
|  | return PR_FAILURE; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * We also need to account for the : separators and the | 
|  | * terminating null byte. | 
|  | */ | 
|  | if (NULL == attr->fdInheritBuffer) { | 
|  | /* The first time, we print "NSPR_INHERIT_FDS=<name>:<type>:<val>" */ | 
|  | newSize = NSPR_INHERIT_FDS_STRLEN + strlen(name) | 
|  | + FD_TYPE_STRLEN + OSFD_STRLEN + 2 + 1; | 
|  | } else { | 
|  | /* At other times, we print ":<name>:<type>:<val>" */ | 
|  | newSize = attr->fdInheritBufferUsed + strlen(name) | 
|  | + FD_TYPE_STRLEN + OSFD_STRLEN + 3 + 1; | 
|  | } | 
|  | if (newSize > attr->fdInheritBufferSize) { | 
|  | /* Make newSize a multiple of FD_INHERIT_BUFFER_INCR */ | 
|  | remainder = newSize % FD_INHERIT_BUFFER_INCR; | 
|  | if (remainder != 0) { | 
|  | newSize += (FD_INHERIT_BUFFER_INCR - remainder); | 
|  | } | 
|  | if (NULL == attr->fdInheritBuffer) { | 
|  | newBuffer = (char *) PR_MALLOC(newSize); | 
|  | } else { | 
|  | newBuffer = (char *) PR_REALLOC(attr->fdInheritBuffer, newSize); | 
|  | } | 
|  | if (NULL == newBuffer) { | 
|  | PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); | 
|  | return PR_FAILURE; | 
|  | } | 
|  | attr->fdInheritBuffer = newBuffer; | 
|  | attr->fdInheritBufferSize = newSize; | 
|  | } | 
|  | cur = attr->fdInheritBuffer + attr->fdInheritBufferUsed; | 
|  | freeSize = attr->fdInheritBufferSize - attr->fdInheritBufferUsed; | 
|  | if (0 == attr->fdInheritBufferUsed) { | 
|  | nwritten = PR_snprintf(cur, freeSize, | 
|  | "NSPR_INHERIT_FDS=%s:%d:0x%" PR_PRIxOSFD, | 
|  | name, (PRIntn)fd->methods->file_type, fd->secret->md.osfd); | 
|  | } else { | 
|  | nwritten = PR_snprintf(cur, freeSize, ":%s:%d:0x%" PR_PRIxOSFD, | 
|  | name, (PRIntn)fd->methods->file_type, fd->secret->md.osfd); | 
|  | } | 
|  | attr->fdInheritBufferUsed += nwritten; | 
|  | return PR_SUCCESS; | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(PRFileDesc *) PR_GetInheritedFD( | 
|  | const char *name) | 
|  | { | 
|  | PRFileDesc *fd; | 
|  | const char *envVar; | 
|  | const char *ptr; | 
|  | int len = strlen(name); | 
|  | PROsfd osfd; | 
|  | int nColons; | 
|  | PRIntn fileType; | 
|  |  | 
|  | envVar = PR_GetEnv("NSPR_INHERIT_FDS"); | 
|  | if (NULL == envVar || '\0' == envVar[0]) { | 
|  | PR_SetError(PR_UNKNOWN_ERROR, 0); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | ptr = envVar; | 
|  | while (1) { | 
|  | if ((ptr[len] == ':') && (strncmp(ptr, name, len) == 0)) { | 
|  | ptr += len + 1; | 
|  | if (PR_sscanf(ptr, "%d:0x%" PR_SCNxOSFD, &fileType, &osfd) != 2) { | 
|  | PR_SetError(PR_UNKNOWN_ERROR, 0); | 
|  | return NULL; | 
|  | } | 
|  | switch ((PRDescType)fileType) { | 
|  | case PR_DESC_FILE: | 
|  | fd = PR_ImportFile(osfd); | 
|  | break; | 
|  | case PR_DESC_PIPE: | 
|  | fd = PR_ImportPipe(osfd); | 
|  | break; | 
|  | case PR_DESC_SOCKET_TCP: | 
|  | fd = PR_ImportTCPSocket(osfd); | 
|  | break; | 
|  | case PR_DESC_SOCKET_UDP: | 
|  | fd = PR_ImportUDPSocket(osfd); | 
|  | break; | 
|  | default: | 
|  | PR_ASSERT(0); | 
|  | PR_SetError(PR_UNKNOWN_ERROR, 0); | 
|  | fd = NULL; | 
|  | break; | 
|  | } | 
|  | if (fd) { | 
|  | /* | 
|  | * An inherited FD is inheritable by default. | 
|  | * The child process needs to call PR_SetFDInheritable | 
|  | * to make it non-inheritable if so desired. | 
|  | */ | 
|  | fd->secret->inheritable = _PR_TRI_TRUE; | 
|  | } | 
|  | return fd; | 
|  | } | 
|  | /* Skip three colons */ | 
|  | nColons = 0; | 
|  | while (*ptr) { | 
|  | if (*ptr == ':') { | 
|  | if (++nColons == 3) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | ptr++; | 
|  | } | 
|  | if (*ptr == '\0') { | 
|  | PR_SetError(PR_UNKNOWN_ERROR, 0); | 
|  | return NULL; | 
|  | } | 
|  | ptr++; | 
|  | } | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(PRProcess*) PR_CreateProcess( | 
|  | const char *path, | 
|  | char *const *argv, | 
|  | char *const *envp, | 
|  | const PRProcessAttr *attr) | 
|  | { | 
|  | return _PR_MD_CREATE_PROCESS(path, argv, envp, attr); | 
|  | }  /* PR_CreateProcess */ | 
|  |  | 
|  | PR_IMPLEMENT(PRStatus) PR_CreateProcessDetached( | 
|  | const char *path, | 
|  | char *const *argv, | 
|  | char *const *envp, | 
|  | const PRProcessAttr *attr) | 
|  | { | 
|  | PRProcess *process; | 
|  | PRStatus rv; | 
|  |  | 
|  | process = PR_CreateProcess(path, argv, envp, attr); | 
|  | if (NULL == process) { | 
|  | return PR_FAILURE; | 
|  | } | 
|  | rv = PR_DetachProcess(process); | 
|  | PR_ASSERT(PR_SUCCESS == rv); | 
|  | if (rv == PR_FAILURE) { | 
|  | PR_DELETE(process); | 
|  | return PR_FAILURE; | 
|  | } | 
|  | return PR_SUCCESS; | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(PRStatus) PR_DetachProcess(PRProcess *process) | 
|  | { | 
|  | return _PR_MD_DETACH_PROCESS(process); | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(PRStatus) PR_WaitProcess(PRProcess *process, PRInt32 *exitCode) | 
|  | { | 
|  | return _PR_MD_WAIT_PROCESS(process, exitCode); | 
|  | }  /* PR_WaitProcess */ | 
|  |  | 
|  | PR_IMPLEMENT(PRStatus) PR_KillProcess(PRProcess *process) | 
|  | { | 
|  | return _PR_MD_KILL_PROCESS(process); | 
|  | } | 
|  |  | 
|  | /* | 
|  | ******************************************************************** | 
|  | * | 
|  | * Module initialization | 
|  | * | 
|  | ******************************************************************** | 
|  | */ | 
|  |  | 
|  | static struct { | 
|  | PRLock *ml; | 
|  | PRCondVar *cv; | 
|  | } mod_init; | 
|  |  | 
|  | static void _PR_InitCallOnce(void) { | 
|  | mod_init.ml = PR_NewLock(); | 
|  | PR_ASSERT(NULL != mod_init.ml); | 
|  | mod_init.cv = PR_NewCondVar(mod_init.ml); | 
|  | PR_ASSERT(NULL != mod_init.cv); | 
|  | } | 
|  |  | 
|  | void _PR_CleanupCallOnce() | 
|  | { | 
|  | PR_DestroyLock(mod_init.ml); | 
|  | mod_init.ml = NULL; | 
|  | PR_DestroyCondVar(mod_init.cv); | 
|  | mod_init.cv = NULL; | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(PRStatus) PR_CallOnce( | 
|  | PRCallOnceType *once, | 
|  | PRCallOnceFN    func) | 
|  | { | 
|  | if (!_pr_initialized) { | 
|  | _PR_ImplicitInitialization(); | 
|  | } | 
|  |  | 
|  | PR_Lock(mod_init.ml); | 
|  | PRIntn initialized = once->initialized; | 
|  | PRStatus status = once->status; | 
|  | PR_Unlock(mod_init.ml); | 
|  | if (!initialized) { | 
|  | if (PR_ATOMIC_SET(&once->inProgress, 1) == 0) { | 
|  | status = (*func)(); | 
|  | PR_Lock(mod_init.ml); | 
|  | once->status = status; | 
|  | once->initialized = 1; | 
|  | PR_NotifyAllCondVar(mod_init.cv); | 
|  | PR_Unlock(mod_init.ml); | 
|  | } else { | 
|  | PR_Lock(mod_init.ml); | 
|  | while (!once->initialized) { | 
|  | PR_WaitCondVar(mod_init.cv, PR_INTERVAL_NO_TIMEOUT); | 
|  | } | 
|  | status = once->status; | 
|  | PR_Unlock(mod_init.ml); | 
|  | if (PR_SUCCESS != status) { | 
|  | PR_SetError(PR_CALL_ONCE_ERROR, 0); | 
|  | } | 
|  | } | 
|  | return status; | 
|  | } | 
|  | if (PR_SUCCESS != status) { | 
|  | PR_SetError(PR_CALL_ONCE_ERROR, 0); | 
|  | } | 
|  | return status; | 
|  | } | 
|  |  | 
|  | PR_IMPLEMENT(PRStatus) PR_CallOnceWithArg( | 
|  | PRCallOnceType      *once, | 
|  | PRCallOnceWithArgFN  func, | 
|  | void                *arg) | 
|  | { | 
|  | if (!_pr_initialized) { | 
|  | _PR_ImplicitInitialization(); | 
|  | } | 
|  |  | 
|  | PR_Lock(mod_init.ml); | 
|  | PRIntn initialized = once->initialized; | 
|  | PRStatus status = once->status; | 
|  | PR_Unlock(mod_init.ml); | 
|  | if (!initialized) { | 
|  | if (PR_ATOMIC_SET(&once->inProgress, 1) == 0) { | 
|  | status = (*func)(arg); | 
|  | PR_Lock(mod_init.ml); | 
|  | once->status = status; | 
|  | once->initialized = 1; | 
|  | PR_NotifyAllCondVar(mod_init.cv); | 
|  | PR_Unlock(mod_init.ml); | 
|  | } else { | 
|  | PR_Lock(mod_init.ml); | 
|  | while (!once->initialized) { | 
|  | PR_WaitCondVar(mod_init.cv, PR_INTERVAL_NO_TIMEOUT); | 
|  | } | 
|  | status = once->status; | 
|  | PR_Unlock(mod_init.ml); | 
|  | if (PR_SUCCESS != status) { | 
|  | PR_SetError(PR_CALL_ONCE_ERROR, 0); | 
|  | } | 
|  | } | 
|  | return status; | 
|  | } | 
|  | if (PR_SUCCESS != status) { | 
|  | PR_SetError(PR_CALL_ONCE_ERROR, 0); | 
|  | } | 
|  | return status; | 
|  | } | 
|  |  | 
|  | PRBool _PR_Obsolete(const char *obsolete, const char *preferred) | 
|  | { | 
|  | #if defined(DEBUG) | 
|  | PR_fprintf( | 
|  | PR_STDERR, "'%s' is obsolete. Use '%s' instead.\n", | 
|  | obsolete, (NULL == preferred) ? "something else" : preferred); | 
|  | #endif | 
|  | return PR_FALSE; | 
|  | }  /* _PR_Obsolete */ | 
|  |  | 
|  | /* prinit.c */ | 
|  |  | 
|  |  |