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-exception
10//
11//===----------------------------------------------------------------------===//
12
13/* We use GetEnvironmentVariable for Windows* OS instead of getenv because the
14   act of loading a DLL on Windows* OS makes any user-set environment variables
15   (i.e. with putenv()) unavailable.  getenv() apparently gets a clean copy of
16   the env variables as they existed at the start of the run. JH 12/23/2002
17
18   On Windows* OS, there are two environments (at least, see below):
19
20   1. Environment maintained by Windows* OS on IA-32 architecture. Accessible
21      through GetEnvironmentVariable(), SetEnvironmentVariable(), and
22      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 on
29   IA-32 architecture.
30
31   Windows* OS on IA-32 architecture maintained by OS, so there is always only
32   one Windows* OS on IA-32 architecture per process. Changes in Windows* OS on
33   IA-32 architecture are process-visible.
34
35   C environment maintained by C RTL. Multiple copies of C RTL may be present
36   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-32
41      architecture are being updated. Windows* OS on IA-32 architecture may be
42      considered primary target, while updating C RTL environment is free bonus.
43
44   2. Get variables with GetEnvironmentVariable() -- getenv() does not
45      search Windows* OS on IA-32 architecture, and can not see variables
46      set with SetEnvironmentVariable().
47
48   2007-04-05 -- lev
49*/
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_UNIX
59#include <stdlib.h> // getenv, setenv, unsetenv.
60#include <string.h> // strlen, strcpy.
61#if KMP_OS_DARWIN
62#include <crt_externs.h>
63#define environ (*_NSGetEnviron())
64#else
65extern char **environ;
66#endif
67#elif KMP_OS_WINDOWS
68#include <windows.h> // GetEnvironmentVariable, SetEnvironmentVariable,
69// GetLastError.
70#else
71#error Unknown or unsupported OS.
72#endif
73
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} // allocate
83
84char *__kmp_env_get(char const *name) {
85
86  char *result = NULL;
87
88#if KMP_OS_UNIX
89  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_WINDOWS
99  /* We use GetEnvironmentVariable for Windows* OS instead of getenv because the
100     act of loading a DLL on Windows* OS makes any user-set environment
101     variables (i.e. with putenv()) unavailable. getenv() apparently gets a
102     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#else
134#error Unknown or unsupported OS.
135#endif
136
137  return result;
138
139} // func __kmp_env_get
140
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_free
150
151int __kmp_env_exists(char const *name) {
152
153#if KMP_OS_UNIX
154  char const *value = getenv(name);
155  return ((value == NULL) ? (0) : (1));
156#elif KMP_OS_WINDOWS
157  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#else
168#error Unknown or unsupported OS.
169#endif
170
171} // func __kmp_env_exists
172
173void __kmp_env_set(char const *name, char const *value, int overwrite) {
174
175#if KMP_OS_UNIX
176  int rc = setenv(name, value, overwrite);
177  if (rc != 0) {
178    // Dead code. I tried to put too many variables into Linux* OS
179    // environment on IA-32 architecture. When application consumes
180    // more than ~2.5 GB of memory, entire system feels bad. Sometimes
181    // application is killed (by OS?), sometimes system stops
182    // responding... But this error message never appears. --ln
183    __kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_HNT(NotEnoughMemory),
184                __kmp_msg_null);
185  }
186#elif KMP_OS_WINDOWS
187  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#else
205#error Unknown or unsupported OS.
206#endif
207
208} // func __kmp_env_set
209
210void __kmp_env_unset(char const *name) {
211
212#if KMP_OS_UNIX
213  unsetenv(name);
214#elif KMP_OS_WINDOWS
215  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#else
221#error Unknown or unsupported OS.
222#endif
223
224} // func __kmp_env_unset
225
226/* Intel OpenMP RTL string representation of environment: just a string of
227   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 void
237___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 (maximum
253  // 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 with
295   environment variables. Each variable is terminated with zero byte, entire
296   block is terminated with one extra zero byte, so we have two zero bytes at
297   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_WINDOWS
305static 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 size
321    // 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#endif
372
373/* Unix environment block is a array of pointers to variables, last pointer in
374   array is NULL:
375
376        { "HOME=/home/lev", "TERM=xterm", NULL }
377*/
378
379#if KMP_OS_UNIX
380static void
381___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#endif
430
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_UNIX
439    ___kmp_env_blk_parse_unix(block, environ);
440#elif KMP_OS_WINDOWS
441    {
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#else
452#error Unknown or unsupported OS.
453#endif
454  }
455
456} // __kmp_env_blk_init
457
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_sort
472
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_free
484
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_var
499
500// end of file //
501