diff options
Diffstat (limited to 'runtime/src/kmp_environment.cpp')
-rw-r--r-- | runtime/src/kmp_environment.cpp | 501 |
1 files changed, 501 insertions, 0 deletions
diff --git a/runtime/src/kmp_environment.cpp b/runtime/src/kmp_environment.cpp new file mode 100644 index 0000000000000..22c2941133e1b --- /dev/null +++ b/runtime/src/kmp_environment.cpp @@ -0,0 +1,501 @@ +/* + * kmp_environment.cpp -- Handle environment variables OS-independently. + */ + +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.txt for details. +// +//===----------------------------------------------------------------------===// + +/* 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 } +*/ + +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; + int size = 0; // Size of bulk. + + // Count number of variables and length of required bulk. + { + count = 0; + size = 0; + 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. + int len; // Length of variable. + int i; + var = bulk; + for (i = 0; i < count; ++i) { + // Copy variable to bulk. + len = KMP_STRLEN(env[i]); + KMP_MEMCPY_S(var, size, 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; +} + +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 // |