|  | /* | 
|  | * kmp_environment.cpp -- Handle environment variables OS-independently. | 
|  | */ | 
|  |  | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | /* We use GetEnvironmentVariable for Windows* OS instead of getenv because the | 
|  | act of loading a DLL on Windows* OS makes any user-set environment variables | 
|  | (i.e. with putenv()) unavailable.  getenv() apparently gets a clean copy of | 
|  | the env variables as they existed at the start of the run. JH 12/23/2002 | 
|  |  | 
|  | On Windows* OS, there are two environments (at least, see below): | 
|  |  | 
|  | 1. Environment maintained by Windows* OS on IA-32 architecture. Accessible | 
|  | through GetEnvironmentVariable(), SetEnvironmentVariable(), and | 
|  | GetEnvironmentStrings(). | 
|  |  | 
|  | 2. Environment maintained by C RTL. Accessible through getenv(), putenv(). | 
|  |  | 
|  | putenv() function updates both C and Windows* OS on IA-32 architecture. | 
|  | getenv() function search for variables in C RTL environment only. | 
|  | Windows* OS on IA-32 architecture functions work *only* with Windows* OS on | 
|  | IA-32 architecture. | 
|  |  | 
|  | Windows* OS on IA-32 architecture maintained by OS, so there is always only | 
|  | one Windows* OS on IA-32 architecture per process. Changes in Windows* OS on | 
|  | IA-32 architecture are process-visible. | 
|  |  | 
|  | C environment maintained by C RTL. Multiple copies of C RTL may be present | 
|  | in the process, and each C RTL maintains its own environment. :-( | 
|  |  | 
|  | Thus, proper way to work with environment on Windows* OS is: | 
|  |  | 
|  | 1. Set variables with putenv() function -- both C and Windows* OS on IA-32 | 
|  | architecture are being updated. Windows* OS on IA-32 architecture may be | 
|  | considered primary target, while updating C RTL environment is free bonus. | 
|  |  | 
|  | 2. Get variables with GetEnvironmentVariable() -- getenv() does not | 
|  | search Windows* OS on IA-32 architecture, and can not see variables | 
|  | set with SetEnvironmentVariable(). | 
|  |  | 
|  | 2007-04-05 -- lev | 
|  | */ | 
|  |  | 
|  | #include "kmp_environment.h" | 
|  |  | 
|  | #include "kmp.h" // | 
|  | #include "kmp_i18n.h" | 
|  | #include "kmp_os.h" // KMP_OS_*. | 
|  | #include "kmp_str.h" // __kmp_str_*(). | 
|  |  | 
|  | #if KMP_OS_UNIX | 
|  | #include <stdlib.h> // getenv, setenv, unsetenv. | 
|  | #include <string.h> // strlen, strcpy. | 
|  | #if KMP_OS_DARWIN | 
|  | #include <crt_externs.h> | 
|  | #define environ (*_NSGetEnviron()) | 
|  | #else | 
|  | extern char **environ; | 
|  | #endif | 
|  | #elif KMP_OS_WINDOWS | 
|  | #include <windows.h> // GetEnvironmentVariable, SetEnvironmentVariable, | 
|  | // GetLastError. | 
|  | #else | 
|  | #error Unknown or unsupported OS. | 
|  | #endif | 
|  |  | 
|  | // TODO: Eliminate direct memory allocations, use string operations instead. | 
|  |  | 
|  | static inline void *allocate(size_t size) { | 
|  | void *ptr = KMP_INTERNAL_MALLOC(size); | 
|  | if (ptr == NULL) { | 
|  | KMP_FATAL(MemoryAllocFailed); | 
|  | } | 
|  | return ptr; | 
|  | } // allocate | 
|  |  | 
|  | char *__kmp_env_get(char const *name) { | 
|  |  | 
|  | char *result = NULL; | 
|  |  | 
|  | #if KMP_OS_UNIX | 
|  | char const *value = getenv(name); | 
|  | if (value != NULL) { | 
|  | size_t len = KMP_STRLEN(value) + 1; | 
|  | result = (char *)KMP_INTERNAL_MALLOC(len); | 
|  | if (result == NULL) { | 
|  | KMP_FATAL(MemoryAllocFailed); | 
|  | } | 
|  | KMP_STRNCPY_S(result, len, value, len); | 
|  | } | 
|  | #elif KMP_OS_WINDOWS | 
|  | /* We use GetEnvironmentVariable for Windows* OS instead of getenv because the | 
|  | act of loading a DLL on Windows* OS makes any user-set environment | 
|  | variables (i.e. with putenv()) unavailable. getenv() apparently gets a | 
|  | clean copy of the env variables as they existed at the start of the run. | 
|  | JH 12/23/2002 */ | 
|  | DWORD rc; | 
|  | rc = GetEnvironmentVariable(name, NULL, 0); | 
|  | if (!rc) { | 
|  | DWORD error = GetLastError(); | 
|  | if (error != ERROR_ENVVAR_NOT_FOUND) { | 
|  | __kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), __kmp_msg_null); | 
|  | } | 
|  | // Variable is not found, it's ok, just continue. | 
|  | } else { | 
|  | DWORD len = rc; | 
|  | result = (char *)KMP_INTERNAL_MALLOC(len); | 
|  | if (result == NULL) { | 
|  | KMP_FATAL(MemoryAllocFailed); | 
|  | } | 
|  | rc = GetEnvironmentVariable(name, result, len); | 
|  | if (!rc) { | 
|  | // GetEnvironmentVariable() may return 0 if variable is empty. | 
|  | // In such a case GetLastError() returns ERROR_SUCCESS. | 
|  | DWORD error = GetLastError(); | 
|  | if (error != ERROR_SUCCESS) { | 
|  | // Unexpected error. The variable should be in the environment, | 
|  | // and buffer should be large enough. | 
|  | __kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), | 
|  | __kmp_msg_null); | 
|  | KMP_INTERNAL_FREE((void *)result); | 
|  | result = NULL; | 
|  | } | 
|  | } | 
|  | } | 
|  | #else | 
|  | #error Unknown or unsupported OS. | 
|  | #endif | 
|  |  | 
|  | return result; | 
|  |  | 
|  | } // func __kmp_env_get | 
|  |  | 
|  | // TODO: Find and replace all regular free() with __kmp_env_free(). | 
|  |  | 
|  | void __kmp_env_free(char const **value) { | 
|  |  | 
|  | KMP_DEBUG_ASSERT(value != NULL); | 
|  | KMP_INTERNAL_FREE(CCAST(char *, *value)); | 
|  | *value = NULL; | 
|  |  | 
|  | } // func __kmp_env_free | 
|  |  | 
|  | int __kmp_env_exists(char const *name) { | 
|  |  | 
|  | #if KMP_OS_UNIX | 
|  | char const *value = getenv(name); | 
|  | return ((value == NULL) ? (0) : (1)); | 
|  | #elif KMP_OS_WINDOWS | 
|  | DWORD rc; | 
|  | rc = GetEnvironmentVariable(name, NULL, 0); | 
|  | if (rc == 0) { | 
|  | DWORD error = GetLastError(); | 
|  | if (error != ERROR_ENVVAR_NOT_FOUND) { | 
|  | __kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), __kmp_msg_null); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  | return 1; | 
|  | #else | 
|  | #error Unknown or unsupported OS. | 
|  | #endif | 
|  |  | 
|  | } // func __kmp_env_exists | 
|  |  | 
|  | void __kmp_env_set(char const *name, char const *value, int overwrite) { | 
|  |  | 
|  | #if KMP_OS_UNIX | 
|  | int rc = setenv(name, value, overwrite); | 
|  | if (rc != 0) { | 
|  | // Dead code. I tried to put too many variables into Linux* OS | 
|  | // environment on IA-32 architecture. When application consumes | 
|  | // more than ~2.5 GB of memory, entire system feels bad. Sometimes | 
|  | // application is killed (by OS?), sometimes system stops | 
|  | // responding... But this error message never appears. --ln | 
|  | __kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_HNT(NotEnoughMemory), | 
|  | __kmp_msg_null); | 
|  | } | 
|  | #elif KMP_OS_WINDOWS | 
|  | BOOL rc; | 
|  | if (!overwrite) { | 
|  | rc = GetEnvironmentVariable(name, NULL, 0); | 
|  | if (rc) { | 
|  | // Variable exists, do not overwrite. | 
|  | return; | 
|  | } | 
|  | DWORD error = GetLastError(); | 
|  | if (error != ERROR_ENVVAR_NOT_FOUND) { | 
|  | __kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), __kmp_msg_null); | 
|  | } | 
|  | } | 
|  | rc = SetEnvironmentVariable(name, value); | 
|  | if (!rc) { | 
|  | DWORD error = GetLastError(); | 
|  | __kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_ERR(error), __kmp_msg_null); | 
|  | } | 
|  | #else | 
|  | #error Unknown or unsupported OS. | 
|  | #endif | 
|  |  | 
|  | } // func __kmp_env_set | 
|  |  | 
|  | void __kmp_env_unset(char const *name) { | 
|  |  | 
|  | #if KMP_OS_UNIX | 
|  | unsetenv(name); | 
|  | #elif KMP_OS_WINDOWS | 
|  | BOOL rc = SetEnvironmentVariable(name, NULL); | 
|  | if (!rc) { | 
|  | DWORD error = GetLastError(); | 
|  | __kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_ERR(error), __kmp_msg_null); | 
|  | } | 
|  | #else | 
|  | #error Unknown or unsupported OS. | 
|  | #endif | 
|  |  | 
|  | } // func __kmp_env_unset | 
|  |  | 
|  | /* Intel OpenMP RTL string representation of environment: just a string of | 
|  | characters, variables are separated with vertical bars, e. g.: | 
|  |  | 
|  | "KMP_WARNINGS=0|KMP_AFFINITY=compact|" | 
|  |  | 
|  | Empty variables are allowed and ignored: | 
|  |  | 
|  | "||KMP_WARNINGS=1||" | 
|  | */ | 
|  |  | 
|  | static void | 
|  | ___kmp_env_blk_parse_string(kmp_env_blk_t *block, // M: Env block to fill. | 
|  | char const *env // I: String to parse. | 
|  | ) { | 
|  |  | 
|  | char const chr_delimiter = '|'; | 
|  | char const str_delimiter[] = {chr_delimiter, 0}; | 
|  |  | 
|  | char *bulk = NULL; | 
|  | kmp_env_var_t *vars = NULL; | 
|  | int count = 0; // Number of used elements in vars array. | 
|  | int delimiters = 0; // Number of delimiters in input string. | 
|  |  | 
|  | // Copy original string, we will modify the copy. | 
|  | bulk = __kmp_str_format("%s", env); | 
|  |  | 
|  | // Loop thru all the vars in environment block. Count delimiters (maximum | 
|  | // number of variables is number of delimiters plus one). | 
|  | { | 
|  | char const *ptr = bulk; | 
|  | for (;;) { | 
|  | ptr = strchr(ptr, chr_delimiter); | 
|  | if (ptr == NULL) { | 
|  | break; | 
|  | } | 
|  | ++delimiters; | 
|  | ptr += 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Allocate vars array. | 
|  | vars = (kmp_env_var_t *)allocate((delimiters + 1) * sizeof(kmp_env_var_t)); | 
|  |  | 
|  | // Loop thru all the variables. | 
|  | { | 
|  | char *var; // Pointer to variable (both name and value). | 
|  | char *name; // Pointer to name of variable. | 
|  | char *value; // Pointer to value. | 
|  | char *buf; // Buffer for __kmp_str_token() function. | 
|  | var = __kmp_str_token(bulk, str_delimiter, &buf); // Get the first var. | 
|  | while (var != NULL) { | 
|  | // Save found variable in vars array. | 
|  | __kmp_str_split(var, '=', &name, &value); | 
|  | KMP_DEBUG_ASSERT(count < delimiters + 1); | 
|  | vars[count].name = name; | 
|  | vars[count].value = value; | 
|  | ++count; | 
|  | // Get the next var. | 
|  | var = __kmp_str_token(NULL, str_delimiter, &buf); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Fill out result. | 
|  | block->bulk = bulk; | 
|  | block->vars = vars; | 
|  | block->count = count; | 
|  | } | 
|  |  | 
|  | /* Windows* OS (actually, DOS) environment block is a piece of memory with | 
|  | environment variables. Each variable is terminated with zero byte, entire | 
|  | block is terminated with one extra zero byte, so we have two zero bytes at | 
|  | the end of environment block, e. g.: | 
|  |  | 
|  | "HOME=C:\\users\\lev\x00OS=Windows_NT\x00\x00" | 
|  |  | 
|  | It is not clear how empty environment is represented. "\x00\x00"? | 
|  | */ | 
|  |  | 
|  | #if KMP_OS_WINDOWS | 
|  | static void ___kmp_env_blk_parse_windows( | 
|  | kmp_env_blk_t *block, // M: Env block to fill. | 
|  | char const *env // I: Pointer to Windows* OS (DOS) environment block. | 
|  | ) { | 
|  |  | 
|  | char *bulk = NULL; | 
|  | kmp_env_var_t *vars = NULL; | 
|  | int count = 0; // Number of used elements in vars array. | 
|  | int size = 0; // Size of bulk. | 
|  |  | 
|  | char *name; // Pointer to name of variable. | 
|  | char *value; // Pointer to value. | 
|  |  | 
|  | if (env != NULL) { | 
|  |  | 
|  | // Loop thru all the vars in environment block. Count variables, find size | 
|  | // of block. | 
|  | { | 
|  | char const *var; // Pointer to beginning of var. | 
|  | int len; // Length of variable. | 
|  | count = 0; | 
|  | var = | 
|  | env; // The first variable starts and beginning of environment block. | 
|  | len = KMP_STRLEN(var); | 
|  | while (len != 0) { | 
|  | ++count; | 
|  | size = size + len + 1; | 
|  | var = var + len + | 
|  | 1; // Move pointer to the beginning of the next variable. | 
|  | len = KMP_STRLEN(var); | 
|  | } | 
|  | size = | 
|  | size + 1; // Total size of env block, including terminating zero byte. | 
|  | } | 
|  |  | 
|  | // Copy original block to bulk, we will modify bulk, not original block. | 
|  | bulk = (char *)allocate(size); | 
|  | KMP_MEMCPY_S(bulk, size, env, size); | 
|  | // Allocate vars array. | 
|  | vars = (kmp_env_var_t *)allocate(count * sizeof(kmp_env_var_t)); | 
|  |  | 
|  | // Loop thru all the vars, now in bulk. | 
|  | { | 
|  | char *var; // Pointer to beginning of var. | 
|  | int len; // Length of variable. | 
|  | count = 0; | 
|  | var = bulk; | 
|  | len = KMP_STRLEN(var); | 
|  | while (len != 0) { | 
|  | // Save variable in vars array. | 
|  | __kmp_str_split(var, '=', &name, &value); | 
|  | vars[count].name = name; | 
|  | vars[count].value = value; | 
|  | ++count; | 
|  | // Get the next var. | 
|  | var = var + len + 1; | 
|  | len = KMP_STRLEN(var); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Fill out result. | 
|  | block->bulk = bulk; | 
|  | block->vars = vars; | 
|  | block->count = count; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* Unix environment block is a array of pointers to variables, last pointer in | 
|  | array is NULL: | 
|  |  | 
|  | { "HOME=/home/lev", "TERM=xterm", NULL } | 
|  | */ | 
|  |  | 
|  | #if KMP_OS_UNIX | 
|  | static void | 
|  | ___kmp_env_blk_parse_unix(kmp_env_blk_t *block, // M: Env block to fill. | 
|  | char **env // I: Unix environment to parse. | 
|  | ) { | 
|  | char *bulk = NULL; | 
|  | kmp_env_var_t *vars = NULL; | 
|  | int count = 0; | 
|  | size_t size = 0; // Size of bulk. | 
|  |  | 
|  | // Count number of variables and length of required bulk. | 
|  | { | 
|  | while (env[count] != NULL) { | 
|  | size += KMP_STRLEN(env[count]) + 1; | 
|  | ++count; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Allocate memory. | 
|  | bulk = (char *)allocate(size); | 
|  | vars = (kmp_env_var_t *)allocate(count * sizeof(kmp_env_var_t)); | 
|  |  | 
|  | // Loop thru all the vars. | 
|  | { | 
|  | char *var; // Pointer to beginning of var. | 
|  | char *name; // Pointer to name of variable. | 
|  | char *value; // Pointer to value. | 
|  | size_t len; // Length of variable. | 
|  | int i; | 
|  | var = bulk; | 
|  | for (i = 0; i < count; ++i) { | 
|  | KMP_ASSERT(var < bulk + size); | 
|  | [[maybe_unused]] size_t ssize = size - (var - bulk); | 
|  | // Copy variable to bulk. | 
|  | len = KMP_STRLEN(env[i]); | 
|  | KMP_MEMCPY_S(var, ssize, env[i], len + 1); | 
|  | // Save found variable in vars array. | 
|  | __kmp_str_split(var, '=', &name, &value); | 
|  | vars[i].name = name; | 
|  | vars[i].value = value; | 
|  | // Move pointer. | 
|  | var += len + 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Fill out result. | 
|  | block->bulk = bulk; | 
|  | block->vars = vars; | 
|  | block->count = count; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | void __kmp_env_blk_init(kmp_env_blk_t *block, // M: Block to initialize. | 
|  | char const *bulk // I: Initialization string, or NULL. | 
|  | ) { | 
|  |  | 
|  | if (bulk != NULL) { | 
|  | ___kmp_env_blk_parse_string(block, bulk); | 
|  | } else { | 
|  | #if KMP_OS_UNIX | 
|  | ___kmp_env_blk_parse_unix(block, environ); | 
|  | #elif KMP_OS_WINDOWS | 
|  | { | 
|  | char *mem = GetEnvironmentStrings(); | 
|  | if (mem == NULL) { | 
|  | DWORD error = GetLastError(); | 
|  | __kmp_fatal(KMP_MSG(CantGetEnvironment), KMP_ERR(error), | 
|  | __kmp_msg_null); | 
|  | } | 
|  | ___kmp_env_blk_parse_windows(block, mem); | 
|  | FreeEnvironmentStrings(mem); | 
|  | } | 
|  | #else | 
|  | #error Unknown or unsupported OS. | 
|  | #endif | 
|  | } | 
|  |  | 
|  | } // __kmp_env_blk_init | 
|  |  | 
|  | static int ___kmp_env_var_cmp( // Comparison function for qsort(). | 
|  | kmp_env_var_t const *lhs, kmp_env_var_t const *rhs) { | 
|  | return strcmp(lhs->name, rhs->name); | 
|  | } | 
|  |  | 
|  | void __kmp_env_blk_sort( | 
|  | kmp_env_blk_t *block // M: Block of environment variables to sort. | 
|  | ) { | 
|  |  | 
|  | qsort(CCAST(kmp_env_var_t *, block->vars), block->count, | 
|  | sizeof(kmp_env_var_t), | 
|  | (int (*)(void const *, void const *)) & ___kmp_env_var_cmp); | 
|  |  | 
|  | } // __kmp_env_block_sort | 
|  |  | 
|  | void __kmp_env_blk_free( | 
|  | kmp_env_blk_t *block // M: Block of environment variables to free. | 
|  | ) { | 
|  |  | 
|  | KMP_INTERNAL_FREE(CCAST(kmp_env_var_t *, block->vars)); | 
|  | __kmp_str_free(&(block->bulk)); | 
|  |  | 
|  | block->count = 0; | 
|  | block->vars = NULL; | 
|  |  | 
|  | } // __kmp_env_blk_free | 
|  |  | 
|  | char const * // R: Value of variable or NULL if variable does not exist. | 
|  | __kmp_env_blk_var(kmp_env_blk_t *block, // I: Block of environment variables. | 
|  | char const *name // I: Name of variable to find. | 
|  | ) { | 
|  |  | 
|  | int i; | 
|  | for (i = 0; i < block->count; ++i) { | 
|  | if (strcmp(block->vars[i].name, name) == 0) { | 
|  | return block->vars[i].value; | 
|  | } | 
|  | } | 
|  | return NULL; | 
|  |  | 
|  | } // __kmp_env_block_var | 
|  |  | 
|  | // end of file // |