xref: /freebsd/contrib/llvm-project/openmp/runtime/src/kmp_environment.cpp (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
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
65 extern 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 
76 static 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 
84 char *__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 
143 void __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 
151 int __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 
173 void __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 
210 void __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 
236 static 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
305 static 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
380 static 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       // Copy variable to bulk.
411       len = KMP_STRLEN(env[i]);
412       KMP_MEMCPY_S(var, size, env[i], len + 1);
413       // Save found variable in vars array.
414       __kmp_str_split(var, '=', &name, &value);
415       vars[i].name = name;
416       vars[i].value = value;
417       // Move pointer.
418       var += len + 1;
419     }
420   }
421 
422   // Fill out result.
423   block->bulk = bulk;
424   block->vars = vars;
425   block->count = count;
426 }
427 #endif
428 
429 void __kmp_env_blk_init(kmp_env_blk_t *block, // M: Block to initialize.
430                         char const *bulk // I: Initialization string, or NULL.
431 ) {
432 
433   if (bulk != NULL) {
434     ___kmp_env_blk_parse_string(block, bulk);
435   } else {
436 #if KMP_OS_UNIX
437     ___kmp_env_blk_parse_unix(block, environ);
438 #elif KMP_OS_WINDOWS
439     {
440       char *mem = GetEnvironmentStrings();
441       if (mem == NULL) {
442         DWORD error = GetLastError();
443         __kmp_fatal(KMP_MSG(CantGetEnvironment), KMP_ERR(error),
444                     __kmp_msg_null);
445       }
446       ___kmp_env_blk_parse_windows(block, mem);
447       FreeEnvironmentStrings(mem);
448     }
449 #else
450 #error Unknown or unsupported OS.
451 #endif
452   }
453 
454 } // __kmp_env_blk_init
455 
456 static int ___kmp_env_var_cmp( // Comparison function for qsort().
457     kmp_env_var_t const *lhs, kmp_env_var_t const *rhs) {
458   return strcmp(lhs->name, rhs->name);
459 }
460 
461 void __kmp_env_blk_sort(
462     kmp_env_blk_t *block // M: Block of environment variables to sort.
463 ) {
464 
465   qsort(CCAST(kmp_env_var_t *, block->vars), block->count,
466         sizeof(kmp_env_var_t),
467         (int (*)(void const *, void const *)) & ___kmp_env_var_cmp);
468 
469 } // __kmp_env_block_sort
470 
471 void __kmp_env_blk_free(
472     kmp_env_blk_t *block // M: Block of environment variables to free.
473 ) {
474 
475   KMP_INTERNAL_FREE(CCAST(kmp_env_var_t *, block->vars));
476   __kmp_str_free(&(block->bulk));
477 
478   block->count = 0;
479   block->vars = NULL;
480 
481 } // __kmp_env_blk_free
482 
483 char const * // R: Value of variable or NULL if variable does not exist.
484 __kmp_env_blk_var(kmp_env_blk_t *block, // I: Block of environment variables.
485                   char const *name // I: Name of variable to find.
486 ) {
487 
488   int i;
489   for (i = 0; i < block->count; ++i) {
490     if (strcmp(block->vars[i].name, name) == 0) {
491       return block->vars[i].value;
492     }
493   }
494   return NULL;
495 
496 } // __kmp_env_block_var
497 
498 // end of file //
499