diff --git a/src/pal/src/cruntime/misc.cpp b/src/pal/src/cruntime/misc.cpp index 107d5e316d1a..708f882c7f49 100644 --- a/src/pal/src/cruntime/misc.cpp +++ b/src/pal/src/cruntime/misc.cpp @@ -20,7 +20,6 @@ Module Name: #include "pal/thread.hpp" #include "pal/threadsusp.hpp" -#include "pal/malloc.hpp" #include "pal/palinternal.h" #include "pal/dbgmsg.h" #include "pal/misc.h" @@ -31,9 +30,7 @@ Module Name: #include #include #include -#if HAVE_CRT_EXTERNS_H -#include -#endif // HAVE_CRT_EXTERNS_H + #if defined(_AMD64_) || defined(_x86_) #include #endif // defined(_AMD64_) || defined(_x86_) @@ -43,10 +40,6 @@ Module Name: SET_DEFAULT_DEBUG_CHANNEL(CRT); -char **palEnvironment = NULL; - -CRITICAL_SECTION gcsEnvironment; - using namespace CorUnix; /*++ @@ -138,65 +131,6 @@ int * __cdecl PAL_errno( int caller ) return retval; } -/*++ - -Function : _putenv. - -See MSDN for more details. - -Note: The BSD implementation can cause - memory leaks. See man pages for more details. ---*/ -int -__cdecl -_putenv( const char * envstring ) -{ - int ret = -1; - - PERF_ENTRY(_putenv); - ENTRY( "_putenv( %p (%s) )\n", envstring ? envstring : "NULL", envstring ? envstring : "NULL") ; - - if (!envstring) - { - ERROR( "_putenv() called with NULL envstring!\n"); - goto EXIT; - } - - ret = MiscPutenv(envstring, TRUE) ? 0 : -1; - -EXIT: - LOGEXIT( "_putenv returning %d\n", ret); - PERF_EXIT(_putenv); - return ret; -} - -/*++ - -Function : PAL_getenv - -See MSDN for more details. ---*/ -char * __cdecl PAL_getenv(const char *varname) -{ - char *retval; - - PERF_ENTRY(getenv); - ENTRY("getenv (%p (%s))\n", varname ? varname : "NULL", varname ? varname : "NULL"); - - if (strcmp(varname, "") == 0) - { - ERROR("getenv called with a empty variable name\n"); - LOGEXIT("getenv returning NULL\n"); - PERF_EXIT(getenv); - return(NULL); - } - retval = MiscGetenv(varname); - - LOGEXIT("getenv returning %p\n", retval); - PERF_EXIT(getenv); - return(retval); -} - /*++ Function: @@ -361,306 +295,6 @@ void PAL__mm_setcsr(unsigned int i) #endif // _AMD64_ -/*++ -Function: - MiscGetEnvArray - -Get a reference to the process's environ array into palEnvironment - -NOTE: This function MUST be called while holding the gcsEnvironment - critical section (except if the caller is the initialization - routine) ---*/ -void -MiscGetEnvArray(void) -{ -#if HAVE__NSGETENVIRON - palEnvironment = *(_NSGetEnviron()); -#else // HAVE__NSGETENVIRON - extern char **environ; - palEnvironment = environ; -#endif // HAVE__NSGETENVIRON -} - -/*++ -Function: - MiscSetEnvArray - -Make sure the process's environ array is in sync with palEnvironment variable - -NOTE: This function MUST be called while holding the gcsEnvironment - critical section (except if the caller is the initialization - routine) ---*/ -void -MiscSetEnvArray(void) -{ -#if HAVE__NSGETENVIRON - *(_NSGetEnviron()) = palEnvironment; -#else // HAVE__NSGETENVIRON - extern char **environ; - environ = palEnvironment; -#endif // HAVE__NSGETENVIRON -} - -/*++ -Function: - MiscInitialize - -Initialization function called from PAL_Initialize. -Allocates the TLS Index. On systems that use extern variables for -time zone information, this also initializes those variables. - -Note: This is called before debug channels are initialized, so it - cannot use debug tracing calls. ---*/ -BOOL -MiscInitialize(void) -{ - InternalInitializeCriticalSection(&gcsEnvironment); - MiscGetEnvArray(); - - return TRUE; -} - -/*++ -Function: - MiscCleanup - -Termination function called from PAL_Terminate to delete the -TLS Keys created in MiscInitialize ---*/ -void MiscCleanup(void) -{ - TRACE("Cleaning Misc...\n"); - InternalDeleteCriticalSection(&gcsEnvironment); -} - -/*++ -Function: - MiscGetenv - -Gets an environment variable's value from environ. The returned buffer -must not be modified or freed. ---*/ -char *MiscGetenv(const char *name) -{ - int i, length; - char *equals; - char *pRet = NULL; - CPalThread * pthrCurrent = InternalGetCurrentThread(); - - InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); - - length = strlen(name); - for(i = 0; palEnvironment[i] != NULL; i++) - { - if (memcmp(palEnvironment[i], name, length) == 0) - { - equals = palEnvironment[i] + length; - if (*equals == '\0') - { - pRet = (char *) ""; - goto done; - } - else if (*equals == '=') - { - pRet = equals + 1; - goto done; - } - } - } - -done: - InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); - return pRet; -} - - -/*++ -Function: - MiscPutenv - -Sets an environment variable's value by directly modifying palEnvironment. -Returns TRUE if the variable was set, or FALSE if PAL_malloc or realloc -failed or if the given string is malformed. ---*/ -BOOL MiscPutenv(const char *string, BOOL deleteIfEmpty) -{ - const char *equals, *existingEquals; - char *copy = NULL; - int length; - int i, j; - bool fOwningCS = false; - BOOL result = FALSE; - CPalThread * pthrCurrent = InternalGetCurrentThread(); - - equals = strchr(string, '='); - if (equals == string || equals == NULL) - { - // "=foo" and "foo" have no meaning - goto done; - } - if (equals[1] == '\0' && deleteIfEmpty) - { - // "foo=" removes foo from the environment in _putenv() on Windows. - // The same string can result from a call to SetEnvironmentVariable() - // with the empty string as the value, but in that case we want to - // set the variable's value to "". deleteIfEmpty will be FALSE in - // that case. - length = strlen(string); - copy = (char *) InternalMalloc(length); - if (copy == NULL) - { - goto done; - } - memcpy(copy, string, length - 1); - copy[length - 1] = '\0'; // Change '=' to '\0' - MiscUnsetenv(copy); - result = TRUE; - } - else - { - // See if we are replacing an item or adding one. - - // Make our copy up front, since we'll use it either way. - copy = strdup(string); - if (copy == NULL) - { - goto done; - } - - length = equals - string; - - InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); - fOwningCS = true; - - for(i = 0; palEnvironment[i] != NULL; i++) - { - existingEquals = strchr(palEnvironment[i], '='); - if (existingEquals == NULL) - { - // The PAL screens out malformed strings, but - // environ comes from the system, so it might - // have strings without '='. We treat the entire - // string as a name in that case. - existingEquals = palEnvironment[i] + strlen(palEnvironment[i]); - } - if (existingEquals - palEnvironment[i] == length) - { - if (memcmp(string, palEnvironment[i], length) == 0) - { - // Replace this one. Don't free the original, - // though, because there may be outstanding - // references to it that were acquired via - // getenv. This is an unavoidable memory leak. - palEnvironment[i] = copy; - - // Set 'copy' to NULL so it won't be freed - copy = NULL; - - result = TRUE; - break; - } - } - } - if (palEnvironment[i] == NULL) - { - static BOOL sAllocatedEnviron = FALSE; - // Add a new environment variable. - // We'd like to realloc palEnvironment, but we can't do that the - // first time through. - char **newEnviron = NULL; - - if (sAllocatedEnviron) { - if (NULL == (newEnviron = - (char **)InternalRealloc(palEnvironment, (i + 2) * sizeof(char *)))) - { - goto done; - } - } - else - { - // Allocate palEnvironment ourselves so we can realloc it later. - newEnviron = (char **)InternalMalloc((i + 2) * sizeof(char *)); - if (newEnviron == NULL) - { - goto done; - } - for(j = 0; palEnvironment[j] != NULL; j++) - { - newEnviron[j] = palEnvironment[j]; - } - sAllocatedEnviron = TRUE; - } - palEnvironment = newEnviron; - MiscSetEnvArray(); - palEnvironment[i] = copy; - palEnvironment[i + 1] = NULL; - - // Set 'copy' to NULL so it won't be freed - copy = NULL; - - result = TRUE; - } - } -done: - - if (fOwningCS) - { - InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); - } - if (NULL != copy) - { - InternalFree(copy); - } - return result; -} - -/*++ -Function: - MiscUnsetenv - -Removes a variable from the environment. Does nothing if the variable -does not exist in the environment. ---*/ -void MiscUnsetenv(const char *name) -{ - const char *equals; - int length; - int i, j; - CPalThread * pthrCurrent = InternalGetCurrentThread(); - - length = strlen(name); - - InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); - for(i = 0; palEnvironment[i] != NULL; i++) - { - equals = strchr(palEnvironment[i], '='); - if (equals == NULL) - { - equals = palEnvironment[i] + strlen(palEnvironment[i]); - } - if (equals - palEnvironment[i] == length) - { - if (memcmp(name, palEnvironment[i], length) == 0) - { - // Remove this one. Don't free it, though, since - // there might be oustanding references to it that - // were acquired via getenv. This is an - // unavoidable memory leak. - for(j = i + 1; palEnvironment[j] != NULL; j++) { } - // i is now the one we want to remove. j is the - // last index in palEnvironment, which is NULL. - - // Shift palEnvironment down by the difference between i and j. - memmove(palEnvironment + i, palEnvironment + i + 1, (j - i) * sizeof(char *)); - } - } - } - InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); -} - #if defined(_DEBUG) /*++ diff --git a/src/pal/src/debug/debug.cpp b/src/pal/src/debug/debug.cpp index dd4ea277dfad..86ea9f98e42f 100644 --- a/src/pal/src/debug/debug.cpp +++ b/src/pal/src/debug/debug.cpp @@ -34,7 +34,7 @@ Revision History: #include "pal/process.h" #include "pal/context.h" #include "pal/debug.h" -#include "pal/misc.h" +#include "pal/environ.h" #include "pal/malloc.hpp" #include "pal/module.h" #include "pal/stackstring.hpp" @@ -172,17 +172,19 @@ OutputDebugStringA( { PERF_ENTRY(OutputDebugStringA); ENTRY("OutputDebugStringA (lpOutputString=%p (%s))\n", - lpOutputString?lpOutputString:"NULL", - lpOutputString?lpOutputString:"NULL"); - - /* as we don't support debug events, we are going to output the debug string - to stderr instead of generating OUT_DEBUG_STRING_EVENT */ - if ( (lpOutputString != NULL) && - (NULL != MiscGetenv(PAL_OUTPUTDEBUGSTRING))) + lpOutputString ? lpOutputString : "NULL", + lpOutputString ? lpOutputString : "NULL"); + + // As we don't support debug events, we are going to output the debug string + // to stderr instead of generating OUT_DEBUG_STRING_EVENT. It's safe to tell + // EnvironGetenv not to make a copy of the value here since we only want to + // check whether it exists, not actually use it. + if ((lpOutputString != NULL) && + (NULL != EnvironGetenv(PAL_OUTPUTDEBUGSTRING, /* copyValue */ FALSE))) { fprintf(stderr, "%s", lpOutputString); } - + LOGEXIT("OutputDebugStringA returns\n"); PERF_EXIT(OutputDebugStringA); } @@ -340,11 +342,14 @@ DebugBreakCommand() { #ifdef ENABLE_RUN_ON_DEBUG_BREAK extern MODSTRUCT exe_module; - const char *command_string = getenv (PAL_RUN_ON_DEBUG_BREAK); - if (command_string) { + + char *command_string = EnvironGetenv(PAL_RUN_ON_DEBUG_BREAK); + if (command_string) + { char pid_buf[sizeof (PID_TEXT) + 32]; PathCharString exe_bufString; int libNameLength = 10; + if (exe_module.lib_name != NULL) { libNameLength = PAL_wcslen(exe_module.lib_name); @@ -358,10 +363,13 @@ DebugBreakCommand() goto FAILED; } - if (snprintf (pid_buf, sizeof (pid_buf), PID_TEXT "%d", getpid()) <= 0) { + if (snprintf (pid_buf, sizeof (pid_buf), PID_TEXT "%d", getpid()) <= 0) + { goto FAILED; } - if (snprintf (exe_buf, sizeof (CHAR) * (dwexe_buf + 1), EXE_TEXT "%ls", (wchar_t *)exe_module.lib_name) <= 0) { + + if (snprintf (exe_buf, sizeof (CHAR) * (dwexe_buf + 1), EXE_TEXT "%ls", (wchar_t *)exe_module.lib_name) <= 0) + { goto FAILED; } @@ -370,16 +378,28 @@ DebugBreakCommand() variables in the child process, but if we do that we can't check for errors. putenv/setenv can fail when out of memory */ - if (!MiscPutenv (pid_buf, FALSE) || !MiscPutenv (exe_buf, FALSE)) { + if (!EnvironPutenv (pid_buf, FALSE) || !EnvironPutenv (exe_buf, FALSE)) + { goto FAILED; } - if (run_debug_command (command_string)) { + + if (run_debug_command (command_string)) + { goto FAILED; } + + InternalFree(command_string); return 1; } + return 0; + FAILED: + if (command_string) + { + InternalFree(command_string); + } + fprintf (stderr, "Failed to execute command: '%s'\n", command_string); return -1; #else // ENABLE_RUN_ON_DEBUG_BREAK diff --git a/src/pal/src/exception/machexception.cpp b/src/pal/src/exception/machexception.cpp index ea57173ea026..cd4e26ddee12 100644 --- a/src/pal/src/exception/machexception.cpp +++ b/src/pal/src/exception/machexception.cpp @@ -29,6 +29,7 @@ Module Name: #include "pal/process.h" #include "pal/virtual.h" #include "pal/map.hpp" +#include "pal/environ.h" #include "machmessage.h" @@ -170,10 +171,11 @@ GetExceptionMask() { exMode = MachException_Default; - const char * exceptionSettings = getenv(PAL_MACH_EXCEPTION_MODE); + char* exceptionSettings = EnvironGetenv(PAL_MACH_EXCEPTION_MODE); if (exceptionSettings) { exMode = (MachExceptionMode)atoi(exceptionSettings); + InternalFree(exceptionSettings); } else { diff --git a/src/pal/src/exception/machmessage.cpp b/src/pal/src/exception/machmessage.cpp index 2e11da802092..a6f7e57484f2 100644 --- a/src/pal/src/exception/machmessage.cpp +++ b/src/pal/src/exception/machmessage.cpp @@ -16,6 +16,8 @@ Module Name: #include "config.h" #include "pal/dbgmsg.h" +#include "pal/environ.h" +#include "pal/malloc.hpp" #include "pal/thread.hpp" #include "machmessage.h" diff --git a/src/pal/src/exception/machmessage.h b/src/pal/src/exception/machmessage.h index 22ec0a75bfd9..244396cd35af 100644 --- a/src/pal/src/exception/machmessage.h +++ b/src/pal/src/exception/machmessage.h @@ -56,7 +56,7 @@ using namespace CorUnix; #ifdef _DEBUG -#define NONPAL_TRACE_ENABLED getenv("NONPAL_TRACING") +#define NONPAL_TRACE_ENABLED EnvironGetenv("NONPAL_TRACING", /* copyValue */ false) #define NONPAL_ASSERT(_msg, ...) NONPAL_RETAIL_ASSERT(_msg, __VA_ARGS__) @@ -67,7 +67,7 @@ using namespace CorUnix; } while (false) // Debug-only output with printf-style formatting. -#define NONPAL_TRACE(_format, ...) do { \ +#define NONPAL_TRACE(_format, ...) do { \ if (NONPAL_TRACE_ENABLED) printf("NONPAL_TRACE: " _format, ## __VA_ARGS__); \ } while (false) diff --git a/src/pal/src/include/pal/environ.h b/src/pal/src/include/pal/environ.h new file mode 100644 index 000000000000..1c0bce21ca1d --- /dev/null +++ b/src/pal/src/include/pal/environ.h @@ -0,0 +1,78 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*++ + + + +Module Name: + + include/pal/environ.h + +Abstract: + Header file for functions manipulating environment variables + + +--*/ + +#ifndef __ENVIRON_H_ +#define __ENVIRON_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +/*++ +Variables : + + palEnvironment: a global variable equivalent to environ on systems on + which that exists, and a pointer to an array of environment + strings on systems without environ. + gcsEnvironment: critical section to synchronize access to palEnvironment +--*/ +extern char **palEnvironment; +extern CRITICAL_SECTION gcsEnvironment; + +/*++ + +Function: + EnvironInitialize + +Initialization function for the PAL environment code. +--*/ +BOOL EnvironInitialize(); + +/*++ +Function: + EnvironGetenv + +Get the value of environment variable with the given name. +--*/ +char *EnvironGetenv(const char *name, BOOL copyValue = TRUE); + +/*++ +Function: + EnvironPutenv + +Add the environment variable string provided to the PAL version +of the environment. +--*/ +BOOL EnvironPutenv(const char *string, BOOL deleteIfEmpty); + +/*++ +Function: + EnvironUnsetenv + +Remove the environment variable with the given name from the PAL +version of the environment if it exists. +--*/ +void EnvironUnsetenv(const char *name); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif /* __ENVIRON_H_ */ + diff --git a/src/pal/src/include/pal/misc.h b/src/pal/src/include/pal/misc.h index 2be2cc06b285..65d59aee60bb 100644 --- a/src/pal/src/include/pal/misc.h +++ b/src/pal/src/include/pal/misc.h @@ -26,17 +26,6 @@ extern "C" { #endif // __cplusplus -/*++ -Variables : - - palEnvironment: a global variable equivalent to environ on systems on - which that exists, and a pointer to an array of environment - strings on systems without environ. - gcsEnvironment: critical section to synchronize access to palEnvironment ---*/ -extern char **palEnvironment; -extern CRITICAL_SECTION gcsEnvironment; - /*++ Function : @@ -87,49 +76,6 @@ Function : --*/ void MsgBoxCleanup( void ); -/*++ - -Function: - MiscInitialize - ---*/ -BOOL MiscInitialize(); - -/*++ -Function: - MiscCleanup - ---*/ -VOID MiscCleanup(); - -/*++ -Function: - MiscGetenv - -Gets an environment variable's value from environ. The returned buffer -must not be modified or freed. ---*/ -char *MiscGetenv(const char *name); - -/*++ -Function: - MiscPutenv - -Sets an environment variable's value by directly modifying environ. -Returns TRUE if the variable was set, or FALSE if malloc or realloc -failed or if the given string is malformed. ---*/ -BOOL MiscPutenv(const char *string, BOOL deleteIfEmpty); - -/*++ -Function: - MiscUnsetenv - -Removes a variable from the environment. Does nothing if the variable -does not exist in the environment. ---*/ -void MiscUnsetenv(const char *name); - #ifdef __cplusplus } #endif // __cplusplus diff --git a/src/pal/src/init/pal.cpp b/src/pal/src/init/pal.cpp index b2efdf2779bb..10871aadaaae 100644 --- a/src/pal/src/init/pal.cpp +++ b/src/pal/src/init/pal.cpp @@ -34,6 +34,7 @@ Module Name: #include "pal/module.h" #include "pal/virtual.h" #include "pal/misc.h" +#include "pal/environ.h" #include "pal/utils.h" #include "pal/debug.h" #include "pal/locale.h" @@ -240,14 +241,14 @@ Initialize( } // Initialize the environment. - if (FALSE == MiscInitialize()) + if (FALSE == EnvironInitialize()) { goto CLEANUP0; } // Initialize debug channel settings before anything else. // This depends on the environment, so it must come after - // MiscInitialize. + // EnvironInitialize. if (FALSE == DBG_init_channels()) { goto CLEANUP0; @@ -1182,19 +1183,16 @@ static LPWSTR INIT_FindEXEPath(LPCSTR exe_name) /* no path was specified : search $PATH */ - env_path = MiscGetenv("PATH"); + env_path = EnvironGetenv("PATH"); if (!env_path || *env_path=='\0') { WARN("$PATH isn't set.\n"); - goto last_resort; - } + if (env_path != NULL) + { + InternalFree(env_path); + } - /* get our own copy of env_path so we can modify it */ - env_path = strdup(env_path); - if (!env_path) - { - ERROR("Not enough memory to copy $PATH!\n"); - return NULL; + goto last_resort; } exe_name_length=strlen(exe_name); @@ -1327,7 +1325,7 @@ static LPWSTR INIT_FindEXEPath(LPCSTR exe_name) } InternalFree(env_path); - TRACE("No %s found in $PATH (%s)\n", exe_name, MiscGetenv("PATH")); + TRACE("No %s found in $PATH (%s)\n", exe_name, EnvironGetenv("PATH", FALSE)); last_resort: /* last resort : see if the executable is in the current directory. This is diff --git a/src/pal/src/loader/module.cpp b/src/pal/src/loader/module.cpp index d05e27222786..83d45f3e230d 100644 --- a/src/pal/src/loader/module.cpp +++ b/src/pal/src/loader/module.cpp @@ -30,7 +30,7 @@ Module Name: #include "pal/utils.h" #include "pal/init.h" #include "pal/modulename.h" -#include "pal/misc.h" +#include "pal/environ.h" #include "pal/virtual.h" #include "pal/map.hpp" #include "pal/stackstring.hpp" @@ -745,12 +745,17 @@ PAL_LOADLoadPEFile(HANDLE hFile) #ifdef _DEBUG if (loadedBase != nullptr) { - char* envVar = getenv("PAL_ForcePEMapFailure"); - if (envVar && strlen(envVar) > 0) + char* envVar = EnvironGetenv("PAL_ForcePEMapFailure"); + if (envVar) { - TRACE("Forcing failure of PE file map, and retry\n"); - PAL_LOADUnloadPEFile(loadedBase); // unload it - loadedBase = MAPMapPEFile(hFile); // load it again + if (strlen(envVar) > 0) + { + TRACE("Forcing failure of PE file map, and retry\n"); + PAL_LOADUnloadPEFile(loadedBase); // unload it + loadedBase = MAPMapPEFile(hFile); // load it again + } + + InternalFree(envVar); } } #endif // _DEBUG diff --git a/src/pal/src/map/map.cpp b/src/pal/src/map/map.cpp index f8b30faaeaa5..7d4cf9272e9a 100644 --- a/src/pal/src/map/map.cpp +++ b/src/pal/src/map/map.cpp @@ -24,6 +24,7 @@ Module Name: #include "pal/init.h" #include "pal/critsect.h" #include "pal/virtual.h" +#include "pal/environ.h" #include "common.h" #include "pal/map.hpp" #include "pal/thread.hpp" @@ -2288,6 +2289,7 @@ void * MAPMapPEFile(HANDLE hFile) void * retval; #if _DEBUG bool forceRelocs = false; + char* envVar; #endif ENTRY("MAPMapPEFile (hFile=%p)\n", hFile); @@ -2393,13 +2395,18 @@ void * MAPMapPEFile(HANDLE hFile) } #if _DEBUG - char * envVar; - envVar = getenv("PAL_ForceRelocs"); - if (envVar && strlen(envVar) > 0) + envVar = EnvironGetenv("PAL_ForceRelocs"); + if (envVar) { - forceRelocs = true; - TRACE_(LOADER)("Forcing rebase of image\n"); + if (strlen(envVar) > 0) + { + forceRelocs = true; + TRACE_(LOADER)("Forcing rebase of image\n"); + } + + InternalFree(envVar); } + void * pForceRelocBase; pForceRelocBase = NULL; if (forceRelocs) diff --git a/src/pal/src/misc/dbgmsg.cpp b/src/pal/src/misc/dbgmsg.cpp index f03db0a2905f..c96dbdd50f40 100644 --- a/src/pal/src/misc/dbgmsg.cpp +++ b/src/pal/src/misc/dbgmsg.cpp @@ -29,7 +29,7 @@ Module Name: #include "pal/cruntime.h" #include "pal/critsect.h" #include "pal/file.h" -#include "pal/misc.h" +#include "pal/environ.h" /* standard headers */ @@ -150,7 +150,7 @@ Function : BOOL DBG_init_channels(void) { INT i; - LPCSTR env_string; + LPSTR env_string; LPSTR env_workstring; LPSTR env_pcache; LPSTR entry_ptr; @@ -168,21 +168,9 @@ BOOL DBG_init_channels(void) /* parse PAL_DBG_CHANNELS environment variable */ - if (!(env_string = MiscGetenv(ENV_CHANNELS))) - { - env_pcache = env_workstring = NULL; - } - else - { - env_pcache = env_workstring = PAL__strdup(env_string); + env_string = EnvironGetenv(ENV_CHANNELS); + env_pcache = env_workstring = env_string; - if (env_workstring == NULL) - { - /* Not enough memory */ - DeleteCriticalSection(&fprintf_crit_section); - return FALSE; - } - } while(env_workstring) { entry_ptr=env_workstring; @@ -316,7 +304,7 @@ BOOL DBG_init_channels(void) PAL_free(env_pcache); /* select output file */ - env_string=MiscGetenv(ENV_FILE); + env_string = EnvironGetenv(ENV_FILE); if(env_string && *env_string!='\0') { if(!strcmp(env_string, "stderr")) @@ -346,8 +334,13 @@ BOOL DBG_init_channels(void) output_file = stderr; /* output to stderr by default */ } + if(env_string) + { + PAL_free(env_string); + } + /* see if we need to disable assertions */ - env_string = MiscGetenv(ENV_ASSERTS); + env_string = EnvironGetenv(ENV_ASSERTS); if(env_string && 0 == strcmp(env_string,"1")) { g_Dbg_asserts_enabled = FALSE; @@ -357,11 +350,17 @@ BOOL DBG_init_channels(void) g_Dbg_asserts_enabled = TRUE; } + if(env_string) + { + PAL_free(env_string); + } + /* select ENTRY level limitation */ - env_string = MiscGetenv(ENV_ENTRY_LEVELS); + env_string = EnvironGetenv(ENV_ENTRY_LEVELS); if(env_string) { max_entry_level = atoi(env_string); + PAL_free(env_string); } else { @@ -819,15 +818,32 @@ bool DBG_ShouldCheckStackAlignment() if (caMode == CheckAlignment_Uninitialized) { - const char * checkAlignmentSettings = getenv(PAL_CHECK_ALIGNMENT_MODE); + char* checkAlignmentSettings; + bool shouldFreeCheckAlignmentSettings = false; + if (palEnvironment == nullptr) + { + // This function might be called before the PAL environment is initialized. + // In this case, use the system getenv instead. + checkAlignmentSettings = ::getenv(PAL_CHECK_ALIGNMENT_MODE); + } + else + { + checkAlignmentSettings = EnvironGetenv(PAL_CHECK_ALIGNMENT_MODE); + shouldFreeCheckAlignmentSettings = true; + } + caMode = checkAlignmentSettings ? (CheckAlignmentMode)atoi(checkAlignmentSettings) : CheckAlignment_Default; + + if (checkAlignmentSettings && shouldFreeCheckAlignmentSettings) + { + InternalFree(checkAlignmentSettings); + } } return caMode == CheckAlignment_On; } #endif // _DEBUG && __APPLE__ - #ifdef __APPLE__ #include "CoreFoundation/CFUserNotification.h" @@ -860,10 +876,12 @@ void PAL_DisplayDialog(const char *szTitle, const char *szText) if (dispDialog == DisplayDialog_Uninitialized) { - const char * displayDialog = getenv(PAL_DISPLAY_DIALOG); + char* displayDialog = EnvironGetenv(PAL_DISPLAY_DIALOG); if (displayDialog) { int i = atoi(displayDialog); + InternalFree(displayDialog); + switch (i) { case 0: diff --git a/src/pal/src/misc/environ.cpp b/src/pal/src/misc/environ.cpp index e715db04658a..9edfd135d3e0 100644 --- a/src/pal/src/misc/environ.cpp +++ b/src/pal/src/misc/environ.cpp @@ -8,7 +8,7 @@ Module Name: - environ.c + environ.cpp Abstract: @@ -23,12 +23,25 @@ Revision History: #include "pal/palinternal.h" #include "pal/critsect.h" #include "pal/dbgmsg.h" -#include "pal/misc.h" +#include "pal/environ.h" +#include "pal/malloc.hpp" + +#if HAVE_CRT_EXTERNS_H +#include +#endif #include +using namespace CorUnix; + SET_DEFAULT_DEBUG_CHANNEL(MISC); +char **palEnvironment = nullptr; +int palEnvironmentCount = 0; +int palEnvironmentCapacity = 0; + +CRITICAL_SECTION gcsEnvironment; + /*++ Function: GetEnvironmentVariableA @@ -41,11 +54,11 @@ characters. Parameters lpName - [in] Pointer to a null-terminated string that specifies the environment variable. + [in] Pointer to a null-terminated string that specifies the environment variable. lpBuffer - [out] Pointer to a buffer to receive the value of the specified environment variable. + [out] Pointer to a buffer to receive the value of the specified environment variable. nSize - [in] Specifies the size, in TCHARs, of the buffer pointed to by the lpBuffer parameter. + [in] Specifies the size, in TCHARs, of the buffer pointed to by the lpBuffer parameter. Return Values @@ -73,58 +86,72 @@ GetEnvironmentVariableA( PERF_ENTRY(GetEnvironmentVariableA); ENTRY("GetEnvironmentVariableA(lpName=%p (%s), lpBuffer=%p, nSize=%u)\n", - lpName?lpName:"NULL", - lpName?lpName:"NULL", lpBuffer, nSize); - - if (lpName == NULL) + lpName ? lpName : "NULL", + lpName ? lpName : "NULL", lpBuffer, nSize); + + CPalThread * pthrCurrent = InternalGetCurrentThread(); + + if (lpName == nullptr) { - ERROR("lpName is NULL\n"); + ERROR("lpName is null\n"); SetLastError(ERROR_INVALID_PARAMETER); goto done; } if (lpName[0] == 0) { - TRACE("lpName is empty string\n", lpName); + TRACE("lpName is an empty string\n", lpName); SetLastError(ERROR_ENVVAR_NOT_FOUND); goto done; } - - if (strchr(lpName, '=') != NULL) + + if (strchr(lpName, '=') != nullptr) { // GetEnvironmentVariable doesn't permit '=' in variable names. - value = NULL; + value = nullptr; } else { - value = MiscGetenv(lpName); + // Enter the environment critical section so that we can safely get + // the environment variable value without EnvironGetenv making an + // intermediate copy. We will just copy the string to the output + // buffer anyway, so just stay in the critical section until then. + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); + + value = EnvironGetenv(lpName, /* copyValue */ FALSE); + + if (value != nullptr) + { + DWORD valueLength = strlen(value); + if (valueLength < nSize) + { + strcpy_s(lpBuffer, nSize, value); + dwRet = valueLength; + } + else + { + dwRet = valueLength + 1; + } + + SetLastError(ERROR_SUCCESS); + } + + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); } - - if (value == NULL) + + if (value == nullptr) { TRACE("%s is not found\n", lpName); SetLastError(ERROR_ENVVAR_NOT_FOUND); - goto done; } - if (strlen(value) < nSize) - { - strcpy_s(lpBuffer, nSize, value); - dwRet = strlen(value); - } - else - { - dwRet = strlen(value)+1; - } - SetLastError(ERROR_SUCCESS); - done: + LOGEXIT("GetEnvironmentVariableA returns DWORD 0x%x\n", dwRet); PERF_EXIT(GetEnvironmentVariableA); return dwRet; } - /*++ Function: GetEnvironmentVariableW @@ -138,36 +165,37 @@ GetEnvironmentVariableW( OUT LPWSTR lpBuffer, IN DWORD nSize) { - CHAR *inBuff = NULL; - CHAR *outBuff = NULL; + CHAR *inBuff = nullptr; + CHAR *outBuff = nullptr; INT inBuffSize; DWORD size = 0; PERF_ENTRY(GetEnvironmentVariableW); ENTRY("GetEnvironmentVariableW(lpName=%p (%S), lpBuffer=%p, nSize=%u)\n", - lpName?lpName:W16_NULLSTRING, - lpName?lpName:W16_NULLSTRING, lpBuffer, nSize); + lpName ? lpName : W16_NULLSTRING, + lpName ? lpName : W16_NULLSTRING, lpBuffer, nSize); - inBuffSize = WideCharToMultiByte( CP_ACP, 0, lpName, -1, - inBuff, 0, NULL, NULL); - if ( 0 == inBuffSize ) + inBuffSize = WideCharToMultiByte(CP_ACP, 0, lpName, -1, + inBuff, 0, nullptr, nullptr); + if (0 == inBuffSize) { - ERROR( "lpName has to be a valid parameter\n" ); - SetLastError( ERROR_INVALID_PARAMETER ); + ERROR("lpName has to be a valid parameter\n"); + SetLastError(ERROR_INVALID_PARAMETER); goto done; } inBuff = (CHAR *)PAL_malloc(inBuffSize); - if (inBuff == NULL) + if (inBuff == nullptr) { ERROR("malloc failed\n"); SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto done; } - - if (nSize) { + + if (nSize) + { outBuff = (CHAR *)PAL_malloc(nSize*2); - if (outBuff == NULL) + if (outBuff == nullptr) { ERROR("malloc failed\n"); SetLastError(ERROR_NOT_ENOUGH_MEMORY); @@ -175,34 +203,35 @@ GetEnvironmentVariableW( } } - if ( 0 == WideCharToMultiByte( CP_ACP, 0, lpName, -1, inBuff, - inBuffSize, NULL, NULL ) ) + if (0 == WideCharToMultiByte(CP_ACP, 0, lpName, -1, inBuff, + inBuffSize, nullptr, nullptr)) { - ASSERT( "WideCharToMultiByte failed!\n" ); - SetLastError( ERROR_INTERNAL_ERROR ); + ASSERT("WideCharToMultiByte failed!\n"); + SetLastError(ERROR_INTERNAL_ERROR); goto done; } + size = GetEnvironmentVariableA(inBuff, outBuff, nSize); if (size > nSize) { TRACE("Insufficient buffer\n"); } - else if ( size == 0 ) + else if (size == 0) { - /* error handle in GetEnvironmentVariableA */ + // handle error in GetEnvironmentVariableA } else { size = MultiByteToWideChar(CP_ACP, 0, outBuff, -1, lpBuffer, nSize); - if ( 0 != size ) + if (0 != size) { - /* Not including the NULL. */ + // -1 for the null. size--; } else { - ASSERT( "MultiByteToWideChar failed!\n" ); - SetLastError( ERROR_INTERNAL_ERROR ); + ASSERT("MultiByteToWideChar failed!\n"); + SetLastError(ERROR_INTERNAL_ERROR); size = 0; *lpBuffer = '\0'; } @@ -214,10 +243,10 @@ GetEnvironmentVariableW( LOGEXIT("GetEnvironmentVariableW returns DWORD 0x%x\n", size); PERF_EXIT(GetEnvironmentVariableW); + return size; } - /*++ Function: SetEnvironmentVariableW @@ -231,11 +260,11 @@ lpName [in] Pointer to a null-terminated string that specifies the environment variable whose value is being set. The operating system creates the environment variable if it does not exist - and lpValue is not NULL. + and lpValue is not null. lpValue [in] Pointer to a null-terminated string containing the new value of the specified environment variable. If this parameter - is NULL, the variable is deleted from the current process's + is null, the variable is deleted from the current process's environment. Return Values @@ -257,8 +286,8 @@ SetEnvironmentVariableW( IN LPCWSTR lpName, IN LPCWSTR lpValue) { - PCHAR name = NULL; - PCHAR value = NULL; + PCHAR name = nullptr; + PCHAR value = nullptr; INT nameSize = 0; INT valueSize = 0; BOOL bRet = FALSE; @@ -269,7 +298,7 @@ SetEnvironmentVariableW( lpName?lpName:W16_NULLSTRING, lpValue?lpValue:W16_NULLSTRING, lpValue?lpValue:W16_NULLSTRING); if ((nameSize = WideCharToMultiByte(CP_ACP, 0, lpName, -1, name, 0, - NULL, NULL)) == 0) + nullptr, nullptr)) == 0) { ERROR("WideCharToMultiByte failed\n"); SetLastError(ERROR_INVALID_PARAMETER); @@ -277,25 +306,25 @@ SetEnvironmentVariableW( } name = (PCHAR)PAL_malloc(sizeof(CHAR)* nameSize); - if (name == NULL) + if (name == nullptr) { ERROR("malloc failed\n"); SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto done; } - if ( 0 == WideCharToMultiByte(CP_ACP, 0, lpName, -1, - name, nameSize, NULL, NULL ) ) + if (0 == WideCharToMultiByte(CP_ACP, 0, lpName, -1, + name, nameSize, nullptr, nullptr)) { - ASSERT( "WideCharToMultiByte returned 0\n" ); - SetLastError( ERROR_INTERNAL_ERROR ); + ASSERT("WideCharToMultiByte returned 0\n"); + SetLastError(ERROR_INTERNAL_ERROR); goto done; } - if ( NULL != lpValue ) + if (lpValue != nullptr) { if ((valueSize = WideCharToMultiByte(CP_ACP, 0, lpValue, -1, value, - 0, NULL, NULL)) == 0) + 0, nullptr, nullptr)) == 0) { ERROR("WideCharToMultiByte failed\n"); SetLastError(ERROR_INVALID_PARAMETER); @@ -303,35 +332,33 @@ SetEnvironmentVariableW( } value = (PCHAR)PAL_malloc(sizeof(CHAR)*valueSize); - - if ( NULL == value ) + + if (value == nullptr) { ERROR("malloc failed\n"); SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto done; } - - if ( 0 == WideCharToMultiByte( CP_ACP, 0, lpValue, -1, - value, valueSize, NULL, NULL ) ) + + if (0 == WideCharToMultiByte(CP_ACP, 0, lpValue, -1, + value, valueSize, nullptr, nullptr)) { ASSERT("WideCharToMultiByte failed\n"); SetLastError( ERROR_INTERNAL_ERROR ); goto done; } } - bRet = SetEnvironmentVariableA(name, value); done: PAL_free(value); PAL_free(name); - + LOGEXIT("SetEnvironmentVariableW returning BOOL %d\n", bRet); PERF_EXIT(SetEnvironmentVariableW); return bRet; } - /*++ Function: GetEnvironmentStringsW @@ -363,13 +390,14 @@ PALAPI GetEnvironmentStringsW( VOID) { - WCHAR *wenviron = NULL, *tempEnviron; + WCHAR *wenviron = nullptr, *tempEnviron; int i, len, envNum; PERF_ENTRY(GetEnvironmentStringsW); ENTRY("GetEnvironmentStringsW()\n"); - PALCEnterCriticalSection(&gcsEnvironment); + CPalThread * pthrCurrent = InternalGetCurrentThread(); + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); envNum = 0; len = 0; @@ -382,7 +410,7 @@ GetEnvironmentStringsW( } wenviron = (WCHAR *)PAL_malloc(sizeof(WCHAR)* (envNum + 1)); - if (wenviron == NULL) + if (wenviron == nullptr) { ERROR("malloc failed\n"); SetLastError(ERROR_NOT_ENOUGH_MEMORY); @@ -398,17 +426,16 @@ GetEnvironmentStringsW( envNum -= len; } - *tempEnviron = 0; /* Put an extra NULL at the end */ - + *tempEnviron = 0; /* Put an extra null at the end */ + EXIT: - PALCLeaveCriticalSection(&gcsEnvironment); + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); LOGEXIT("GetEnvironmentStringsW returning %p\n", wenviron); PERF_EXIT(GetEnvironmentStringsW); return wenviron; } - /*++ Function: GetEnvironmentStringsA @@ -421,13 +448,14 @@ PALAPI GetEnvironmentStringsA( VOID) { - char *environ = NULL, *tempEnviron; + char *environ = nullptr, *tempEnviron; int i, len, envNum; PERF_ENTRY(GetEnvironmentStringsA); ENTRY("GetEnvironmentStringsA()\n"); - PALCEnterCriticalSection(&gcsEnvironment); + CPalThread * pthrCurrent = InternalGetCurrentThread(); + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); envNum = 0; len = 0; @@ -440,7 +468,7 @@ GetEnvironmentStringsA( } environ = (char *)PAL_malloc(envNum + 1); - if (environ == NULL) + if (environ == nullptr) { ERROR("malloc failed\n"); SetLastError(ERROR_NOT_ENOUGH_MEMORY); @@ -457,17 +485,16 @@ GetEnvironmentStringsA( envNum -= len; } - *tempEnviron = 0; /* Put an extra NULL at the end */ - + *tempEnviron = 0; /* Put an extra null at the end */ + EXIT: - PALCLeaveCriticalSection(&gcsEnvironment); + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); LOGEXIT("GetEnvironmentStringsA returning %p\n", environ); PERF_EXIT(GetEnvironmentStringsA); return environ; } - /*++ Function: FreeEnvironmentStringsW @@ -499,19 +526,18 @@ FreeEnvironmentStringsW( IN LPWSTR lpValue) { PERF_ENTRY(FreeEnvironmentStringsW); - ENTRY("FreeEnvironmentStringsW(lpValue=%p (%S))\n", lpValue?lpValue:W16_NULLSTRING, lpValue?lpValue:W16_NULLSTRING); + ENTRY("FreeEnvironmentStringsW(lpValue=%p (%S))\n", lpValue ? lpValue : W16_NULLSTRING, lpValue ? lpValue : W16_NULLSTRING); - if (lpValue != NULL) + if (lpValue != nullptr) { PAL_free(lpValue); } LOGEXIT("FreeEnvironmentStringW returning BOOL TRUE\n"); PERF_EXIT(FreeEnvironmentStringsW); - return TRUE ; + return TRUE; } - /*++ Function: FreeEnvironmentStringsA @@ -525,19 +551,18 @@ FreeEnvironmentStringsA( IN LPSTR lpValue) { PERF_ENTRY(FreeEnvironmentStringsA); - ENTRY("FreeEnvironmentStringsA(lpValue=%p (%s))\n", lpValue?lpValue:"NULL", lpValue?lpValue:"NULL"); + ENTRY("FreeEnvironmentStringsA(lpValue=%p (%s))\n", lpValue ? lpValue : "NULL", lpValue ? lpValue : "NULL"); - if (lpValue != NULL) + if (lpValue != nullptr) { PAL_free(lpValue); } LOGEXIT("FreeEnvironmentStringA returning BOOL TRUE\n"); PERF_EXIT(FreeEnvironmentStringsA); - return TRUE ; + return TRUE; } - /*++ Function: SetEnvironmentVariableA @@ -551,11 +576,11 @@ lpName [in] Pointer to a null-terminated string that specifies the environment variable whose value is being set. The operating system creates the environment variable if it does not exist - and lpValue is not NULL. + and lpValue is not null. lpValue [in] Pointer to a null-terminated string containing the new value of the specified environment variable. If this parameter - is NULL, the variable is deleted from the current process's + is null, the variable is deleted from the current process's environment. Return Values @@ -574,59 +599,62 @@ environment variables of other processes. BOOL PALAPI SetEnvironmentVariableA( - IN LPCSTR lpName, - IN LPCSTR lpValue) + IN LPCSTR lpName, + IN LPCSTR lpValue) { BOOL bRet = FALSE; int nResult =0; PERF_ENTRY(SetEnvironmentVariableA); ENTRY("SetEnvironmentVariableA(lpName=%p (%s), lpValue=%p (%s))\n", - lpName?lpName:"NULL", - lpName?lpName:"NULL", lpValue?lpValue:"NULL", lpValue?lpValue:"NULL"); + lpName ? lpName : "NULL", lpName ? lpName : "NULL", + lpValue ? lpValue : "NULL", lpValue ? lpValue : "NULL"); - /*check if the input variable name is null - * and if so exit*/ - if ((lpName == NULL) || (lpName[0] == 0)) + // exit if the input variable name is null + if ((lpName == nullptr) || (lpName[0] == 0)) { - ERROR("lpName is NULL\n"); + ERROR("lpName is null\n"); goto done; } - /*check if the input value is null and if so + + /* check if the input value is null and if so * check if the input name is valid and delete - * the variable name from process environment*/ - if (lpValue == NULL) + * the variable name from process environment */ + if (lpValue == nullptr) { - if ((lpValue = MiscGetenv(lpName)) == NULL) + // We tell EnvironGetenv not to bother with making a copy of the + // value since we're not going to use it for anything interesting + // apart from checking whether it's null. + if ((lpValue = EnvironGetenv(lpName, /* copyValue */ FALSE)) == nullptr) { ERROR("Couldn't find environment variable (%s)\n", lpName); SetLastError(ERROR_ENVVAR_NOT_FOUND); goto done; } - MiscUnsetenv(lpName); + + EnvironUnsetenv(lpName); } - /*All the conditions are met. Set the variable*/ else { + // All the conditions are met. Set the variable. int iLen = strlen(lpName) + strlen(lpValue) + 2; LPSTR string = (LPSTR) PAL_malloc(iLen); - if (string == NULL) + if (string == nullptr) { bRet = FALSE; ERROR("Unable to allocate memory\n"); SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto done; } - + sprintf_s(string, iLen, "%s=%s", lpName, lpValue); - nResult = MiscPutenv(string, FALSE) ? 0 : -1; + nResult = EnvironPutenv(string, FALSE) ? 0 : -1; PAL_free(string); - string = NULL; - - // If MiscPutenv returns FALSE, it almost certainly failed to - // allocate memory. - if(nResult == -1) + string = nullptr; + + // If EnvironPutenv returns FALSE, it almost certainly failed to allocate memory. + if (nResult == -1) { bRet = FALSE; ERROR("Unable to allocate memory\n"); @@ -634,11 +662,435 @@ SetEnvironmentVariableA( goto done; } } + bRet = TRUE; + done: - LOGEXIT("SetEnvironmentVariableA returning BOOL %d\n", bRet); + LOGEXIT("SetEnvironmentVariableA returning BOOL %d\n", bRet); PERF_EXIT(SetEnvironmentVariableA); return bRet; } +/*++ +Function: + ResizeEnvironment + +Resizes the PAL environment buffer. + +Parameters + + newSize + [in] New size of palEnvironment + +Return Values + + TRUE on success, FALSE otherwise + +--*/ +BOOL ResizeEnvironment(int newSize) +{ + CPalThread * pthrCurrent = InternalGetCurrentThread(); + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); + + BOOL ret = FALSE; + if (newSize >= palEnvironmentCount) + { + // If palEnvironment is null, realloc acts like malloc. + char **newEnvironment = (char**)realloc(palEnvironment, newSize * sizeof(char *)); + if (newEnvironment != nullptr) + { + // realloc succeeded, so set palEnvironment to what it returned. + palEnvironment = newEnvironment; + palEnvironmentCapacity = newSize; + ret = TRUE; + } + } + else + { + ASSERT("ResizeEnvironment: newSize < current palEnvironmentCount!\n"); + } + + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); + return ret; +} + +/*++ +Function: + EnvironUnsetenv + +Remove the environment variable with the given name from the PAL version +of the environment if it exists. + +Parameters + + name + [in] Name of variable to unset. + +--*/ +void EnvironUnsetenv(const char *name) +{ + int nameLength = strlen(name); + + CPalThread * pthrCurrent = InternalGetCurrentThread(); + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); + + for (int i = 0; palEnvironment[i] != nullptr; ++i) + { + const char *equalsSignPosition = strchr(palEnvironment[i], '='); + if (equalsSignPosition == nullptr) + { + equalsSignPosition = palEnvironment[i] + strlen(palEnvironment[i]); + } + + // Check whether the name of this variable has the same length as the one + // we're looking for before proceeding to compare them. + if (equalsSignPosition - palEnvironment[i] == nameLength) + { + if (memcmp(name, palEnvironment[i], nameLength) == 0) + { + // Free the string we're removing. + InternalFree(palEnvironment[i]); + + // Move the last environment variable pointer here. + palEnvironment[i] = palEnvironment[palEnvironmentCount - 1]; + palEnvironment[palEnvironmentCount - 1] = nullptr; + + palEnvironmentCount--; + } + } + } + + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); +} + +/*++ +Function: + EnvironPutenv + +Add the environment variable string provided to the PAL version +of the environment. + +Parameters + + entry + [in] The variable string to add. Should be in the format + "name=value", where value might be empty (see below). + deleteIfEmpty + [in] If this is TRUE, "name=" will unset the 'name' variable. + +Return Values + + TRUE on success, FALSE otherwise + +--*/ +BOOL EnvironPutenv(const char* entry, BOOL deleteIfEmpty) +{ + BOOL result = FALSE; + + bool fOwningCS = false; + + CPalThread * pthrCurrent = InternalGetCurrentThread(); + + const char *equalsSignPosition = strchr(entry, '='); + if (equalsSignPosition == entry || equalsSignPosition == nullptr) + { + // "=foo" and "foo" have no meaning + return FALSE; + } + + char* copy = strdup(entry); + if (copy == nullptr) + { + return FALSE; + } + + int nameLength = equalsSignPosition - entry; + + if (equalsSignPosition[1] == '\0' && deleteIfEmpty) + { + // "foo=" removes foo from the environment in _putenv() on Windows. + // The same string can result from a call to SetEnvironmentVariable() + // with the empty string as the value, but in that case we want to + // set the variable's value to "". deleteIfEmpty will be FALSE in + // that case. + + // Change '=' to '\0' + copy[nameLength] = '\0'; + + EnvironUnsetenv(copy); + InternalFree(copy); + + result = TRUE; + } + else + { + // See if we are replacing an item or adding one. + + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); + fOwningCS = true; + + int i; + for (i = 0; palEnvironment[i] != nullptr; i++) + { + const char *existingEquals = strchr(palEnvironment[i], '='); + if (existingEquals == nullptr) + { + // The PAL screens out malformed strings, but the strings which + // came from the system during initialization might not have the + // equals sign. We treat the entire string as a name in that case. + existingEquals = palEnvironment[i] + strlen(palEnvironment[i]); + } + + if (existingEquals - palEnvironment[i] == nameLength) + { + if (memcmp(entry, palEnvironment[i], nameLength) == 0) + { + InternalFree(palEnvironment[i]); + palEnvironment[i] = copy; + + result = TRUE; + break; + } + } + } + + if (palEnvironment[i] == nullptr) + { + _ASSERTE(i < palEnvironmentCapacity); + if (i == (palEnvironmentCapacity - 1)) + { + // We found the first null, but it's the last element in our environment + // block. We need more space in our environment, so let's double its size. + int resizeRet = ResizeEnvironment(palEnvironmentCapacity * 2); + if (resizeRet != TRUE) + { + InternalFree(copy); + goto done; + } + } + + _ASSERTE(copy != nullptr); + palEnvironment[i] = copy; + palEnvironment[i + 1] = nullptr; + palEnvironmentCount++; + + result = TRUE; + } + } +done: + + if (fOwningCS) + { + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); + } + + return result; +} + +/*++ +Function: + EnvironGetenv + +Get the value of environment variable with the given name. + +Parameters + + name + [in] The name of the environment variable to get. + copyValue + [in] If this is TRUE, the function will make a copy of the + value and return a pointer to that. Otherwise, it will + return a pointer to the value in the PAL environment + directly. Calling this function with copyValue set to + FALSE is therefore unsafe without taking special pre- + cautions since the pointer may point to garbage later. + +Return Value + + A pointer to the value of the environment variable if it exists, + or nullptr otherwise. + +--*/ +char* EnvironGetenv(const char* name, BOOL copyValue) +{ + char *retValue = nullptr; + + CPalThread * pthrCurrent = InternalGetCurrentThread(); + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); + + int nameLength = strlen(name); + for (int i = 0; palEnvironment[i] != nullptr; ++i) + { + if (strlen(palEnvironment[i]) < nameLength) + { + continue; + } + + if (memcmp(palEnvironment[i], name, nameLength) == 0) + { + char *equalsSignPosition = palEnvironment[i] + nameLength; + + // If this is one of the variables which has no equals sign, we + // treat the whole thing as name, so the value is an empty string. + if (*equalsSignPosition == '\0') + { + retValue = (char *)""; + break; + } + else if (*equalsSignPosition == '=') + { + retValue = equalsSignPosition + 1; + break; + } + } + } + + if ((retValue != nullptr) && copyValue) + { + retValue = strdup(retValue); + } + + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); + return retValue; +} + +/*++ +Function: + EnvironGetSystemEnvironment + +Get a pointer to the array of pointers representing the process's +environment. + +See 'man environ' for details. + +Return Value + A pointer to the environment. + +--*/ +char** EnvironGetSystemEnvironment() +{ + char** sysEnviron; + +#if HAVE__NSGETENVIRON + sysEnviron = *(_NSGetEnviron()); +#else // HAVE__NSGETENVIRON + extern char **environ; + sysEnviron = environ; +#endif // HAVE__NSGETENVIRON + + return sysEnviron; +} + +/*++ +Function: + EnvironInitialize + +Initialization function called from PAL_Initialize. + +Note: This is called before debug channels are initialized, so it + cannot use debug tracing calls. +--*/ +BOOL +EnvironInitialize(void) +{ + BOOL ret = FALSE; + + InternalInitializeCriticalSection(&gcsEnvironment); + + CPalThread * pthrCurrent = InternalGetCurrentThread(); + InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); + + char** sourceEnviron = EnvironGetSystemEnvironment(); + + int variableCount = 0; + while (sourceEnviron[variableCount] != nullptr) + variableCount++; + + palEnvironmentCount = 0; + + // We need to decide how much space to allocate. Since we need enough + // space for all of the 'n' current environment variables, but we don't + // know how many more there will be, we will initially make room for + // '2n' variables. If even more are added, we will resize again. + // If there are no variables, we will still make room for 1 entry to + // store a nullptr there. + int initialSize = (variableCount == 0) ? 1 : variableCount * 2; + + ret = ResizeEnvironment(initialSize); + if (ret == TRUE) + { + _ASSERTE(palEnvironment != nullptr); + for (int i = 0; i < variableCount; ++i) + { + palEnvironment[i] = strdup(sourceEnviron[i]); + palEnvironmentCount++; + } + + // Set the entry after the last variable to null to indicate the end. + palEnvironment[variableCount] = nullptr; + } + + InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); + return ret; +} + +/*++ + +Function : _putenv. + +See MSDN for more details. + +Note: The BSD implementation can cause + memory leaks. See man pages for more details. +--*/ +int +__cdecl +_putenv( const char * envstring ) +{ + int ret = -1; + + PERF_ENTRY(_putenv); + ENTRY( "_putenv( %p (%s) )\n", envstring ? envstring : "NULL", envstring ? envstring : "NULL") ; + + if (envstring != nullptr) + { + ret = EnvironPutenv(envstring, TRUE) ? 0 : -1; + } + else + { + ERROR( "_putenv() called with NULL envstring!\n"); + } + + LOGEXIT( "_putenv returning %d\n", ret); + PERF_EXIT(_putenv); + return ret; +} + +/*++ + +Function : PAL_getenv + +See MSDN for more details. +--*/ +char * __cdecl PAL_getenv(const char *varname) +{ + char *retval; + + PERF_ENTRY(getenv); + ENTRY("getenv (%p (%s))\n", varname ? varname : "NULL", varname ? varname : "NULL"); + + if (strcmp(varname, "") == 0) + { + ERROR("getenv called with a empty variable name\n"); + LOGEXIT("getenv returning NULL\n"); + PERF_EXIT(getenv); + return(NULL); + } + + retval = EnvironGetenv(varname); + + LOGEXIT("getenv returning %p\n", retval); + PERF_EXIT(getenv); + return(retval); +} diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp index 4d40781ca6cf..b2f3703c837f 100644 --- a/src/pal/src/thread/process.cpp +++ b/src/pal/src/thread/process.cpp @@ -30,7 +30,7 @@ Module Name: #include "pal/critsect.h" #include "pal/dbgmsg.h" #include "pal/utils.h" -#include "pal/misc.h" +#include "pal/environ.h" #include "pal/virtual.h" #include "pal/stackstring.hpp" @@ -4357,19 +4357,13 @@ getPath( } pThread = InternalGetCurrentThread(); + /* Then try to look in the path */ - int iLen2 = strlen(MiscGetenv("PATH"))+1; - lpPath = (LPSTR) InternalMalloc(iLen2); + lpPath = EnvironGetenv("PATH"); if (!lpPath) { - ERROR("couldn't allocate memory for $PATH\n"); - return FALSE; - } - - if (strcpy_s(lpPath, iLen2, MiscGetenv("PATH")) != SAFECRT_SUCCESS) - { - ERROR("strcpy_s failed!"); + ERROR("EnvironGetenv returned NULL for $PATH\n"); return FALSE; } diff --git a/src/pal/src/thread/thread.cpp b/src/pal/src/thread/thread.cpp index 07f6c8b680bb..0bef8111dc08 100644 --- a/src/pal/src/thread/thread.cpp +++ b/src/pal/src/thread/thread.cpp @@ -30,7 +30,7 @@ Module Name: #include "pal/process.h" #include "pal/module.h" #include "pal/dbgmsg.h" -#include "pal/misc.h" +#include "pal/environ.h" #include "pal/init.h" #include @@ -1680,7 +1680,7 @@ CorUnix::InitializeGlobalThreadData( // Read in the environment to see whether we need to change the default // thread stack size. // - pszStackSize = MiscGetenv(PAL_THREAD_DEFAULT_STACK_SIZE); + pszStackSize = EnvironGetenv(PAL_THREAD_DEFAULT_STACK_SIZE); if (NULL != pszStackSize) { // Environment variable exists @@ -1690,6 +1690,8 @@ CorUnix::InitializeGlobalThreadData( { CPalThread::s_dwDefaultThreadStackSize = dw; } + + InternalFree(pszStackSize); } #if !HAVE_MACH_EXCEPTIONS