Path: blob/main/contrib/llvm-project/openmp/runtime/src/kmp_environment.cpp
35258 views
/*1* kmp_environment.cpp -- Handle environment variables OS-independently.2*/34//===----------------------------------------------------------------------===//5//6// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.7// See https://llvm.org/LICENSE.txt for license information.8// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception9//10//===----------------------------------------------------------------------===//1112/* We use GetEnvironmentVariable for Windows* OS instead of getenv because the13act of loading a DLL on Windows* OS makes any user-set environment variables14(i.e. with putenv()) unavailable. getenv() apparently gets a clean copy of15the env variables as they existed at the start of the run. JH 12/23/20021617On Windows* OS, there are two environments (at least, see below):18191. Environment maintained by Windows* OS on IA-32 architecture. Accessible20through GetEnvironmentVariable(), SetEnvironmentVariable(), and21GetEnvironmentStrings().22232. Environment maintained by C RTL. Accessible through getenv(), putenv().2425putenv() function updates both C and Windows* OS on IA-32 architecture.26getenv() function search for variables in C RTL environment only.27Windows* OS on IA-32 architecture functions work *only* with Windows* OS on28IA-32 architecture.2930Windows* OS on IA-32 architecture maintained by OS, so there is always only31one Windows* OS on IA-32 architecture per process. Changes in Windows* OS on32IA-32 architecture are process-visible.3334C environment maintained by C RTL. Multiple copies of C RTL may be present35in the process, and each C RTL maintains its own environment. :-(3637Thus, proper way to work with environment on Windows* OS is:38391. Set variables with putenv() function -- both C and Windows* OS on IA-3240architecture are being updated. Windows* OS on IA-32 architecture may be41considered primary target, while updating C RTL environment is free bonus.42432. Get variables with GetEnvironmentVariable() -- getenv() does not44search Windows* OS on IA-32 architecture, and can not see variables45set with SetEnvironmentVariable().46472007-04-05 -- lev48*/4950#include "kmp_environment.h"5152#include "kmp.h" //53#include "kmp_i18n.h"54#include "kmp_os.h" // KMP_OS_*.55#include "kmp_str.h" // __kmp_str_*().5657#if KMP_OS_UNIX58#include <stdlib.h> // getenv, setenv, unsetenv.59#include <string.h> // strlen, strcpy.60#if KMP_OS_DARWIN61#include <crt_externs.h>62#define environ (*_NSGetEnviron())63#else64extern char **environ;65#endif66#elif KMP_OS_WINDOWS67#include <windows.h> // GetEnvironmentVariable, SetEnvironmentVariable,68// GetLastError.69#else70#error Unknown or unsupported OS.71#endif7273// TODO: Eliminate direct memory allocations, use string operations instead.7475static inline void *allocate(size_t size) {76void *ptr = KMP_INTERNAL_MALLOC(size);77if (ptr == NULL) {78KMP_FATAL(MemoryAllocFailed);79}80return ptr;81} // allocate8283char *__kmp_env_get(char const *name) {8485char *result = NULL;8687#if KMP_OS_UNIX88char const *value = getenv(name);89if (value != NULL) {90size_t len = KMP_STRLEN(value) + 1;91result = (char *)KMP_INTERNAL_MALLOC(len);92if (result == NULL) {93KMP_FATAL(MemoryAllocFailed);94}95KMP_STRNCPY_S(result, len, value, len);96}97#elif KMP_OS_WINDOWS98/* We use GetEnvironmentVariable for Windows* OS instead of getenv because the99act of loading a DLL on Windows* OS makes any user-set environment100variables (i.e. with putenv()) unavailable. getenv() apparently gets a101clean copy of the env variables as they existed at the start of the run.102JH 12/23/2002 */103DWORD rc;104rc = GetEnvironmentVariable(name, NULL, 0);105if (!rc) {106DWORD error = GetLastError();107if (error != ERROR_ENVVAR_NOT_FOUND) {108__kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), __kmp_msg_null);109}110// Variable is not found, it's ok, just continue.111} else {112DWORD len = rc;113result = (char *)KMP_INTERNAL_MALLOC(len);114if (result == NULL) {115KMP_FATAL(MemoryAllocFailed);116}117rc = GetEnvironmentVariable(name, result, len);118if (!rc) {119// GetEnvironmentVariable() may return 0 if variable is empty.120// In such a case GetLastError() returns ERROR_SUCCESS.121DWORD error = GetLastError();122if (error != ERROR_SUCCESS) {123// Unexpected error. The variable should be in the environment,124// and buffer should be large enough.125__kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error),126__kmp_msg_null);127KMP_INTERNAL_FREE((void *)result);128result = NULL;129}130}131}132#else133#error Unknown or unsupported OS.134#endif135136return result;137138} // func __kmp_env_get139140// TODO: Find and replace all regular free() with __kmp_env_free().141142void __kmp_env_free(char const **value) {143144KMP_DEBUG_ASSERT(value != NULL);145KMP_INTERNAL_FREE(CCAST(char *, *value));146*value = NULL;147148} // func __kmp_env_free149150int __kmp_env_exists(char const *name) {151152#if KMP_OS_UNIX153char const *value = getenv(name);154return ((value == NULL) ? (0) : (1));155#elif KMP_OS_WINDOWS156DWORD rc;157rc = GetEnvironmentVariable(name, NULL, 0);158if (rc == 0) {159DWORD error = GetLastError();160if (error != ERROR_ENVVAR_NOT_FOUND) {161__kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), __kmp_msg_null);162}163return 0;164}165return 1;166#else167#error Unknown or unsupported OS.168#endif169170} // func __kmp_env_exists171172void __kmp_env_set(char const *name, char const *value, int overwrite) {173174#if KMP_OS_UNIX175int rc = setenv(name, value, overwrite);176if (rc != 0) {177// Dead code. I tried to put too many variables into Linux* OS178// environment on IA-32 architecture. When application consumes179// more than ~2.5 GB of memory, entire system feels bad. Sometimes180// application is killed (by OS?), sometimes system stops181// responding... But this error message never appears. --ln182__kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_HNT(NotEnoughMemory),183__kmp_msg_null);184}185#elif KMP_OS_WINDOWS186BOOL rc;187if (!overwrite) {188rc = GetEnvironmentVariable(name, NULL, 0);189if (rc) {190// Variable exists, do not overwrite.191return;192}193DWORD error = GetLastError();194if (error != ERROR_ENVVAR_NOT_FOUND) {195__kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), __kmp_msg_null);196}197}198rc = SetEnvironmentVariable(name, value);199if (!rc) {200DWORD error = GetLastError();201__kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_ERR(error), __kmp_msg_null);202}203#else204#error Unknown or unsupported OS.205#endif206207} // func __kmp_env_set208209void __kmp_env_unset(char const *name) {210211#if KMP_OS_UNIX212unsetenv(name);213#elif KMP_OS_WINDOWS214BOOL rc = SetEnvironmentVariable(name, NULL);215if (!rc) {216DWORD error = GetLastError();217__kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_ERR(error), __kmp_msg_null);218}219#else220#error Unknown or unsupported OS.221#endif222223} // func __kmp_env_unset224225/* Intel OpenMP RTL string representation of environment: just a string of226characters, variables are separated with vertical bars, e. g.:227228"KMP_WARNINGS=0|KMP_AFFINITY=compact|"229230Empty variables are allowed and ignored:231232"||KMP_WARNINGS=1||"233*/234235static void236___kmp_env_blk_parse_string(kmp_env_blk_t *block, // M: Env block to fill.237char const *env // I: String to parse.238) {239240char const chr_delimiter = '|';241char const str_delimiter[] = {chr_delimiter, 0};242243char *bulk = NULL;244kmp_env_var_t *vars = NULL;245int count = 0; // Number of used elements in vars array.246int delimiters = 0; // Number of delimiters in input string.247248// Copy original string, we will modify the copy.249bulk = __kmp_str_format("%s", env);250251// Loop thru all the vars in environment block. Count delimiters (maximum252// number of variables is number of delimiters plus one).253{254char const *ptr = bulk;255for (;;) {256ptr = strchr(ptr, chr_delimiter);257if (ptr == NULL) {258break;259}260++delimiters;261ptr += 1;262}263}264265// Allocate vars array.266vars = (kmp_env_var_t *)allocate((delimiters + 1) * sizeof(kmp_env_var_t));267268// Loop thru all the variables.269{270char *var; // Pointer to variable (both name and value).271char *name; // Pointer to name of variable.272char *value; // Pointer to value.273char *buf; // Buffer for __kmp_str_token() function.274var = __kmp_str_token(bulk, str_delimiter, &buf); // Get the first var.275while (var != NULL) {276// Save found variable in vars array.277__kmp_str_split(var, '=', &name, &value);278KMP_DEBUG_ASSERT(count < delimiters + 1);279vars[count].name = name;280vars[count].value = value;281++count;282// Get the next var.283var = __kmp_str_token(NULL, str_delimiter, &buf);284}285}286287// Fill out result.288block->bulk = bulk;289block->vars = vars;290block->count = count;291}292293/* Windows* OS (actually, DOS) environment block is a piece of memory with294environment variables. Each variable is terminated with zero byte, entire295block is terminated with one extra zero byte, so we have two zero bytes at296the end of environment block, e. g.:297298"HOME=C:\\users\\lev\x00OS=Windows_NT\x00\x00"299300It is not clear how empty environment is represented. "\x00\x00"?301*/302303#if KMP_OS_WINDOWS304static void ___kmp_env_blk_parse_windows(305kmp_env_blk_t *block, // M: Env block to fill.306char const *env // I: Pointer to Windows* OS (DOS) environment block.307) {308309char *bulk = NULL;310kmp_env_var_t *vars = NULL;311int count = 0; // Number of used elements in vars array.312int size = 0; // Size of bulk.313314char *name; // Pointer to name of variable.315char *value; // Pointer to value.316317if (env != NULL) {318319// Loop thru all the vars in environment block. Count variables, find size320// of block.321{322char const *var; // Pointer to beginning of var.323int len; // Length of variable.324count = 0;325var =326env; // The first variable starts and beginning of environment block.327len = KMP_STRLEN(var);328while (len != 0) {329++count;330size = size + len + 1;331var = var + len +3321; // Move pointer to the beginning of the next variable.333len = KMP_STRLEN(var);334}335size =336size + 1; // Total size of env block, including terminating zero byte.337}338339// Copy original block to bulk, we will modify bulk, not original block.340bulk = (char *)allocate(size);341KMP_MEMCPY_S(bulk, size, env, size);342// Allocate vars array.343vars = (kmp_env_var_t *)allocate(count * sizeof(kmp_env_var_t));344345// Loop thru all the vars, now in bulk.346{347char *var; // Pointer to beginning of var.348int len; // Length of variable.349count = 0;350var = bulk;351len = KMP_STRLEN(var);352while (len != 0) {353// Save variable in vars array.354__kmp_str_split(var, '=', &name, &value);355vars[count].name = name;356vars[count].value = value;357++count;358// Get the next var.359var = var + len + 1;360len = KMP_STRLEN(var);361}362}363}364365// Fill out result.366block->bulk = bulk;367block->vars = vars;368block->count = count;369}370#endif371372/* Unix environment block is a array of pointers to variables, last pointer in373array is NULL:374375{ "HOME=/home/lev", "TERM=xterm", NULL }376*/377378#if KMP_OS_UNIX379static void380___kmp_env_blk_parse_unix(kmp_env_blk_t *block, // M: Env block to fill.381char **env // I: Unix environment to parse.382) {383char *bulk = NULL;384kmp_env_var_t *vars = NULL;385int count = 0;386size_t size = 0; // Size of bulk.387388// Count number of variables and length of required bulk.389{390while (env[count] != NULL) {391size += KMP_STRLEN(env[count]) + 1;392++count;393}394}395396// Allocate memory.397bulk = (char *)allocate(size);398vars = (kmp_env_var_t *)allocate(count * sizeof(kmp_env_var_t));399400// Loop thru all the vars.401{402char *var; // Pointer to beginning of var.403char *name; // Pointer to name of variable.404char *value; // Pointer to value.405size_t len; // Length of variable.406int i;407var = bulk;408for (i = 0; i < count; ++i) {409KMP_ASSERT(var < bulk + size);410[[maybe_unused]] size_t ssize = size - (var - bulk);411// Copy variable to bulk.412len = KMP_STRLEN(env[i]);413KMP_MEMCPY_S(var, ssize, env[i], len + 1);414// Save found variable in vars array.415__kmp_str_split(var, '=', &name, &value);416vars[i].name = name;417vars[i].value = value;418// Move pointer.419var += len + 1;420}421}422423// Fill out result.424block->bulk = bulk;425block->vars = vars;426block->count = count;427}428#endif429430void __kmp_env_blk_init(kmp_env_blk_t *block, // M: Block to initialize.431char const *bulk // I: Initialization string, or NULL.432) {433434if (bulk != NULL) {435___kmp_env_blk_parse_string(block, bulk);436} else {437#if KMP_OS_UNIX438___kmp_env_blk_parse_unix(block, environ);439#elif KMP_OS_WINDOWS440{441char *mem = GetEnvironmentStrings();442if (mem == NULL) {443DWORD error = GetLastError();444__kmp_fatal(KMP_MSG(CantGetEnvironment), KMP_ERR(error),445__kmp_msg_null);446}447___kmp_env_blk_parse_windows(block, mem);448FreeEnvironmentStrings(mem);449}450#else451#error Unknown or unsupported OS.452#endif453}454455} // __kmp_env_blk_init456457static int ___kmp_env_var_cmp( // Comparison function for qsort().458kmp_env_var_t const *lhs, kmp_env_var_t const *rhs) {459return strcmp(lhs->name, rhs->name);460}461462void __kmp_env_blk_sort(463kmp_env_blk_t *block // M: Block of environment variables to sort.464) {465466qsort(CCAST(kmp_env_var_t *, block->vars), block->count,467sizeof(kmp_env_var_t),468(int (*)(void const *, void const *)) & ___kmp_env_var_cmp);469470} // __kmp_env_block_sort471472void __kmp_env_blk_free(473kmp_env_blk_t *block // M: Block of environment variables to free.474) {475476KMP_INTERNAL_FREE(CCAST(kmp_env_var_t *, block->vars));477__kmp_str_free(&(block->bulk));478479block->count = 0;480block->vars = NULL;481482} // __kmp_env_blk_free483484char const * // R: Value of variable or NULL if variable does not exist.485__kmp_env_blk_var(kmp_env_blk_t *block, // I: Block of environment variables.486char const *name // I: Name of variable to find.487) {488489int i;490for (i = 0; i < block->count; ++i) {491if (strcmp(block->vars[i].name, name) == 0) {492return block->vars[i].value;493}494}495return NULL;496497} // __kmp_env_block_var498499// end of file //500501502