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: name); |
90 | if (value != NULL) { |
91 | size_t len = KMP_STRLEN(s: 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: 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: name, value: value, replace: 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: 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(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(s: ptr, c: 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(size: (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(str: bulk, delim: str_delimiter, buf: &buf); // Get the first var. |
276 | while (var != NULL) { |
277 | // Save found variable in vars array. |
278 | __kmp_str_split(str: var, delim: '=', head: &name, tail: &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, delim: str_delimiter, buf: &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(s: env[count]) + 1; |
393 | ++count; |
394 | } |
395 | } |
396 | |
397 | // Allocate memory. |
398 | bulk = (char *)allocate(size); |
399 | vars = (kmp_env_var_t *)allocate(size: 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(s: env[i]); |
414 | KMP_MEMCPY_S(var, ssize, env[i], len + 1); |
415 | // Save found variable in vars array. |
416 | __kmp_str_split(str: var, delim: '=', head: &name, tail: &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, env: bulk); |
437 | } else { |
438 | #if KMP_OS_UNIX |
439 | ___kmp_env_blk_parse_unix(block, env: 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(s1: lhs->name, s2: 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), nmemb: block->count, |
468 | size: sizeof(kmp_env_var_t), |
469 | compar: (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(str: &(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(s1: block->vars[i].name, s2: 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 | |