brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.3 KiB · 4def6ea Raw
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