1 //
2 // environment_initialization.cpp
3 //
4 //      Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // Defines functions for initializing and uninitializing the global environments
7 // and for constructing and destroying environments.  The logic for manipulating
8 // the environment data structures is split across this file and the setenv.cpp
9 // file.
10 //
11 #include <corecrt_internal_traits.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 
16 
17 // The global environment data.  The initial environments store the pointer to
18 // the environment that is passed to main or wmain.  This is used only to
19 // ensure that we do not modify that environment block after we pass it to
20 // user code.  The _environ_table and _wenviron_table hold the current CRT environment.
21 // Their names cannot change; they are publicly documented.
22 extern "C"
23 {
24     char**    __dcrt_initial_narrow_environment = nullptr;
25     wchar_t** __dcrt_initial_wide_environment   = nullptr;
26 
27     __crt_state_management::dual_state_global<char**>    _environ_table;
28     __crt_state_management::dual_state_global<wchar_t**> _wenviron_table;
29 
__p__environ()30     char***    __cdecl __p__environ()  { return &_environ_table.value();  }
__p__wenviron()31     wchar_t*** __cdecl __p__wenviron() { return &_wenviron_table.value(); }
32 }
33 
34 
35 
36 _Ret_opt_z_
get_environment_nolock(char)37 static char**&    get_environment_nolock(char)    throw() { return _environ_table.value();  }
38 
39 _Ret_opt_z_
get_environment_nolock(wchar_t)40 static wchar_t**& get_environment_nolock(wchar_t) throw() { return _wenviron_table.value(); }
41 
42 _Ret_opt_z_
get_initial_environment(char)43 static char**&    __cdecl get_initial_environment(char)    throw() { return __dcrt_initial_narrow_environment; }
44 
45 _Ret_opt_z_
get_initial_environment(wchar_t)46 static wchar_t**& __cdecl get_initial_environment(wchar_t) throw() { return __dcrt_initial_wide_environment;   }
47 
get_dual_state_environment_nolock(char)48 static __crt_state_management::dual_state_global<char**>&    get_dual_state_environment_nolock(char)    throw() { return _environ_table;  }
get_dual_state_environment_nolock(wchar_t)49 static __crt_state_management::dual_state_global<wchar_t**>& get_dual_state_environment_nolock(wchar_t) throw() { return _wenviron_table; }
50 
51 
52 
53 // Counts the number of environment variables in the provided 'environment_block',
54 // excluding those that start with '=' (these are drive letter settings).
55 template <typename Character>
count_variables_in_environment_block(Character * const environment_block)56 static size_t const count_variables_in_environment_block(Character* const environment_block) throw()
57 {
58     typedef __crt_char_traits<Character> traits;
59 
60     // Count the number of variables in the environment block, ignoring drive
61     // letter settings, which begin with '=':
62     size_t count = 0;
63 
64     Character* it = environment_block;
65     while (*it != '\0')
66     {
67         if (*it != '=')
68             ++count;
69 
70         // This advances the iterator to the next string:
71         it += traits::tcslen(it) + 1;
72     }
73 
74     return count;
75 }
76 
77 
78 
79 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
80 //
81 // Environment Create and Free (These do not modify any global data)
82 //
83 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
84 // Frees the environment pointed-to by 'environment'.  This function ensures that
85 // each of the environment strings is freed and that the array itself is freed.
86 // This function requires that 'environment' is either nullptr or that it points
87 // to a valid environment (i.e., one composed of a sequence of zero or more non-
88 // null pointers terminated by a null pointer).
89 template <typename Character>
free_environment(Character ** const environment)90 static void free_environment(Character** const environment) throw()
91 {
92     if (!environment)
93         return;
94 
95     for (Character** it = environment; *it; ++it)
96         _free_crt(*it);
97 
98     _free_crt(environment);
99 }
100 
101 
102 
103 // Creates a new environment, populating it with the strings from the provided
104 // 'environment_block', which must be a double-null-terminated sequence of
105 // environment variable strings of the form "name=value".  Variables beginning
106 // with '=' are ignored (these are drive letter settings).  Returns the newly
107 // created environment on succes; returns nullptr on failure.
108 template <typename Character>
create_environment(Character * const environment_block)109 static Character** const create_environment(Character* const environment_block) throw()
110 {
111     typedef __crt_char_traits<Character> traits;
112 
113     size_t const variable_count = count_variables_in_environment_block(environment_block);
114 
115     __crt_unique_heap_ptr<Character*> environment(_calloc_crt_t(Character*, variable_count + 1));
116     if (!environment)
117         return nullptr;
118 
119     Character*  source_it = environment_block;
120     Character** result_it = environment.get();
121 
122     while (*source_it != '\0')
123     {
124         size_t const required_count = traits::tcslen(source_it) + 1;
125 
126         // Don't copy drive letter settings, which start with '=':
127         if (*source_it != '=')
128         {
129             __crt_unique_heap_ptr<Character> variable(_calloc_crt_t(Character, required_count));
130             if (!variable)
131             {
132                 free_environment(environment.detach());
133                 return nullptr;
134             }
135 
136             _ERRCHECK(traits::tcscpy_s(variable.get(), required_count, source_it));
137             *result_it++ = variable.detach();
138         }
139 
140         // This advances the iterator to the next string:
141         source_it += required_count;
142     }
143 
144     // The sequence of pointers is already null-terminated; return it:
145     return environment.detach();
146 }
147 
148 
149 
150 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
151 //
152 // Environment Initialize and Uninitialize
153 //
154 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
155 // In the initialize function below, we need to ensure that we've initialized
156 // the mbc table before we start performing character transformations.
pre_initialize(char)157 static void pre_initialize(char)    throw() { __acrt_initialize_multibyte(); }
pre_initialize(wchar_t)158 static void pre_initialize(wchar_t) throw() { /* no-op */                    }
159 
160 
161 
162 // Gets the current environment from the operating system and initializes the
163 // CRT environment from that environment.  Returns 0 on success; -1 on failure.
164 // If this function returns successfully, the global environment pointer for
165 // the requested environment will be non-null and valid.
166 template <typename Character>
common_initialize_environment_nolock()167 static int __cdecl common_initialize_environment_nolock() throw()
168 {
169     typedef __crt_char_traits<Character> traits;
170 
171     // We only initialize the environment once.  Once the environment has been
172     // initialized, all updates and modifications go through the other functions
173     // that manipulate the environment.
174     if (get_environment_nolock(Character()))
175         return 0;
176 
177     pre_initialize(Character());
178 
179     __crt_unique_heap_ptr<Character> const os_environment(traits::get_environment_from_os());
180     if (!os_environment)
181         return -1;
182 
183     __crt_unique_heap_ptr<Character*> crt_environment(create_environment(os_environment.get()));
184     if (!crt_environment)
185         return -1;
186 
187     get_initial_environment(Character()) = crt_environment.get();
188     get_dual_state_environment_nolock(Character()).initialize(crt_environment.detach());
189     return 0;
190 }
191 
_initialize_narrow_environment()192 extern "C" int __cdecl _initialize_narrow_environment()
193 {
194     return common_initialize_environment_nolock<char>();
195 }
196 
_initialize_wide_environment()197 extern "C" int __cdecl _initialize_wide_environment()
198 {
199     return common_initialize_environment_nolock<wchar_t>();
200 }
201 
202 
203 
204 // Frees the global wide and narrow environments and returns.
205 template <typename Character>
uninitialize_environment_internal(Character ** & environment)206 static void __cdecl uninitialize_environment_internal(Character**& environment) throw()
207 {
208     if (environment == get_initial_environment(Character()))
209     {
210         return;
211     }
212 
213     free_environment(environment);
214 }
215 
__dcrt_uninitialize_environments_nolock()216 extern "C" void __cdecl __dcrt_uninitialize_environments_nolock()
217 {
218     _environ_table .uninitialize(uninitialize_environment_internal<char>);
219     _wenviron_table.uninitialize(uninitialize_environment_internal<wchar_t>);
220 
221     free_environment(__dcrt_initial_narrow_environment);
222     free_environment(__dcrt_initial_wide_environment);
223 }
224 
225 
226 
227 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
228 //
229 // Environment Get-Or-Create
230 //
231 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
232 // These functions help with synchronization between the narrow and wide
233 // environments.  If the requested environment has not yet been initialized but
234 // the other environment has been initialized, the other environment is cloned
235 // to create the requested environment.  Note that if the other environment has
236 // not yet been initialized, these functions do not do anything.
237 
238 // Gets the other environment and copies each of the environment variables from
239 // it into the requested environment.  Returns 0 on success; -1 on failure.
240 template <typename Character>
initialize_environment_by_cloning_nolock()241 static int __cdecl initialize_environment_by_cloning_nolock() throw()
242 {
243     typedef __crt_char_traits<Character> traits;
244     typedef typename traits::other_char_type other_char_type;
245 
246     other_char_type** const other_environment = get_environment_nolock(other_char_type());
247     if (!other_environment)
248         return -1;
249 
250     for (other_char_type** it = other_environment; *it; ++it)
251     {
252         size_t const required_count = __crt_compute_required_transform_buffer_count(CP_ACP, *it);
253         if (required_count == 0)
254             return -1;
255 
256         __crt_unique_heap_ptr<Character> buffer(_calloc_crt_t(Character, required_count));
257         if (!buffer)
258             return -1;
259 
260         size_t const actual_count = __crt_transform_string(CP_ACP, *it, buffer.get(), required_count);
261         if (actual_count == 0)
262             return -1;
263 
264         // Ignore a failed attempt to set a variable; continue with the rest...
265         traits::set_variable_in_environment_nolock(buffer.detach(), 0);
266     }
267 
268     return 0;
269 }
270 
271 
272 
273 // If the requested environment exists, this function returns it unmodified.  If
274 // the requested environment does not exist but the other environment does, the
275 // other environment is cloned to create the requested environment, and the new
276 // requested environment is returned.  Otherwise, nullptr is returned.
277 template <typename Character>
278 _Deref_ret_opt_z_
common_get_or_create_environment_nolock()279 static Character** __cdecl common_get_or_create_environment_nolock() throw()
280 {
281     typedef __crt_char_traits<Character> traits;
282     typedef typename traits::other_char_type other_char_type;
283 
284     // Check to see if the required environment already exists:
285     Character** const existing_environment = get_environment_nolock(Character());
286     if (existing_environment)
287         return existing_environment;
288 
289     // Check to see if the other environment exists.  We will only initialize
290     // the environment here if the other environment was already initialized.
291     other_char_type** const other_environment = get_environment_nolock(other_char_type());
292     if (!other_environment)
293         return nullptr;
294 
295     if (common_initialize_environment_nolock<Character>() != 0)
296     {
297         if (initialize_environment_by_cloning_nolock<Character>() != 0)
298         {
299             return nullptr;
300         }
301     }
302 
303     return get_environment_nolock(Character());
304 }
305 
__dcrt_get_or_create_narrow_environment_nolock()306 extern "C" char** __cdecl __dcrt_get_or_create_narrow_environment_nolock()
307 {
308     return common_get_or_create_environment_nolock<char>();
309 }
310 
__dcrt_get_or_create_wide_environment_nolock()311 extern "C" wchar_t** __cdecl __dcrt_get_or_create_wide_environment_nolock()
312 {
313     return common_get_or_create_environment_nolock<wchar_t>();
314 }
315 
316 template <typename Character>
common_get_initial_environment()317 static Character** __cdecl common_get_initial_environment() throw()
318 {
319     Character**& initial_environment = get_initial_environment(Character());
320     if (!initial_environment)
321     {
322         initial_environment = common_get_or_create_environment_nolock<Character>();
323     }
324 
325     return initial_environment;
326 }
327 
_get_initial_narrow_environment()328 extern "C" char** __cdecl _get_initial_narrow_environment()
329 {
330     return common_get_initial_environment<char>();
331 }
332 
_get_initial_wide_environment()333 extern "C" wchar_t** __cdecl _get_initial_wide_environment()
334 {
335     return common_get_initial_environment<wchar_t>();
336 }
337