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