summaryrefslogtreecommitdiff
path: root/runtime/src/kmp_environment.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/src/kmp_environment.cpp')
-rw-r--r--runtime/src/kmp_environment.cpp501
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 //