501 lines · cpp
1/*2 * kmp_environment.cpp -- Handle environment variables OS-independently.3 */4 5//===----------------------------------------------------------------------===//6//7// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.8// See https://llvm.org/LICENSE.txt for license information.9// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception10//11//===----------------------------------------------------------------------===//12 13/* We use GetEnvironmentVariable for Windows* OS instead of getenv because the14 act of loading a DLL on Windows* OS makes any user-set environment variables15 (i.e. with putenv()) unavailable. getenv() apparently gets a clean copy of16 the env variables as they existed at the start of the run. JH 12/23/200217 18 On Windows* OS, there are two environments (at least, see below):19 20 1. Environment maintained by Windows* OS on IA-32 architecture. Accessible21 through GetEnvironmentVariable(), SetEnvironmentVariable(), and22 GetEnvironmentStrings().23 24 2. Environment maintained by C RTL. Accessible through getenv(), putenv().25 26 putenv() function updates both C and Windows* OS on IA-32 architecture.27 getenv() function search for variables in C RTL environment only.28 Windows* OS on IA-32 architecture functions work *only* with Windows* OS on29 IA-32 architecture.30 31 Windows* OS on IA-32 architecture maintained by OS, so there is always only32 one Windows* OS on IA-32 architecture per process. Changes in Windows* OS on33 IA-32 architecture are process-visible.34 35 C environment maintained by C RTL. Multiple copies of C RTL may be present36 in the process, and each C RTL maintains its own environment. :-(37 38 Thus, proper way to work with environment on Windows* OS is:39 40 1. Set variables with putenv() function -- both C and Windows* OS on IA-3241 architecture are being updated. Windows* OS on IA-32 architecture may be42 considered primary target, while updating C RTL environment is free bonus.43 44 2. Get variables with GetEnvironmentVariable() -- getenv() does not45 search Windows* OS on IA-32 architecture, and can not see variables46 set with SetEnvironmentVariable().47 48 2007-04-05 -- lev49*/50 51#include "kmp_environment.h"52 53#include "kmp.h" //54#include "kmp_i18n.h"55#include "kmp_os.h" // KMP_OS_*.56#include "kmp_str.h" // __kmp_str_*().57 58#if KMP_OS_UNIX59#include <stdlib.h> // getenv, setenv, unsetenv.60#include <string.h> // strlen, strcpy.61#if KMP_OS_DARWIN62#include <crt_externs.h>63#define environ (*_NSGetEnviron())64#else65extern char **environ;66#endif67#elif KMP_OS_WINDOWS68#include <windows.h> // GetEnvironmentVariable, SetEnvironmentVariable,69// GetLastError.70#else71#error Unknown or unsupported OS.72#endif73 74// TODO: Eliminate direct memory allocations, use string operations instead.75 76static inline void *allocate(size_t size) {77 void *ptr = KMP_INTERNAL_MALLOC(size);78 if (ptr == NULL) {79 KMP_FATAL(MemoryAllocFailed);80 }81 return ptr;82} // allocate83 84char *__kmp_env_get(char const *name) {85 86 char *result = NULL;87 88#if KMP_OS_UNIX89 char const *value = getenv(name);90 if (value != NULL) {91 size_t len = KMP_STRLEN(value) + 1;92 result = (char *)KMP_INTERNAL_MALLOC(len);93 if (result == NULL) {94 KMP_FATAL(MemoryAllocFailed);95 }96 KMP_STRNCPY_S(result, len, value, len);97 }98#elif KMP_OS_WINDOWS99 /* We use GetEnvironmentVariable for Windows* OS instead of getenv because the100 act of loading a DLL on Windows* OS makes any user-set environment101 variables (i.e. with putenv()) unavailable. getenv() apparently gets a102 clean copy of the env variables as they existed at the start of the run.103 JH 12/23/2002 */104 DWORD rc;105 rc = GetEnvironmentVariable(name, NULL, 0);106 if (!rc) {107 DWORD error = GetLastError();108 if (error != ERROR_ENVVAR_NOT_FOUND) {109 __kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), __kmp_msg_null);110 }111 // Variable is not found, it's ok, just continue.112 } else {113 DWORD len = rc;114 result = (char *)KMP_INTERNAL_MALLOC(len);115 if (result == NULL) {116 KMP_FATAL(MemoryAllocFailed);117 }118 rc = GetEnvironmentVariable(name, result, len);119 if (!rc) {120 // GetEnvironmentVariable() may return 0 if variable is empty.121 // In such a case GetLastError() returns ERROR_SUCCESS.122 DWORD error = GetLastError();123 if (error != ERROR_SUCCESS) {124 // Unexpected error. The variable should be in the environment,125 // and buffer should be large enough.126 __kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error),127 __kmp_msg_null);128 KMP_INTERNAL_FREE((void *)result);129 result = NULL;130 }131 }132 }133#else134#error Unknown or unsupported OS.135#endif136 137 return result;138 139} // func __kmp_env_get140 141// TODO: Find and replace all regular free() with __kmp_env_free().142 143void __kmp_env_free(char const **value) {144 145 KMP_DEBUG_ASSERT(value != NULL);146 KMP_INTERNAL_FREE(CCAST(char *, *value));147 *value = NULL;148 149} // func __kmp_env_free150 151int __kmp_env_exists(char const *name) {152 153#if KMP_OS_UNIX154 char const *value = getenv(name);155 return ((value == NULL) ? (0) : (1));156#elif KMP_OS_WINDOWS157 DWORD rc;158 rc = GetEnvironmentVariable(name, NULL, 0);159 if (rc == 0) {160 DWORD error = GetLastError();161 if (error != ERROR_ENVVAR_NOT_FOUND) {162 __kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), __kmp_msg_null);163 }164 return 0;165 }166 return 1;167#else168#error Unknown or unsupported OS.169#endif170 171} // func __kmp_env_exists172 173void __kmp_env_set(char const *name, char const *value, int overwrite) {174 175#if KMP_OS_UNIX176 int rc = setenv(name, value, overwrite);177 if (rc != 0) {178 // Dead code. I tried to put too many variables into Linux* OS179 // environment on IA-32 architecture. When application consumes180 // more than ~2.5 GB of memory, entire system feels bad. Sometimes181 // application is killed (by OS?), sometimes system stops182 // responding... But this error message never appears. --ln183 __kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_HNT(NotEnoughMemory),184 __kmp_msg_null);185 }186#elif KMP_OS_WINDOWS187 BOOL rc;188 if (!overwrite) {189 rc = GetEnvironmentVariable(name, NULL, 0);190 if (rc) {191 // Variable exists, do not overwrite.192 return;193 }194 DWORD error = GetLastError();195 if (error != ERROR_ENVVAR_NOT_FOUND) {196 __kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), __kmp_msg_null);197 }198 }199 rc = SetEnvironmentVariable(name, value);200 if (!rc) {201 DWORD error = GetLastError();202 __kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_ERR(error), __kmp_msg_null);203 }204#else205#error Unknown or unsupported OS.206#endif207 208} // func __kmp_env_set209 210void __kmp_env_unset(char const *name) {211 212#if KMP_OS_UNIX213 unsetenv(name);214#elif KMP_OS_WINDOWS215 BOOL rc = SetEnvironmentVariable(name, NULL);216 if (!rc) {217 DWORD error = GetLastError();218 __kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_ERR(error), __kmp_msg_null);219 }220#else221#error Unknown or unsupported OS.222#endif223 224} // func __kmp_env_unset225 226/* Intel OpenMP RTL string representation of environment: just a string of227 characters, variables are separated with vertical bars, e. g.:228 229 "KMP_WARNINGS=0|KMP_AFFINITY=compact|"230 231 Empty variables are allowed and ignored:232 233 "||KMP_WARNINGS=1||"234*/235 236static void237___kmp_env_blk_parse_string(kmp_env_blk_t *block, // M: Env block to fill.238 char const *env // I: String to parse.239) {240 241 char const chr_delimiter = '|';242 char const str_delimiter[] = {chr_delimiter, 0};243 244 char *bulk = NULL;245 kmp_env_var_t *vars = NULL;246 int count = 0; // Number of used elements in vars array.247 int delimiters = 0; // Number of delimiters in input string.248 249 // Copy original string, we will modify the copy.250 bulk = __kmp_str_format("%s", env);251 252 // Loop thru all the vars in environment block. Count delimiters (maximum253 // number of variables is number of delimiters plus one).254 {255 char const *ptr = bulk;256 for (;;) {257 ptr = strchr(ptr, chr_delimiter);258 if (ptr == NULL) {259 break;260 }261 ++delimiters;262 ptr += 1;263 }264 }265 266 // Allocate vars array.267 vars = (kmp_env_var_t *)allocate((delimiters + 1) * sizeof(kmp_env_var_t));268 269 // Loop thru all the variables.270 {271 char *var; // Pointer to variable (both name and value).272 char *name; // Pointer to name of variable.273 char *value; // Pointer to value.274 char *buf; // Buffer for __kmp_str_token() function.275 var = __kmp_str_token(bulk, str_delimiter, &buf); // Get the first var.276 while (var != NULL) {277 // Save found variable in vars array.278 __kmp_str_split(var, '=', &name, &value);279 KMP_DEBUG_ASSERT(count < delimiters + 1);280 vars[count].name = name;281 vars[count].value = value;282 ++count;283 // Get the next var.284 var = __kmp_str_token(NULL, str_delimiter, &buf);285 }286 }287 288 // Fill out result.289 block->bulk = bulk;290 block->vars = vars;291 block->count = count;292}293 294/* Windows* OS (actually, DOS) environment block is a piece of memory with295 environment variables. Each variable is terminated with zero byte, entire296 block is terminated with one extra zero byte, so we have two zero bytes at297 the end of environment block, e. g.:298 299 "HOME=C:\\users\\lev\x00OS=Windows_NT\x00\x00"300 301 It is not clear how empty environment is represented. "\x00\x00"?302*/303 304#if KMP_OS_WINDOWS305static void ___kmp_env_blk_parse_windows(306 kmp_env_blk_t *block, // M: Env block to fill.307 char const *env // I: Pointer to Windows* OS (DOS) environment block.308) {309 310 char *bulk = NULL;311 kmp_env_var_t *vars = NULL;312 int count = 0; // Number of used elements in vars array.313 int size = 0; // Size of bulk.314 315 char *name; // Pointer to name of variable.316 char *value; // Pointer to value.317 318 if (env != NULL) {319 320 // Loop thru all the vars in environment block. Count variables, find size321 // of block.322 {323 char const *var; // Pointer to beginning of var.324 int len; // Length of variable.325 count = 0;326 var =327 env; // The first variable starts and beginning of environment block.328 len = KMP_STRLEN(var);329 while (len != 0) {330 ++count;331 size = size + len + 1;332 var = var + len +333 1; // Move pointer to the beginning of the next variable.334 len = KMP_STRLEN(var);335 }336 size =337 size + 1; // Total size of env block, including terminating zero byte.338 }339 340 // Copy original block to bulk, we will modify bulk, not original block.341 bulk = (char *)allocate(size);342 KMP_MEMCPY_S(bulk, size, env, size);343 // Allocate vars array.344 vars = (kmp_env_var_t *)allocate(count * sizeof(kmp_env_var_t));345 346 // Loop thru all the vars, now in bulk.347 {348 char *var; // Pointer to beginning of var.349 int len; // Length of variable.350 count = 0;351 var = bulk;352 len = KMP_STRLEN(var);353 while (len != 0) {354 // Save variable in vars array.355 __kmp_str_split(var, '=', &name, &value);356 vars[count].name = name;357 vars[count].value = value;358 ++count;359 // Get the next var.360 var = var + len + 1;361 len = KMP_STRLEN(var);362 }363 }364 }365 366 // Fill out result.367 block->bulk = bulk;368 block->vars = vars;369 block->count = count;370}371#endif372 373/* Unix environment block is a array of pointers to variables, last pointer in374 array is NULL:375 376 { "HOME=/home/lev", "TERM=xterm", NULL }377*/378 379#if KMP_OS_UNIX380static void381___kmp_env_blk_parse_unix(kmp_env_blk_t *block, // M: Env block to fill.382 char **env // I: Unix environment to parse.383) {384 char *bulk = NULL;385 kmp_env_var_t *vars = NULL;386 int count = 0;387 size_t size = 0; // Size of bulk.388 389 // Count number of variables and length of required bulk.390 {391 while (env[count] != NULL) {392 size += KMP_STRLEN(env[count]) + 1;393 ++count;394 }395 }396 397 // Allocate memory.398 bulk = (char *)allocate(size);399 vars = (kmp_env_var_t *)allocate(count * sizeof(kmp_env_var_t));400 401 // Loop thru all the vars.402 {403 char *var; // Pointer to beginning of var.404 char *name; // Pointer to name of variable.405 char *value; // Pointer to value.406 size_t len; // Length of variable.407 int i;408 var = bulk;409 for (i = 0; i < count; ++i) {410 KMP_ASSERT(var < bulk + size);411 [[maybe_unused]] size_t ssize = size - (var - bulk);412 // Copy variable to bulk.413 len = KMP_STRLEN(env[i]);414 KMP_MEMCPY_S(var, ssize, env[i], len + 1);415 // Save found variable in vars array.416 __kmp_str_split(var, '=', &name, &value);417 vars[i].name = name;418 vars[i].value = value;419 // Move pointer.420 var += len + 1;421 }422 }423 424 // Fill out result.425 block->bulk = bulk;426 block->vars = vars;427 block->count = count;428}429#endif430 431void __kmp_env_blk_init(kmp_env_blk_t *block, // M: Block to initialize.432 char const *bulk // I: Initialization string, or NULL.433) {434 435 if (bulk != NULL) {436 ___kmp_env_blk_parse_string(block, bulk);437 } else {438#if KMP_OS_UNIX439 ___kmp_env_blk_parse_unix(block, environ);440#elif KMP_OS_WINDOWS441 {442 char *mem = GetEnvironmentStrings();443 if (mem == NULL) {444 DWORD error = GetLastError();445 __kmp_fatal(KMP_MSG(CantGetEnvironment), KMP_ERR(error),446 __kmp_msg_null);447 }448 ___kmp_env_blk_parse_windows(block, mem);449 FreeEnvironmentStrings(mem);450 }451#else452#error Unknown or unsupported OS.453#endif454 }455 456} // __kmp_env_blk_init457 458static int ___kmp_env_var_cmp( // Comparison function for qsort().459 kmp_env_var_t const *lhs, kmp_env_var_t const *rhs) {460 return strcmp(lhs->name, rhs->name);461}462 463void __kmp_env_blk_sort(464 kmp_env_blk_t *block // M: Block of environment variables to sort.465) {466 467 qsort(CCAST(kmp_env_var_t *, block->vars), block->count,468 sizeof(kmp_env_var_t),469 (int (*)(void const *, void const *)) & ___kmp_env_var_cmp);470 471} // __kmp_env_block_sort472 473void __kmp_env_blk_free(474 kmp_env_blk_t *block // M: Block of environment variables to free.475) {476 477 KMP_INTERNAL_FREE(CCAST(kmp_env_var_t *, block->vars));478 __kmp_str_free(&(block->bulk));479 480 block->count = 0;481 block->vars = NULL;482 483} // __kmp_env_blk_free484 485char const * // R: Value of variable or NULL if variable does not exist.486__kmp_env_blk_var(kmp_env_blk_t *block, // I: Block of environment variables.487 char const *name // I: Name of variable to find.488) {489 490 int i;491 for (i = 0; i < block->count; ++i) {492 if (strcmp(block->vars[i].name, name) == 0) {493 return block->vars[i].value;494 }495 }496 return NULL;497 498} // __kmp_env_block_var499 500// end of file //501