xref: /freebsd/contrib/llvm-project/openmp/runtime/src/kmp_environment.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
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       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 
431 void __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 
458 static 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 
463 void __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 
473 void __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 
485 char 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