1 /*
2  * kmp_environment.cpp -- Handle environment variables OS-independently.
3  */
4 
5 
6 //===----------------------------------------------------------------------===//
7 //
8 //                     The LLVM Compiler Infrastructure
9 //
10 // This file is dual licensed under the MIT and the University of Illinois Open
11 // Source Licenses. See LICENSE.txt for details.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 
16 /* We use GetEnvironmentVariable for Windows* OS instead of getenv because the
17    act of loading a DLL on Windows* OS makes any user-set environment variables
18    (i.e. with putenv()) unavailable.  getenv() apparently gets a clean copy of
19    the env variables as they existed at the start of the run. JH 12/23/2002
20 
21    On Windows* OS, there are two environments (at least, see below):
22 
23    1. Environment maintained by Windows* OS on IA-32 architecture. Accessible
24       through GetEnvironmentVariable(), SetEnvironmentVariable(), and
25       GetEnvironmentStrings().
26 
27    2. Environment maintained by C RTL. Accessible through getenv(), putenv().
28 
29    putenv() function updates both C and Windows* OS on IA-32 architecture.
30    getenv() function search for variables in C RTL environment only.
31    Windows* OS on IA-32 architecture functions work *only* with Windows* OS on
32    IA-32 architecture.
33 
34    Windows* OS on IA-32 architecture maintained by OS, so there is always only
35    one Windows* OS on IA-32 architecture per process. Changes in Windows* OS on
36    IA-32 architecture are process-visible.
37 
38    C environment maintained by C RTL. Multiple copies of C RTL may be present
39    in the process, and each C RTL maintains its own environment. :-(
40 
41    Thus, proper way to work with environment on Windows* OS is:
42 
43    1. Set variables with putenv() function -- both C and Windows* OS on IA-32
44       architecture are being updated. Windows* OS on IA-32 architecture may be
45       considered primary target, while updating C RTL environment is free bonus.
46 
47    2. Get variables with GetEnvironmentVariable() -- getenv() does not
48       search Windows* OS on IA-32 architecture, and can not see variables
49       set with SetEnvironmentVariable().
50 
51    2007-04-05 -- lev
52 */
53 
54 #include "kmp_environment.h"
55 
56 #include "kmp.h" //
57 #include "kmp_i18n.h"
58 #include "kmp_os.h" // KMP_OS_*.
59 #include "kmp_str.h" // __kmp_str_*().
60 
61 #if KMP_OS_UNIX
62 #include <stdlib.h> // getenv, setenv, unsetenv.
63 #include <string.h> // strlen, strcpy.
64 #if KMP_OS_DARWIN
65 #include <crt_externs.h>
66 #define environ (*_NSGetEnviron())
67 #else
68 extern char **environ;
69 #endif
70 #elif KMP_OS_WINDOWS
71 #include <windows.h> // GetEnvironmentVariable, SetEnvironmentVariable,
72 // GetLastError.
73 #else
74 #error Unknown or unsupported OS.
75 #endif
76 
77 // TODO: Eliminate direct memory allocations, use string operations instead.
78 
79 static inline void *allocate(size_t size) {
80   void *ptr = KMP_INTERNAL_MALLOC(size);
81   if (ptr == NULL) {
82     KMP_FATAL(MemoryAllocFailed);
83   }; // if
84   return ptr;
85 } // allocate
86 
87 char *__kmp_env_get(char const *name) {
88 
89   char *result = NULL;
90 
91 #if KMP_OS_UNIX
92   char const *value = getenv(name);
93   if (value != NULL) {
94     size_t len = KMP_STRLEN(value) + 1;
95     result = (char *)KMP_INTERNAL_MALLOC(len);
96     if (result == NULL) {
97       KMP_FATAL(MemoryAllocFailed);
98     }; // if
99     KMP_STRNCPY_S(result, len, value, len);
100   }; // if
101 #elif KMP_OS_WINDOWS
102   /* We use GetEnvironmentVariable for Windows* OS instead of getenv because the
103      act of loading a DLL on Windows* OS makes any user-set environment
104      variables (i.e. with putenv()) unavailable. getenv() apparently gets a
105      clean copy of the env variables as they existed at the start of the run.
106      JH 12/23/2002 */
107   DWORD rc;
108   rc = GetEnvironmentVariable(name, NULL, 0);
109   if (!rc) {
110     DWORD error = GetLastError();
111     if (error != ERROR_ENVVAR_NOT_FOUND) {
112       __kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), __kmp_msg_null);
113     }; // if
114     // Variable is not found, it's ok, just continue.
115   } else {
116     DWORD len = rc;
117     result = (char *)KMP_INTERNAL_MALLOC(len);
118     if (result == NULL) {
119       KMP_FATAL(MemoryAllocFailed);
120     }; // if
121     rc = GetEnvironmentVariable(name, result, len);
122     if (!rc) {
123       // GetEnvironmentVariable() may return 0 if variable is empty.
124       // In such a case GetLastError() returns ERROR_SUCCESS.
125       DWORD error = GetLastError();
126       if (error != ERROR_SUCCESS) {
127         // Unexpected error. The variable should be in the environment,
128         // and buffer should be large enough.
129         __kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error),
130                     __kmp_msg_null);
131         KMP_INTERNAL_FREE((void *)result);
132         result = NULL;
133       }; // if
134     }; // if
135   }; // if
136 #else
137 #error Unknown or unsupported OS.
138 #endif
139 
140   return result;
141 
142 } // func __kmp_env_get
143 
144 // TODO: Find and replace all regular free() with __kmp_env_free().
145 
146 void __kmp_env_free(char const **value) {
147 
148   KMP_DEBUG_ASSERT(value != NULL);
149   KMP_INTERNAL_FREE(CCAST(char *, *value));
150   *value = NULL;
151 
152 } // func __kmp_env_free
153 
154 int __kmp_env_exists(char const *name) {
155 
156 #if KMP_OS_UNIX
157   char const *value = getenv(name);
158   return ((value == NULL) ? (0) : (1));
159 #elif KMP_OS_WINDOWS
160   DWORD rc;
161   rc = GetEnvironmentVariable(name, NULL, 0);
162   if (rc == 0) {
163     DWORD error = GetLastError();
164     if (error != ERROR_ENVVAR_NOT_FOUND) {
165       __kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), __kmp_msg_null);
166     }; // if
167     return 0;
168   }; // if
169   return 1;
170 #else
171 #error Unknown or unsupported OS.
172 #endif
173 
174 } // func __kmp_env_exists
175 
176 void __kmp_env_set(char const *name, char const *value, int overwrite) {
177 
178 #if KMP_OS_UNIX
179   int rc = setenv(name, value, overwrite);
180   if (rc != 0) {
181     // Dead code. I tried to put too many variables into Linux* OS
182     // environment on IA-32 architecture. When application consumes
183     // more than ~2.5 GB of memory, entire system feels bad. Sometimes
184     // application is killed (by OS?), sometimes system stops
185     // responding... But this error message never appears. --ln
186     __kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_HNT(NotEnoughMemory),
187                 __kmp_msg_null);
188   }; // if
189 #elif KMP_OS_WINDOWS
190   BOOL rc;
191   if (!overwrite) {
192     rc = GetEnvironmentVariable(name, NULL, 0);
193     if (rc) {
194       // Variable exists, do not overwrite.
195       return;
196     }; // if
197     DWORD error = GetLastError();
198     if (error != ERROR_ENVVAR_NOT_FOUND) {
199       __kmp_fatal(KMP_MSG(CantGetEnvVar, name), KMP_ERR(error), __kmp_msg_null);
200     }; // if
201   }; // if
202   rc = SetEnvironmentVariable(name, value);
203   if (!rc) {
204     DWORD error = GetLastError();
205     __kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_ERR(error), __kmp_msg_null);
206   }; // if
207 #else
208 #error Unknown or unsupported OS.
209 #endif
210 
211 } // func __kmp_env_set
212 
213 void __kmp_env_unset(char const *name) {
214 
215 #if KMP_OS_UNIX
216   unsetenv(name);
217 #elif KMP_OS_WINDOWS
218   BOOL rc = SetEnvironmentVariable(name, NULL);
219   if (!rc) {
220     DWORD error = GetLastError();
221     __kmp_fatal(KMP_MSG(CantSetEnvVar, name), KMP_ERR(error), __kmp_msg_null);
222   }; // if
223 #else
224 #error Unknown or unsupported OS.
225 #endif
226 
227 } // func __kmp_env_unset
228 
229 /* Intel OpenMP RTL string representation of environment: just a string of
230    characters, variables are separated with vertical bars, e. g.:
231 
232         "KMP_WARNINGS=0|KMP_AFFINITY=compact|"
233 
234     Empty variables are allowed and ignored:
235 
236         "||KMP_WARNINGS=1||"
237 */
238 
239 static void
240 ___kmp_env_blk_parse_string(kmp_env_blk_t *block, // M: Env block to fill.
241                             char const *env // I: String to parse.
242                             ) {
243 
244   char const chr_delimiter = '|';
245   char const str_delimiter[] = {chr_delimiter, 0};
246 
247   char *bulk = NULL;
248   kmp_env_var_t *vars = NULL;
249   int count = 0; // Number of used elements in vars array.
250   int delimiters = 0; // Number of delimiters in input string.
251 
252   // Copy original string, we will modify the copy.
253   bulk = __kmp_str_format("%s", env);
254 
255   // Loop thru all the vars in environment block. Count delimiters (maximum
256   // number of variables is number of delimiters plus one).
257   {
258     char const *ptr = bulk;
259     for (;;) {
260       ptr = strchr(ptr, chr_delimiter);
261       if (ptr == NULL) {
262         break;
263       }; // if
264       ++delimiters;
265       ptr += 1;
266     }; // forever
267   }
268 
269   // Allocate vars array.
270   vars = (kmp_env_var_t *)allocate((delimiters + 1) * sizeof(kmp_env_var_t));
271 
272   // Loop thru all the variables.
273   {
274     char *var; // Pointer to variable (both name and value).
275     char *name; // Pointer to name of variable.
276     char *value; // Pointer to value.
277     char *buf; // Buffer for __kmp_str_token() function.
278     var = __kmp_str_token(bulk, str_delimiter, &buf); // Get the first var.
279     while (var != NULL) {
280       // Save found variable in vars array.
281       __kmp_str_split(var, '=', &name, &value);
282       KMP_DEBUG_ASSERT(count < delimiters + 1);
283       vars[count].name = name;
284       vars[count].value = value;
285       ++count;
286       // Get the next var.
287       var = __kmp_str_token(NULL, str_delimiter, &buf);
288     }; // while
289   }
290 
291   // Fill out result.
292   block->bulk = bulk;
293   block->vars = vars;
294   block->count = count;
295 
296 }; // ___kmp_env_blk_parse_string
297 
298 /* Windows* OS (actually, DOS) environment block is a piece of memory with
299    environment variables. Each variable is terminated with zero byte, entire
300    block is terminated with one extra zero byte, so we have two zero bytes at
301    the end of environment block, e. g.:
302 
303         "HOME=C:\\users\\lev\x00OS=Windows_NT\x00\x00"
304 
305     It is not clear how empty environment is represented. "\x00\x00"?
306 */
307 
308 #if KMP_OS_WINDOWS
309 static void ___kmp_env_blk_parse_windows(
310     kmp_env_blk_t *block, // M: Env block to fill.
311     char const *env // I: Pointer to Windows* OS (DOS) environment block.
312     ) {
313 
314   char *bulk = NULL;
315   kmp_env_var_t *vars = NULL;
316   int count = 0; // Number of used elements in vars array.
317   int size = 0; // Size of bulk.
318 
319   char *name; // Pointer to name of variable.
320   char *value; // Pointer to value.
321 
322   if (env != NULL) {
323 
324     // Loop thru all the vars in environment block. Count variables, find size
325     // of block.
326     {
327       char const *var; // Pointer to beginning of var.
328       int len; // Length of variable.
329       count = 0;
330       var =
331           env; // The first variable starts and beginning of environment block.
332       len = KMP_STRLEN(var);
333       while (len != 0) {
334         ++count;
335         size = size + len + 1;
336         var = var + len +
337               1; // Move pointer to the beginning of the next variable.
338         len = KMP_STRLEN(var);
339       }; // while
340       size =
341           size + 1; // Total size of env block, including terminating zero byte.
342     }
343 
344     // Copy original block to bulk, we will modify bulk, not original block.
345     bulk = (char *)allocate(size);
346     KMP_MEMCPY_S(bulk, size, env, size);
347     // Allocate vars array.
348     vars = (kmp_env_var_t *)allocate(count * sizeof(kmp_env_var_t));
349 
350     // Loop thru all the vars, now in bulk.
351     {
352       char *var; // Pointer to beginning of var.
353       int len; // Length of variable.
354       count = 0;
355       var = bulk;
356       len = KMP_STRLEN(var);
357       while (len != 0) {
358         // Save variable in vars array.
359         __kmp_str_split(var, '=', &name, &value);
360         vars[count].name = name;
361         vars[count].value = value;
362         ++count;
363         // Get the next var.
364         var = var + len + 1;
365         len = KMP_STRLEN(var);
366       }; // while
367     }
368 
369   }; // if
370 
371   // Fill out result.
372   block->bulk = bulk;
373   block->vars = vars;
374   block->count = count;
375 
376 }; // ___kmp_env_blk_parse_windows
377 #endif
378 
379 /* Unix environment block is a array of pointers to variables, last pointer in
380    array is NULL:
381 
382         { "HOME=/home/lev", "TERM=xterm", NULL }
383 */
384 
385 static void
386 ___kmp_env_blk_parse_unix(kmp_env_blk_t *block, // M: Env block to fill.
387                           char **env // I: Unix environment to parse.
388                           ) {
389 
390   char *bulk = NULL;
391   kmp_env_var_t *vars = NULL;
392   int count = 0;
393   int size = 0; // Size of bulk.
394 
395   // Count number of variables and length of required bulk.
396   {
397     count = 0;
398     size = 0;
399     while (env[count] != NULL) {
400       size += KMP_STRLEN(env[count]) + 1;
401       ++count;
402     }; // while
403   }
404 
405   // Allocate memory.
406   bulk = (char *)allocate(size);
407   vars = (kmp_env_var_t *)allocate(count * sizeof(kmp_env_var_t));
408 
409   // Loop thru all the vars.
410   {
411     char *var; // Pointer to beginning of var.
412     char *name; // Pointer to name of variable.
413     char *value; // Pointer to value.
414     int len; // Length of variable.
415     int i;
416     var = bulk;
417     for (i = 0; i < count; ++i) {
418       // Copy variable to bulk.
419       len = KMP_STRLEN(env[i]);
420       KMP_MEMCPY_S(var, size, env[i], len + 1);
421       // Save found variable in vars array.
422       __kmp_str_split(var, '=', &name, &value);
423       vars[i].name = name;
424       vars[i].value = value;
425       // Move pointer.
426       var += len + 1;
427     }; // for
428   }
429 
430   // Fill out result.
431   block->bulk = bulk;
432   block->vars = vars;
433   block->count = count;
434 
435 }; // ___kmp_env_blk_parse_unix
436 
437 void __kmp_env_blk_init(kmp_env_blk_t *block, // M: Block to initialize.
438                         char const *bulk // I: Initialization string, or NULL.
439                         ) {
440 
441   if (bulk != NULL) {
442     ___kmp_env_blk_parse_string(block, bulk);
443   } else {
444 #if KMP_OS_UNIX
445     ___kmp_env_blk_parse_unix(block, environ);
446 #elif KMP_OS_WINDOWS
447     {
448       char *mem = GetEnvironmentStrings();
449       if (mem == NULL) {
450         DWORD error = GetLastError();
451         __kmp_fatal(KMP_MSG(CantGetEnvironment), KMP_ERR(error),
452                     __kmp_msg_null);
453       }; // if
454       ___kmp_env_blk_parse_windows(block, mem);
455       FreeEnvironmentStrings(mem);
456     }
457 #else
458 #error Unknown or unsupported OS.
459 #endif
460   }; // if
461 
462 } // __kmp_env_blk_init
463 
464 static int ___kmp_env_var_cmp( // Comparison function for qsort().
465     kmp_env_var_t const *lhs, kmp_env_var_t const *rhs) {
466   return strcmp(lhs->name, rhs->name);
467 }
468 
469 void __kmp_env_blk_sort(
470     kmp_env_blk_t *block // M: Block of environment variables to sort.
471     ) {
472 
473   qsort(CCAST(kmp_env_var_t *, block->vars), block->count,
474         sizeof(kmp_env_var_t),
475         (int (*)(void const *, void const *)) & ___kmp_env_var_cmp);
476 
477 } // __kmp_env_block_sort
478 
479 void __kmp_env_blk_free(
480     kmp_env_blk_t *block // M: Block of environment variables to free.
481     ) {
482 
483   KMP_INTERNAL_FREE(CCAST(kmp_env_var_t *, block->vars));
484   __kmp_str_free(&(block->bulk));
485 
486   block->count = 0;
487   block->vars = NULL;
488 
489 } // __kmp_env_blk_free
490 
491 char const * // R: Value of variable or NULL if variable does not exist.
492     __kmp_env_blk_var(
493         kmp_env_blk_t *block, // I: Block of environment variables.
494         char const *name // I: Name of variable to find.
495         ) {
496 
497   int i;
498   for (i = 0; i < block->count; ++i) {
499     if (strcmp(block->vars[i].name, name) == 0) {
500       return block->vars[i].value;
501     }; // if
502   }; // for
503   return NULL;
504 
505 } // __kmp_env_block_var
506 
507 // end of file //
508