1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | /*++ |
6 | |
7 | |
8 | |
9 | Module Name: |
10 | |
11 | environ.cpp |
12 | |
13 | Abstract: |
14 | |
15 | Implementation of functions manipulating environment variables. |
16 | |
17 | Revision History: |
18 | |
19 | |
20 | |
21 | --*/ |
22 | |
23 | #include "pal/palinternal.h" |
24 | #include "pal/critsect.h" |
25 | #include "pal/dbgmsg.h" |
26 | #include "pal/environ.h" |
27 | #include "pal/malloc.hpp" |
28 | |
29 | #if HAVE_CRT_EXTERNS_H |
30 | #include <crt_externs.h> |
31 | #endif |
32 | |
33 | #include <stdlib.h> |
34 | |
35 | using namespace CorUnix; |
36 | |
37 | SET_DEFAULT_DEBUG_CHANNEL(MISC); |
38 | |
39 | char **palEnvironment = nullptr; |
40 | int palEnvironmentCount = 0; |
41 | int palEnvironmentCapacity = 0; |
42 | |
43 | CRITICAL_SECTION gcsEnvironment; |
44 | |
45 | /*++ |
46 | Function: |
47 | GetEnvironmentVariableA |
48 | |
49 | The GetEnvironmentVariable function retrieves the value of the |
50 | specified variable from the environment block of the calling |
51 | process. The value is in the form of a null-terminated string of |
52 | characters. |
53 | |
54 | Parameters |
55 | |
56 | lpName |
57 | [in] Pointer to a null-terminated string that specifies the environment variable. |
58 | lpBuffer |
59 | [out] Pointer to a buffer to receive the value of the specified environment variable. |
60 | nSize |
61 | [in] Specifies the size, in TCHARs, of the buffer pointed to by the lpBuffer parameter. |
62 | |
63 | Return Values |
64 | |
65 | If the function succeeds, the return value is the number of TCHARs |
66 | stored into the buffer pointed to by lpBuffer, not including the |
67 | terminating null character. |
68 | |
69 | If the specified environment variable name was not found in the |
70 | environment block for the current process, the return value is zero. |
71 | |
72 | If the buffer pointed to by lpBuffer is not large enough, the return |
73 | value is the buffer size, in TCHARs, required to hold the value string |
74 | and its terminating null character. |
75 | |
76 | --*/ |
77 | DWORD |
78 | PALAPI |
79 | GetEnvironmentVariableA( |
80 | IN LPCSTR lpName, |
81 | OUT LPSTR lpBuffer, |
82 | IN DWORD nSize) |
83 | { |
84 | char *value; |
85 | DWORD dwRet = 0; |
86 | |
87 | PERF_ENTRY(GetEnvironmentVariableA); |
88 | ENTRY("GetEnvironmentVariableA(lpName=%p (%s), lpBuffer=%p, nSize=%u)\n" , |
89 | lpName ? lpName : "NULL" , |
90 | lpName ? lpName : "NULL" , lpBuffer, nSize); |
91 | |
92 | CPalThread * pthrCurrent = InternalGetCurrentThread(); |
93 | |
94 | if (lpName == nullptr) |
95 | { |
96 | ERROR("lpName is null\n" ); |
97 | SetLastError(ERROR_INVALID_PARAMETER); |
98 | goto done; |
99 | } |
100 | |
101 | if (lpName[0] == 0) |
102 | { |
103 | TRACE("lpName is an empty string\n" , lpName); |
104 | SetLastError(ERROR_ENVVAR_NOT_FOUND); |
105 | goto done; |
106 | } |
107 | |
108 | if (strchr(lpName, '=') != nullptr) |
109 | { |
110 | // GetEnvironmentVariable doesn't permit '=' in variable names. |
111 | value = nullptr; |
112 | } |
113 | else |
114 | { |
115 | // Enter the environment critical section so that we can safely get |
116 | // the environment variable value without EnvironGetenv making an |
117 | // intermediate copy. We will just copy the string to the output |
118 | // buffer anyway, so just stay in the critical section until then. |
119 | InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); |
120 | |
121 | value = EnvironGetenv(lpName, /* copyValue */ FALSE); |
122 | |
123 | if (value != nullptr) |
124 | { |
125 | DWORD valueLength = strlen(value); |
126 | if (valueLength < nSize) |
127 | { |
128 | strcpy_s(lpBuffer, nSize, value); |
129 | dwRet = valueLength; |
130 | } |
131 | else |
132 | { |
133 | dwRet = valueLength + 1; |
134 | } |
135 | |
136 | SetLastError(ERROR_SUCCESS); |
137 | } |
138 | |
139 | InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); |
140 | } |
141 | |
142 | if (value == nullptr) |
143 | { |
144 | TRACE("%s is not found\n" , lpName); |
145 | SetLastError(ERROR_ENVVAR_NOT_FOUND); |
146 | } |
147 | |
148 | done: |
149 | |
150 | LOGEXIT("GetEnvironmentVariableA returns DWORD 0x%x\n" , dwRet); |
151 | PERF_EXIT(GetEnvironmentVariableA); |
152 | return dwRet; |
153 | } |
154 | |
155 | /*++ |
156 | Function: |
157 | GetEnvironmentVariableW |
158 | |
159 | See MSDN doc. |
160 | --*/ |
161 | DWORD |
162 | PALAPI |
163 | GetEnvironmentVariableW( |
164 | IN LPCWSTR lpName, |
165 | OUT LPWSTR lpBuffer, |
166 | IN DWORD nSize) |
167 | { |
168 | CHAR *inBuff = nullptr; |
169 | CHAR *outBuff = nullptr; |
170 | INT inBuffSize; |
171 | DWORD size = 0; |
172 | |
173 | PERF_ENTRY(GetEnvironmentVariableW); |
174 | ENTRY("GetEnvironmentVariableW(lpName=%p (%S), lpBuffer=%p, nSize=%u)\n" , |
175 | lpName ? lpName : W16_NULLSTRING, |
176 | lpName ? lpName : W16_NULLSTRING, lpBuffer, nSize); |
177 | |
178 | inBuffSize = WideCharToMultiByte(CP_ACP, 0, lpName, -1, |
179 | inBuff, 0, nullptr, nullptr); |
180 | if (0 == inBuffSize) |
181 | { |
182 | ERROR("lpName has to be a valid parameter\n" ); |
183 | SetLastError(ERROR_INVALID_PARAMETER); |
184 | goto done; |
185 | } |
186 | |
187 | inBuff = (CHAR *)PAL_malloc(inBuffSize); |
188 | if (inBuff == nullptr) |
189 | { |
190 | ERROR("malloc failed\n" ); |
191 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
192 | goto done; |
193 | } |
194 | |
195 | if (nSize) |
196 | { |
197 | outBuff = (CHAR *)PAL_malloc(nSize*2); |
198 | if (outBuff == nullptr) |
199 | { |
200 | ERROR("malloc failed\n" ); |
201 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
202 | goto done; |
203 | } |
204 | } |
205 | |
206 | if (0 == WideCharToMultiByte(CP_ACP, 0, lpName, -1, inBuff, |
207 | inBuffSize, nullptr, nullptr)) |
208 | { |
209 | ASSERT("WideCharToMultiByte failed!\n" ); |
210 | SetLastError(ERROR_INTERNAL_ERROR); |
211 | goto done; |
212 | } |
213 | |
214 | size = GetEnvironmentVariableA(inBuff, outBuff, nSize); |
215 | if (size > nSize) |
216 | { |
217 | TRACE("Insufficient buffer\n" ); |
218 | } |
219 | else if (size == 0) |
220 | { |
221 | // If size is 0, it either means GetEnvironmentVariableA failed, or that |
222 | // it succeeded and the value of the variable is empty. Check GetLastError |
223 | // to determine which. If the call failed, we won't touch the buffer. |
224 | if (GetLastError() == ERROR_SUCCESS) |
225 | { |
226 | *lpBuffer = '\0'; |
227 | } |
228 | } |
229 | else |
230 | { |
231 | size = MultiByteToWideChar(CP_ACP, 0, outBuff, -1, lpBuffer, nSize); |
232 | if (0 != size) |
233 | { |
234 | // -1 for the null. |
235 | size--; |
236 | } |
237 | else |
238 | { |
239 | ASSERT("MultiByteToWideChar failed!\n" ); |
240 | SetLastError(ERROR_INTERNAL_ERROR); |
241 | size = 0; |
242 | *lpBuffer = '\0'; |
243 | } |
244 | } |
245 | |
246 | done: |
247 | PAL_free(outBuff); |
248 | PAL_free(inBuff); |
249 | |
250 | LOGEXIT("GetEnvironmentVariableW returns DWORD 0x%x\n" , size); |
251 | PERF_EXIT(GetEnvironmentVariableW); |
252 | |
253 | return size; |
254 | } |
255 | |
256 | /*++ |
257 | Function: |
258 | SetEnvironmentVariableW |
259 | |
260 | The SetEnvironmentVariable function sets the value of an environment |
261 | variable for the current process. |
262 | |
263 | Parameters |
264 | |
265 | lpName |
266 | [in] Pointer to a null-terminated string that specifies the |
267 | environment variable whose value is being set. The operating |
268 | system creates the environment variable if it does not exist |
269 | and lpValue is not null. |
270 | lpValue |
271 | [in] Pointer to a null-terminated string containing the new |
272 | value of the specified environment variable. If this parameter |
273 | is null, the variable is deleted from the current process's |
274 | environment. |
275 | |
276 | Return Values |
277 | |
278 | If the function succeeds, the return value is nonzero. |
279 | |
280 | If the function fails, the return value is zero. To get extended error |
281 | information, call GetLastError. |
282 | |
283 | Remarks |
284 | |
285 | This function has no effect on the system environment variables or the |
286 | environment variables of other processes. |
287 | |
288 | --*/ |
289 | BOOL |
290 | PALAPI |
291 | SetEnvironmentVariableW( |
292 | IN LPCWSTR lpName, |
293 | IN LPCWSTR lpValue) |
294 | { |
295 | PCHAR name = nullptr; |
296 | PCHAR value = nullptr; |
297 | INT nameSize = 0; |
298 | INT valueSize = 0; |
299 | BOOL bRet = FALSE; |
300 | |
301 | PERF_ENTRY(SetEnvironmentVariableW); |
302 | ENTRY("SetEnvironmentVariableW(lpName=%p (%S), lpValue=%p (%S))\n" , |
303 | lpName?lpName:W16_NULLSTRING, |
304 | lpName?lpName:W16_NULLSTRING, lpValue?lpValue:W16_NULLSTRING, lpValue?lpValue:W16_NULLSTRING); |
305 | |
306 | if ((nameSize = WideCharToMultiByte(CP_ACP, 0, lpName, -1, name, 0, |
307 | nullptr, nullptr)) == 0) |
308 | { |
309 | ERROR("WideCharToMultiByte failed\n" ); |
310 | SetLastError(ERROR_INVALID_PARAMETER); |
311 | goto done; |
312 | } |
313 | |
314 | name = (PCHAR)PAL_malloc(sizeof(CHAR)* nameSize); |
315 | if (name == nullptr) |
316 | { |
317 | ERROR("malloc failed\n" ); |
318 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
319 | goto done; |
320 | } |
321 | |
322 | if (0 == WideCharToMultiByte(CP_ACP, 0, lpName, -1, |
323 | name, nameSize, nullptr, nullptr)) |
324 | { |
325 | ASSERT("WideCharToMultiByte returned 0\n" ); |
326 | SetLastError(ERROR_INTERNAL_ERROR); |
327 | goto done; |
328 | } |
329 | |
330 | if (lpValue != nullptr) |
331 | { |
332 | if ((valueSize = WideCharToMultiByte(CP_ACP, 0, lpValue, -1, value, |
333 | 0, nullptr, nullptr)) == 0) |
334 | { |
335 | ERROR("WideCharToMultiByte failed\n" ); |
336 | SetLastError(ERROR_INVALID_PARAMETER); |
337 | goto done; |
338 | } |
339 | |
340 | value = (PCHAR)PAL_malloc(sizeof(CHAR)*valueSize); |
341 | |
342 | if (value == nullptr) |
343 | { |
344 | ERROR("malloc failed\n" ); |
345 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
346 | goto done; |
347 | } |
348 | |
349 | if (0 == WideCharToMultiByte(CP_ACP, 0, lpValue, -1, |
350 | value, valueSize, nullptr, nullptr)) |
351 | { |
352 | ASSERT("WideCharToMultiByte failed\n" ); |
353 | SetLastError( ERROR_INTERNAL_ERROR ); |
354 | goto done; |
355 | } |
356 | } |
357 | |
358 | bRet = SetEnvironmentVariableA(name, value); |
359 | done: |
360 | PAL_free(value); |
361 | PAL_free(name); |
362 | |
363 | LOGEXIT("SetEnvironmentVariableW returning BOOL %d\n" , bRet); |
364 | PERF_EXIT(SetEnvironmentVariableW); |
365 | return bRet; |
366 | } |
367 | |
368 | /*++ |
369 | Function: |
370 | GetEnvironmentStringsW |
371 | |
372 | The GetEnvironmentStrings function retrieves the environment block for |
373 | the current process. |
374 | |
375 | Parameters |
376 | |
377 | This function has no parameters. |
378 | |
379 | Return Values |
380 | |
381 | The return value is a pointer to an environment block for the current process. |
382 | |
383 | Remarks |
384 | |
385 | The GetEnvironmentStrings function returns a pointer to the |
386 | environment block of the calling process. This should be treated as a |
387 | read-only block; do not modify it directly. Instead, use the |
388 | GetEnvironmentVariable and SetEnvironmentVariable functions to |
389 | retrieve or change the environment variables within this block. When |
390 | the block is no longer needed, it should be freed by calling |
391 | FreeEnvironmentStrings. |
392 | |
393 | --*/ |
394 | LPWSTR |
395 | PALAPI |
396 | GetEnvironmentStringsW( |
397 | VOID) |
398 | { |
399 | WCHAR *wenviron = nullptr, *tempEnviron; |
400 | int i, len, envNum; |
401 | |
402 | PERF_ENTRY(GetEnvironmentStringsW); |
403 | ENTRY("GetEnvironmentStringsW()\n" ); |
404 | |
405 | CPalThread * pthrCurrent = InternalGetCurrentThread(); |
406 | InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); |
407 | |
408 | envNum = 0; |
409 | len = 0; |
410 | |
411 | /* get total length of the bytes that we need to allocate */ |
412 | for (i = 0; palEnvironment[i] != 0; i++) |
413 | { |
414 | len = MultiByteToWideChar(CP_ACP, 0, palEnvironment[i], -1, wenviron, 0); |
415 | envNum += len; |
416 | } |
417 | |
418 | wenviron = (WCHAR *)PAL_malloc(sizeof(WCHAR)* (envNum + 1)); |
419 | if (wenviron == nullptr) |
420 | { |
421 | ERROR("malloc failed\n" ); |
422 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
423 | goto EXIT; |
424 | } |
425 | |
426 | len = 0; |
427 | tempEnviron = wenviron; |
428 | for (i = 0; palEnvironment[i] != 0; i++) |
429 | { |
430 | len = MultiByteToWideChar(CP_ACP, 0, palEnvironment[i], -1, tempEnviron, envNum); |
431 | tempEnviron += len; |
432 | envNum -= len; |
433 | } |
434 | |
435 | *tempEnviron = 0; /* Put an extra null at the end */ |
436 | |
437 | EXIT: |
438 | InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); |
439 | |
440 | LOGEXIT("GetEnvironmentStringsW returning %p\n" , wenviron); |
441 | PERF_EXIT(GetEnvironmentStringsW); |
442 | return wenviron; |
443 | } |
444 | |
445 | /*++ |
446 | Function: |
447 | GetEnvironmentStringsA |
448 | |
449 | See GetEnvironmentStringsW. |
450 | |
451 | --*/ |
452 | LPSTR |
453 | PALAPI |
454 | GetEnvironmentStringsA( |
455 | VOID) |
456 | { |
457 | char *environ = nullptr, *tempEnviron; |
458 | int i, len, envNum; |
459 | |
460 | PERF_ENTRY(GetEnvironmentStringsA); |
461 | ENTRY("GetEnvironmentStringsA()\n" ); |
462 | |
463 | CPalThread * pthrCurrent = InternalGetCurrentThread(); |
464 | InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); |
465 | |
466 | envNum = 0; |
467 | len = 0; |
468 | |
469 | /* get total length of the bytes that we need to allocate */ |
470 | for (i = 0; palEnvironment[i] != 0; i++) |
471 | { |
472 | len = strlen(palEnvironment[i]) + 1; |
473 | envNum += len; |
474 | } |
475 | |
476 | environ = (char *)PAL_malloc(envNum + 1); |
477 | if (environ == nullptr) |
478 | { |
479 | ERROR("malloc failed\n" ); |
480 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
481 | goto EXIT; |
482 | } |
483 | |
484 | len = 0; |
485 | tempEnviron = environ; |
486 | for (i = 0; palEnvironment[i] != 0; i++) |
487 | { |
488 | len = strlen(palEnvironment[i]) + 1; |
489 | memcpy(tempEnviron, palEnvironment[i], len); |
490 | tempEnviron += len; |
491 | envNum -= len; |
492 | } |
493 | |
494 | *tempEnviron = 0; /* Put an extra null at the end */ |
495 | |
496 | EXIT: |
497 | InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); |
498 | |
499 | LOGEXIT("GetEnvironmentStringsA returning %p\n" , environ); |
500 | PERF_EXIT(GetEnvironmentStringsA); |
501 | return environ; |
502 | } |
503 | |
504 | /*++ |
505 | Function: |
506 | FreeEnvironmentStringsW |
507 | |
508 | The FreeEnvironmentStrings function frees a block of environment strings. |
509 | |
510 | Parameters |
511 | |
512 | lpszEnvironmentBlock [in] Pointer to a block of environment strings. The pointer to |
513 | the block must be obtained by calling the |
514 | GetEnvironmentStrings function. |
515 | |
516 | Return Values |
517 | |
518 | If the function succeeds, the return value is nonzero. If the |
519 | function fails, the return value is zero. To get extended error |
520 | information, call GetLastError. |
521 | |
522 | Remarks |
523 | |
524 | When GetEnvironmentStrings is called, it allocates memory for a block |
525 | of environment strings. When the block is no longer needed, it should |
526 | be freed by calling FreeEnvironmentStrings. |
527 | |
528 | --*/ |
529 | BOOL |
530 | PALAPI |
531 | FreeEnvironmentStringsW( |
532 | IN LPWSTR lpValue) |
533 | { |
534 | PERF_ENTRY(FreeEnvironmentStringsW); |
535 | ENTRY("FreeEnvironmentStringsW(lpValue=%p (%S))\n" , lpValue ? lpValue : W16_NULLSTRING, lpValue ? lpValue : W16_NULLSTRING); |
536 | |
537 | if (lpValue != nullptr) |
538 | { |
539 | PAL_free(lpValue); |
540 | } |
541 | |
542 | LOGEXIT("FreeEnvironmentStringW returning BOOL TRUE\n" ); |
543 | PERF_EXIT(FreeEnvironmentStringsW); |
544 | return TRUE; |
545 | } |
546 | |
547 | /*++ |
548 | Function: |
549 | FreeEnvironmentStringsA |
550 | |
551 | See FreeEnvironmentStringsW. |
552 | |
553 | --*/ |
554 | BOOL |
555 | PALAPI |
556 | FreeEnvironmentStringsA( |
557 | IN LPSTR lpValue) |
558 | { |
559 | PERF_ENTRY(FreeEnvironmentStringsA); |
560 | ENTRY("FreeEnvironmentStringsA(lpValue=%p (%s))\n" , lpValue ? lpValue : "NULL" , lpValue ? lpValue : "NULL" ); |
561 | |
562 | if (lpValue != nullptr) |
563 | { |
564 | PAL_free(lpValue); |
565 | } |
566 | |
567 | LOGEXIT("FreeEnvironmentStringA returning BOOL TRUE\n" ); |
568 | PERF_EXIT(FreeEnvironmentStringsA); |
569 | return TRUE; |
570 | } |
571 | |
572 | /*++ |
573 | Function: |
574 | SetEnvironmentVariableA |
575 | |
576 | The SetEnvironmentVariable function sets the value of an environment |
577 | variable for the current process. |
578 | |
579 | Parameters |
580 | |
581 | lpName |
582 | [in] Pointer to a null-terminated string that specifies the |
583 | environment variable whose value is being set. The operating |
584 | system creates the environment variable if it does not exist |
585 | and lpValue is not null. |
586 | lpValue |
587 | [in] Pointer to a null-terminated string containing the new |
588 | value of the specified environment variable. If this parameter |
589 | is null, the variable is deleted from the current process's |
590 | environment. |
591 | |
592 | Return Values |
593 | |
594 | If the function succeeds, the return value is nonzero. |
595 | |
596 | If the function fails, the return value is zero. To get extended error |
597 | information, call GetLastError. |
598 | |
599 | Remarks |
600 | |
601 | This function has no effect on the system environment variables or the |
602 | environment variables of other processes. |
603 | |
604 | --*/ |
605 | BOOL |
606 | PALAPI |
607 | SetEnvironmentVariableA( |
608 | IN LPCSTR lpName, |
609 | IN LPCSTR lpValue) |
610 | { |
611 | |
612 | BOOL bRet = FALSE; |
613 | int nResult =0; |
614 | PERF_ENTRY(SetEnvironmentVariableA); |
615 | ENTRY("SetEnvironmentVariableA(lpName=%p (%s), lpValue=%p (%s))\n" , |
616 | lpName ? lpName : "NULL" , lpName ? lpName : "NULL" , |
617 | lpValue ? lpValue : "NULL" , lpValue ? lpValue : "NULL" ); |
618 | |
619 | // exit if the input variable name is null |
620 | if ((lpName == nullptr) || (lpName[0] == 0)) |
621 | { |
622 | ERROR("lpName is null\n" ); |
623 | goto done; |
624 | } |
625 | |
626 | /* check if the input value is null and if so |
627 | * check if the input name is valid and delete |
628 | * the variable name from process environment */ |
629 | if (lpValue == nullptr) |
630 | { |
631 | // We tell EnvironGetenv not to bother with making a copy of the |
632 | // value since we're not going to use it for anything interesting |
633 | // apart from checking whether it's null. |
634 | if ((lpValue = EnvironGetenv(lpName, /* copyValue */ FALSE)) == nullptr) |
635 | { |
636 | ERROR("Couldn't find environment variable (%s)\n" , lpName); |
637 | SetLastError(ERROR_ENVVAR_NOT_FOUND); |
638 | goto done; |
639 | } |
640 | |
641 | EnvironUnsetenv(lpName); |
642 | } |
643 | else |
644 | { |
645 | // All the conditions are met. Set the variable. |
646 | int iLen = strlen(lpName) + strlen(lpValue) + 2; |
647 | LPSTR string = (LPSTR) PAL_malloc(iLen); |
648 | if (string == nullptr) |
649 | { |
650 | bRet = FALSE; |
651 | ERROR("Unable to allocate memory\n" ); |
652 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
653 | goto done; |
654 | } |
655 | |
656 | sprintf_s(string, iLen, "%s=%s" , lpName, lpValue); |
657 | nResult = EnvironPutenv(string, FALSE) ? 0 : -1; |
658 | |
659 | PAL_free(string); |
660 | string = nullptr; |
661 | |
662 | // If EnvironPutenv returns FALSE, it almost certainly failed to allocate memory. |
663 | if (nResult == -1) |
664 | { |
665 | bRet = FALSE; |
666 | ERROR("Unable to allocate memory\n" ); |
667 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
668 | goto done; |
669 | } |
670 | } |
671 | |
672 | bRet = TRUE; |
673 | |
674 | done: |
675 | LOGEXIT("SetEnvironmentVariableA returning BOOL %d\n" , bRet); |
676 | PERF_EXIT(SetEnvironmentVariableA); |
677 | return bRet; |
678 | } |
679 | |
680 | /*++ |
681 | Function: |
682 | ResizeEnvironment |
683 | |
684 | Resizes the PAL environment buffer. |
685 | |
686 | Parameters |
687 | |
688 | newSize |
689 | [in] New size of palEnvironment |
690 | |
691 | Return Values |
692 | |
693 | TRUE on success, FALSE otherwise |
694 | |
695 | --*/ |
696 | BOOL ResizeEnvironment(int newSize) |
697 | { |
698 | CPalThread * pthrCurrent = InternalGetCurrentThread(); |
699 | InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); |
700 | |
701 | BOOL ret = FALSE; |
702 | if (newSize >= palEnvironmentCount) |
703 | { |
704 | // If palEnvironment is null, realloc acts like malloc. |
705 | char **newEnvironment = (char**)realloc(palEnvironment, newSize * sizeof(char *)); |
706 | if (newEnvironment != nullptr) |
707 | { |
708 | // realloc succeeded, so set palEnvironment to what it returned. |
709 | palEnvironment = newEnvironment; |
710 | palEnvironmentCapacity = newSize; |
711 | ret = TRUE; |
712 | } |
713 | } |
714 | else |
715 | { |
716 | ASSERT("ResizeEnvironment: newSize < current palEnvironmentCount!\n" ); |
717 | } |
718 | |
719 | InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); |
720 | return ret; |
721 | } |
722 | |
723 | /*++ |
724 | Function: |
725 | EnvironUnsetenv |
726 | |
727 | Remove the environment variable with the given name from the PAL version |
728 | of the environment if it exists. |
729 | |
730 | Parameters |
731 | |
732 | name |
733 | [in] Name of variable to unset. |
734 | |
735 | --*/ |
736 | void EnvironUnsetenv(const char *name) |
737 | { |
738 | int nameLength = strlen(name); |
739 | |
740 | CPalThread * pthrCurrent = InternalGetCurrentThread(); |
741 | InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); |
742 | |
743 | for (int i = 0; palEnvironment[i] != nullptr; ++i) |
744 | { |
745 | const char *equalsSignPosition = strchr(palEnvironment[i], '='); |
746 | if (equalsSignPosition == nullptr) |
747 | { |
748 | equalsSignPosition = palEnvironment[i] + strlen(palEnvironment[i]); |
749 | } |
750 | |
751 | // Check whether the name of this variable has the same length as the one |
752 | // we're looking for before proceeding to compare them. |
753 | if (equalsSignPosition - palEnvironment[i] == nameLength) |
754 | { |
755 | if (memcmp(name, palEnvironment[i], nameLength) == 0) |
756 | { |
757 | // Free the string we're removing. |
758 | free(palEnvironment[i]); |
759 | |
760 | // Move the last environment variable pointer here. |
761 | palEnvironment[i] = palEnvironment[palEnvironmentCount - 1]; |
762 | palEnvironment[palEnvironmentCount - 1] = nullptr; |
763 | |
764 | palEnvironmentCount--; |
765 | } |
766 | } |
767 | } |
768 | |
769 | InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); |
770 | } |
771 | |
772 | /*++ |
773 | Function: |
774 | EnvironPutenv |
775 | |
776 | Add the environment variable string provided to the PAL version |
777 | of the environment. |
778 | |
779 | Parameters |
780 | |
781 | entry |
782 | [in] The variable string to add. Should be in the format |
783 | "name=value", where value might be empty (see below). |
784 | deleteIfEmpty |
785 | [in] If this is TRUE, "name=" will unset the 'name' variable. |
786 | |
787 | Return Values |
788 | |
789 | TRUE on success, FALSE otherwise |
790 | |
791 | --*/ |
792 | BOOL EnvironPutenv(const char* entry, BOOL deleteIfEmpty) |
793 | { |
794 | BOOL result = FALSE; |
795 | |
796 | bool fOwningCS = false; |
797 | |
798 | CPalThread * pthrCurrent = InternalGetCurrentThread(); |
799 | |
800 | const char *equalsSignPosition = strchr(entry, '='); |
801 | if (equalsSignPosition == entry || equalsSignPosition == nullptr) |
802 | { |
803 | // "=foo" and "foo" have no meaning |
804 | return FALSE; |
805 | } |
806 | |
807 | char* copy = strdup(entry); |
808 | if (copy == nullptr) |
809 | { |
810 | return FALSE; |
811 | } |
812 | |
813 | int nameLength = equalsSignPosition - entry; |
814 | |
815 | if (equalsSignPosition[1] == '\0' && deleteIfEmpty) |
816 | { |
817 | // "foo=" removes foo from the environment in _putenv() on Windows. |
818 | // The same string can result from a call to SetEnvironmentVariable() |
819 | // with the empty string as the value, but in that case we want to |
820 | // set the variable's value to "". deleteIfEmpty will be FALSE in |
821 | // that case. |
822 | |
823 | // Change '=' to '\0' |
824 | copy[nameLength] = '\0'; |
825 | |
826 | EnvironUnsetenv(copy); |
827 | free(copy); |
828 | |
829 | result = TRUE; |
830 | } |
831 | else |
832 | { |
833 | // See if we are replacing an item or adding one. |
834 | |
835 | InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); |
836 | fOwningCS = true; |
837 | |
838 | int i; |
839 | for (i = 0; palEnvironment[i] != nullptr; i++) |
840 | { |
841 | const char *existingEquals = strchr(palEnvironment[i], '='); |
842 | if (existingEquals == nullptr) |
843 | { |
844 | // The PAL screens out malformed strings, but the strings which |
845 | // came from the system during initialization might not have the |
846 | // equals sign. We treat the entire string as a name in that case. |
847 | existingEquals = palEnvironment[i] + strlen(palEnvironment[i]); |
848 | } |
849 | |
850 | if (existingEquals - palEnvironment[i] == nameLength) |
851 | { |
852 | if (memcmp(entry, palEnvironment[i], nameLength) == 0) |
853 | { |
854 | free(palEnvironment[i]); |
855 | palEnvironment[i] = copy; |
856 | |
857 | result = TRUE; |
858 | break; |
859 | } |
860 | } |
861 | } |
862 | |
863 | if (palEnvironment[i] == nullptr) |
864 | { |
865 | _ASSERTE(i < palEnvironmentCapacity); |
866 | if (i == (palEnvironmentCapacity - 1)) |
867 | { |
868 | // We found the first null, but it's the last element in our environment |
869 | // block. We need more space in our environment, so let's double its size. |
870 | int resizeRet = ResizeEnvironment(palEnvironmentCapacity * 2); |
871 | if (resizeRet != TRUE) |
872 | { |
873 | free(copy); |
874 | goto done; |
875 | } |
876 | } |
877 | |
878 | _ASSERTE(copy != nullptr); |
879 | palEnvironment[i] = copy; |
880 | palEnvironment[i + 1] = nullptr; |
881 | palEnvironmentCount++; |
882 | |
883 | result = TRUE; |
884 | } |
885 | } |
886 | done: |
887 | |
888 | if (fOwningCS) |
889 | { |
890 | InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); |
891 | } |
892 | |
893 | return result; |
894 | } |
895 | |
896 | /*++ |
897 | Function: |
898 | EnvironGetenv |
899 | |
900 | Get the value of environment variable with the given name. |
901 | |
902 | Parameters |
903 | |
904 | name |
905 | [in] The name of the environment variable to get. |
906 | copyValue |
907 | [in] If this is TRUE, the function will make a copy of the |
908 | value and return a pointer to that. Otherwise, it will |
909 | return a pointer to the value in the PAL environment |
910 | directly. Calling this function with copyValue set to |
911 | FALSE is therefore unsafe without taking special pre- |
912 | cautions since the pointer may point to garbage later. |
913 | |
914 | Return Value |
915 | |
916 | A pointer to the value of the environment variable if it exists, |
917 | or nullptr otherwise. |
918 | |
919 | --*/ |
920 | char* EnvironGetenv(const char* name, BOOL copyValue) |
921 | { |
922 | char *retValue = nullptr; |
923 | |
924 | CPalThread * pthrCurrent = InternalGetCurrentThread(); |
925 | InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); |
926 | |
927 | int nameLength = strlen(name); |
928 | for (int i = 0; palEnvironment[i] != nullptr; ++i) |
929 | { |
930 | if (strlen(palEnvironment[i]) < nameLength) |
931 | { |
932 | continue; |
933 | } |
934 | |
935 | if (memcmp(palEnvironment[i], name, nameLength) == 0) |
936 | { |
937 | char *equalsSignPosition = palEnvironment[i] + nameLength; |
938 | |
939 | // If this is one of the variables which has no equals sign, we |
940 | // treat the whole thing as name, so the value is an empty string. |
941 | if (*equalsSignPosition == '\0') |
942 | { |
943 | retValue = (char *)"" ; |
944 | break; |
945 | } |
946 | else if (*equalsSignPosition == '=') |
947 | { |
948 | retValue = equalsSignPosition + 1; |
949 | break; |
950 | } |
951 | } |
952 | } |
953 | |
954 | if ((retValue != nullptr) && copyValue) |
955 | { |
956 | retValue = strdup(retValue); |
957 | } |
958 | |
959 | InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); |
960 | return retValue; |
961 | } |
962 | |
963 | /*++ |
964 | Function: |
965 | EnvironGetSystemEnvironment |
966 | |
967 | Get a pointer to the array of pointers representing the process's |
968 | environment. |
969 | |
970 | See 'man environ' for details. |
971 | |
972 | Return Value |
973 | |
974 | A pointer to the environment. |
975 | |
976 | --*/ |
977 | char** EnvironGetSystemEnvironment() |
978 | { |
979 | char** sysEnviron; |
980 | |
981 | #if HAVE__NSGETENVIRON |
982 | sysEnviron = *(_NSGetEnviron()); |
983 | #else // HAVE__NSGETENVIRON |
984 | extern char **environ; |
985 | sysEnviron = environ; |
986 | #endif // HAVE__NSGETENVIRON |
987 | |
988 | return sysEnviron; |
989 | } |
990 | |
991 | /*++ |
992 | Function: |
993 | EnvironInitialize |
994 | |
995 | Initialization function called from PAL_Initialize. |
996 | |
997 | Note: This is called before debug channels are initialized, so it |
998 | cannot use debug tracing calls. |
999 | --*/ |
1000 | BOOL |
1001 | EnvironInitialize(void) |
1002 | { |
1003 | BOOL ret = FALSE; |
1004 | |
1005 | InternalInitializeCriticalSection(&gcsEnvironment); |
1006 | |
1007 | CPalThread * pthrCurrent = InternalGetCurrentThread(); |
1008 | InternalEnterCriticalSection(pthrCurrent, &gcsEnvironment); |
1009 | |
1010 | char** sourceEnviron = EnvironGetSystemEnvironment(); |
1011 | |
1012 | int variableCount = 0; |
1013 | while (sourceEnviron[variableCount] != nullptr) |
1014 | variableCount++; |
1015 | |
1016 | palEnvironmentCount = 0; |
1017 | |
1018 | // We need to decide how much space to allocate. Since we need enough |
1019 | // space for all of the 'n' current environment variables, but we don't |
1020 | // know how many more there will be, we will initially make room for |
1021 | // '2n' variables. If even more are added, we will resize again. |
1022 | // If there are no variables, we will still make room for 1 entry to |
1023 | // store a nullptr there. |
1024 | int initialSize = (variableCount == 0) ? 1 : variableCount * 2; |
1025 | |
1026 | ret = ResizeEnvironment(initialSize); |
1027 | if (ret == TRUE) |
1028 | { |
1029 | _ASSERTE(palEnvironment != nullptr); |
1030 | for (int i = 0; i < variableCount; ++i) |
1031 | { |
1032 | palEnvironment[i] = strdup(sourceEnviron[i]); |
1033 | palEnvironmentCount++; |
1034 | } |
1035 | |
1036 | // Set the entry after the last variable to null to indicate the end. |
1037 | palEnvironment[variableCount] = nullptr; |
1038 | } |
1039 | |
1040 | InternalLeaveCriticalSection(pthrCurrent, &gcsEnvironment); |
1041 | return ret; |
1042 | } |
1043 | |
1044 | /*++ |
1045 | |
1046 | Function : _putenv. |
1047 | |
1048 | See MSDN for more details. |
1049 | |
1050 | Note: The BSD implementation can cause |
1051 | memory leaks. See man pages for more details. |
1052 | --*/ |
1053 | int |
1054 | __cdecl |
1055 | _putenv( const char * envstring ) |
1056 | { |
1057 | int ret = -1; |
1058 | |
1059 | PERF_ENTRY(_putenv); |
1060 | ENTRY( "_putenv( %p (%s) )\n" , envstring ? envstring : "NULL" , envstring ? envstring : "NULL" ) ; |
1061 | |
1062 | if (envstring != nullptr) |
1063 | { |
1064 | ret = EnvironPutenv(envstring, TRUE) ? 0 : -1; |
1065 | } |
1066 | else |
1067 | { |
1068 | ERROR( "_putenv() called with NULL envstring!\n" ); |
1069 | } |
1070 | |
1071 | LOGEXIT( "_putenv returning %d\n" , ret); |
1072 | PERF_EXIT(_putenv); |
1073 | return ret; |
1074 | } |
1075 | |
1076 | /*++ |
1077 | |
1078 | Function : PAL_getenv |
1079 | |
1080 | See MSDN for more details. |
1081 | --*/ |
1082 | char * __cdecl PAL_getenv(const char *varname) |
1083 | { |
1084 | char *retval; |
1085 | |
1086 | PERF_ENTRY(getenv); |
1087 | ENTRY("getenv (%p (%s))\n" , varname ? varname : "NULL" , varname ? varname : "NULL" ); |
1088 | |
1089 | if (strcmp(varname, "" ) == 0) |
1090 | { |
1091 | ERROR("getenv called with a empty variable name\n" ); |
1092 | LOGEXIT("getenv returning NULL\n" ); |
1093 | PERF_EXIT(getenv); |
1094 | return(NULL); |
1095 | } |
1096 | |
1097 | retval = EnvironGetenv(varname); |
1098 | |
1099 | LOGEXIT("getenv returning %p\n" , retval); |
1100 | PERF_EXIT(getenv); |
1101 | return(retval); |
1102 | } |
1103 | |