* kmp_environment.cpp -- Handle environment variables OS-independently.
*/
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"
#include "kmp_str.h"
#if KMP_OS_UNIX
#include <stdlib.h>
#include <string.h>
#if KMP_OS_DARWIN
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#else
extern char **environ;
#endif
#elif KMP_OS_WINDOWS
#include <windows.h>
#else
#error Unknown or unsupported OS.
#endif
static inline void *allocate(size_t size) {
void *ptr = KMP_INTERNAL_MALLOC(size);
if (ptr == NULL) {
KMP_FATAL(MemoryAllocFailed);
}
return ptr;
}
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
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);
}
} else {
DWORD len = rc;
result = (char *)KMP_INTERNAL_MALLOC(len);
if (result == NULL) {
KMP_FATAL(MemoryAllocFailed);
}
rc = GetEnvironmentVariable(name, result, len);
if (!rc) {
DWORD error = GetLastError();
if (error != ERROR_SUCCESS) {
__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;
}
void __kmp_env_free(char const **value) {
KMP_DEBUG_ASSERT(value != NULL);
KMP_INTERNAL_FREE(CCAST(char *, *value));
*value = NULL;
}
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
}
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) {
__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) {
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
}
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
}
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,
char const *env
) {
char const chr_delimiter = '|';
char const str_delimiter[] = {chr_delimiter, 0};
char *bulk = NULL;
kmp_env_var_t *vars = NULL;
int count = 0;
int delimiters = 0;
bulk = __kmp_str_format("%s", env);
{
char const *ptr = bulk;
for (;;) {
ptr = strchr(ptr, chr_delimiter);
if (ptr == NULL) {
break;
}
++delimiters;
ptr += 1;
}
}
vars = (kmp_env_var_t *)allocate((delimiters + 1) * sizeof(kmp_env_var_t));
{
char *var;
char *name;
char *value;
char *buf;
var = __kmp_str_token(bulk, str_delimiter, &buf);
while (var != NULL) {
__kmp_str_split(var, '=', &name, &value);
KMP_DEBUG_ASSERT(count < delimiters + 1);
vars[count].name = name;
vars[count].value = value;
++count;
var = __kmp_str_token(NULL, str_delimiter, &buf);
}
}
block->bulk = bulk;
block->vars = vars;
block->count = count;
}
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,
char const *env
) {
char *bulk = NULL;
kmp_env_var_t *vars = NULL;
int count = 0;
int size = 0;
char *name;
char *value;
if (env != NULL) {
{
char const *var;
int len;
count = 0;
var =
env;
len = KMP_STRLEN(var);
while (len != 0) {
++count;
size = size + len + 1;
var = var + len +
1;
len = KMP_STRLEN(var);
}
size =
size + 1;
}
bulk = (char *)allocate(size);
KMP_MEMCPY_S(bulk, size, env, size);
vars = (kmp_env_var_t *)allocate(count * sizeof(kmp_env_var_t));
{
char *var;
int len;
count = 0;
var = bulk;
len = KMP_STRLEN(var);
while (len != 0) {
__kmp_str_split(var, '=', &name, &value);
vars[count].name = name;
vars[count].value = value;
++count;
var = var + len + 1;
len = KMP_STRLEN(var);
}
}
}
block->bulk = bulk;
block->vars = vars;
block->count = count;
}
#endif
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,
char **env
) {
char *bulk = NULL;
kmp_env_var_t *vars = NULL;
int count = 0;
size_t size = 0;
{
while (env[count] != NULL) {
size += KMP_STRLEN(env[count]) + 1;
++count;
}
}
bulk = (char *)allocate(size);
vars = (kmp_env_var_t *)allocate(count * sizeof(kmp_env_var_t));
{
char *var;
char *name;
char *value;
size_t len;
int i;
var = bulk;
for (i = 0; i < count; ++i) {
KMP_ASSERT(var < bulk + size);
[[maybe_unused]] size_t ssize = size - (var - bulk);
len = KMP_STRLEN(env[i]);
KMP_MEMCPY_S(var, ssize, env[i], len + 1);
__kmp_str_split(var, '=', &name, &value);
vars[i].name = name;
vars[i].value = value;
var += len + 1;
}
}
block->bulk = bulk;
block->vars = vars;
block->count = count;
}
#endif
void __kmp_env_blk_init(kmp_env_blk_t *block,
char const *bulk
) {
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
}
}
static int ___kmp_env_var_cmp(
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
) {
qsort(CCAST(kmp_env_var_t *, block->vars), block->count,
sizeof(kmp_env_var_t),
(int (*)(void const *, void const *)) & ___kmp_env_var_cmp);
}
void __kmp_env_blk_free(
kmp_env_blk_t *block
) {
KMP_INTERNAL_FREE(CCAST(kmp_env_var_t *, block->vars));
__kmp_str_free(&(block->bulk));
block->count = 0;
block->vars = NULL;
}
char const *
__kmp_env_blk_var(kmp_env_blk_t *block,
char const *name
) {
int i;
for (i = 0; i < block->count; ++i) {
if (strcmp(block->vars[i].name, name) == 0) {
return block->vars[i].value;
}
}
return NULL;
}