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// CLRConfig.cpp
6//
7
8//
9// Unified method of accessing configuration values from environment variables,
10// registry and config file. See file:../inc/CLRConfigValues.h for details on how to add config values.
11//
12//*****************************************************************************
13
14#include "stdafx.h"
15#include "clrconfig.h"
16
17#ifndef ERANGE
18#define ERANGE 34
19#endif
20
21//
22// Initialize the EEConfig::GetConfiguration function pointer to NULL. If EEConfig isn't init'ed, this will
23// stay NULL and CLRConfig will ignore config files.
24//
25CLRConfig::GetConfigValueFunction CLRConfig::s_GetConfigValueCallback = NULL;
26
27//
28// Creating structs using the macro table in CLRConfigValues.h
29//
30
31// These macros intialize ConfigDWORDInfo structs.
32#define RETAIL_CONFIG_DWORD_INFO(symbol, name, defaultValue, description) \
33 const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::EEConfig_default};
34#define RETAIL_CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) \
35 const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, lookupOptions};
36
37// These macros intialize ConfigStringInfo structs.
38#define RETAIL_CONFIG_STRING_INFO(symbol, name, description) \
39 const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default};
40#define RETAIL_CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \
41 const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions};
42
43// TEMPORARY macros that intialize strings for config value accesses that haven't been moved over to
44// CLRConfig yet. Once all accesses have been moved, these macros (and corresponding instantiations in
45// file:../utilcode/CLRConfig.h) should be removed.
46#define RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \
47 const LPCWSTR CLRConfig::symbol = name;
48#define RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \
49 const LPCWSTR CLRConfig::symbol = name;
50//
51// Debug versions of the macros
52//
53#ifdef _DEBUG
54 #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description) \
55 const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::EEConfig_default};
56 #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) \
57 const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, lookupOptions};
58 #define CONFIG_STRING_INFO(symbol, name, description) \
59 const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default};
60 #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \
61 const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions};
62 #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \
63 const LPCWSTR CLRConfig::symbol = name;
64 #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \
65 const LPCWSTR CLRConfig::symbol = name;
66#else
67 #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description)
68 #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions)
69 #define CONFIG_STRING_INFO(symbol, name, description)
70 #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions)
71 #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description)
72 #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description)
73#endif // _DEBUG
74
75 // Now that we have defined what what the macros in file:../inc/CLRConfigValues.h mean, include it to generate the code.
76 #include "clrconfigvalues.h"
77
78#undef RETAIL_CONFIG_DWORD_INFO
79#undef RETAIL_CONFIG_STRING_INFO
80#undef RETAIL_CONFIG_DWORD_INFO_EX
81#undef RETAIL_CONFIG_STRING_INFO_EX
82#undef RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS
83#undef RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS
84#undef CONFIG_DWORD_INFO
85#undef CONFIG_STRING_INFO
86#undef CONFIG_DWORD_INFO_EX
87#undef CONFIG_STRING_INFO_EX
88#undef CONFIG_DWORD_INFO_DIRECT_ACCESS
89#undef CONFIG_STRING_INFO_DIRECT_ACCESS
90
91
92
93
94// Return if a quirk is a enabled.
95// This will also return enabled as true when the quirk has a value set.
96BOOL CLRConfig::IsConfigEnabled(const ConfigDWORDInfo & info)
97{
98 CONTRACTL
99 {
100 NOTHROW;
101 GC_NOTRIGGER;
102 FORBID_FAULT;
103 SO_INTOLERANT;
104 }
105 CONTRACTL_END;
106
107 DWORD result = info.defaultValue;
108
109 //
110 // Set up REGUTIL options.
111 //
112 REGUTIL::CORConfigLevel level = GetConfigLevel(info.options);
113 BOOL prependCOMPlus = !CheckLookupOption(info, DontPrependCOMPlus_);
114
115 //
116 // If we aren't favoring config files, we check REGUTIL here.
117 //
118 if(CheckLookupOption(info, FavorConfigFile) == FALSE)
119 {
120 REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPlus);
121 if(result>0)
122 return TRUE;
123 LPWSTR result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPlus, level);
124 if(result != NULL && result[0] != 0)
125 {
126 return TRUE;
127 }
128 }
129
130 //
131 // Check config files through EEConfig.
132 //
133 if(CheckLookupOption(info, IgnoreConfigFiles) == FALSE && // Check that we aren't ignoring config files.
134 s_GetConfigValueCallback != NULL)// Check that GetConfigValueCallback function has been registered.
135 {
136 LPCWSTR pvalue;
137
138 // EEConfig lookup options.
139 BOOL systemOnly = CheckLookupOption(info, ConfigFile_SystemOnly) ? TRUE : FALSE;
140 BOOL applicationFirst = CheckLookupOption(info, ConfigFile_ApplicationFirst) ? TRUE : FALSE;
141
142 if(SUCCEEDED(s_GetConfigValueCallback(info.name, &pvalue, systemOnly, applicationFirst)) && pvalue != NULL)
143 {
144 WCHAR * end;
145 errno = 0;
146 result = wcstoul(pvalue, &end, 0);
147
148 // errno is ERANGE if the number is out of range, and end is set to pvalue if
149 // no valid conversion exists.
150 if (errno == ERANGE || end == pvalue)
151 {
152 if(pvalue[0]!=0)
153 return TRUE;
154
155 result = info.defaultValue;
156 }
157
158 if(result>0)
159 return TRUE;
160 }
161 }
162
163 //
164 // If we are favoring config files and we don't have a result from EEConfig, we check REGUTIL here.
165 //
166 if(CheckLookupOption(info, FavorConfigFile) == TRUE)
167 {
168 REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &result, level, prependCOMPlus);
169 if(result>0)
170 return TRUE;
171 LPWSTR result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPlus, level);
172 if(result != NULL && result[0] != 0)
173 {
174 return TRUE;
175 }
176 }
177
178 if(info.defaultValue>0)
179 return TRUE;
180 else
181 return FALSE;
182}
183
184//
185// Look up a DWORD config value.
186//
187// Arguments:
188// * info - see file:../inc/CLRConfig.h for details.
189//
190// * useDefaultIfNotSet - if true, fall back to the default value if the value is not set.
191//
192// * acceptExplicitDefaultFromRegutil - if false, only accept a value returned by REGUTIL if it is
193// different from the default value. This parameter is useful as a way to preserve existing
194// behavior.
195//
196// * result - the result.
197//
198// Return value:
199// * true for success, false otherwise.
200//
201// static
202DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info, bool acceptExplicitDefaultFromRegutil, /* [Out] */ bool *isDefault)
203{
204 CONTRACTL
205 {
206 NOTHROW;
207 GC_NOTRIGGER;
208 FORBID_FAULT;
209 SO_TOLERANT; // Need this to be tolerant to stack overflows since REGUTIL::GetConfigDWORD was too. (This replaces calls to REGUTIL::GetConfigDWORD)
210 }
211 CONTRACTL_END;
212
213 _ASSERTE (isDefault != nullptr);
214
215
216 //
217 // Set up REGUTIL options.
218 //
219 REGUTIL::CORConfigLevel level = GetConfigLevel(info.options);
220 BOOL prependCOMPlus = !CheckLookupOption(info, DontPrependCOMPlus_);
221
222 //
223 // If we aren't favoring config files, we check REGUTIL here.
224 //
225 if (CheckLookupOption(info, FavorConfigFile) == FALSE)
226 {
227 DWORD resultMaybe;
228 HRESULT hr = REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &resultMaybe, level, prependCOMPlus);
229
230 if (!acceptExplicitDefaultFromRegutil)
231 {
232 // Ignore the default value even if it's set explicitly.
233 if (resultMaybe != info.defaultValue)
234 {
235 *isDefault = false;
236 return resultMaybe;
237 }
238 }
239 else
240 {
241 // If we are willing to accept the default value when it's set explicitly,
242 // checking the HRESULT here is sufficient. E_FAIL is returned when the
243 // default is used.
244 if (SUCCEEDED(hr))
245 {
246 *isDefault = false;
247 return resultMaybe;
248 }
249 }
250 }
251
252 //
253 // Check config files through EEConfig.
254 //
255 if (CheckLookupOption(info, IgnoreConfigFiles) == FALSE && // Check that we aren't ignoring config files.
256 s_GetConfigValueCallback != NULL)// Check that GetConfigValueCallback function has been registered.
257 {
258 LPCWSTR pvalue;
259
260 // EEConfig lookup options.
261 BOOL systemOnly = CheckLookupOption(info, ConfigFile_SystemOnly) ? TRUE : FALSE;
262 BOOL applicationFirst = CheckLookupOption(info, ConfigFile_ApplicationFirst) ? TRUE : FALSE;
263
264 if (SUCCEEDED(s_GetConfigValueCallback(info.name, &pvalue, systemOnly, applicationFirst)) && pvalue != NULL)
265 {
266 WCHAR * end;
267 errno = 0;
268 DWORD resultMaybe = wcstoul(pvalue, &end, 0);
269
270 // errno is ERANGE if the number is out of range, and end is set to pvalue if
271 // no valid conversion exists.
272 if (errno != ERANGE && end != pvalue)
273 {
274 *isDefault = false;
275 return resultMaybe;
276 }
277 else
278 {
279 // If an invalid value is defined we treat it as the default value.
280 // i.e. we don't look further.
281 *isDefault = true;
282 return info.defaultValue;
283 }
284 }
285 }
286
287 //
288 // If we are favoring config files and we don't have a result from EEConfig, we check REGUTIL here.
289 //
290 if (CheckLookupOption(info, FavorConfigFile) == TRUE)
291 {
292 DWORD resultMaybe;
293 HRESULT hr = REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &resultMaybe, level, prependCOMPlus);
294
295 if (!acceptExplicitDefaultFromRegutil)
296 {
297 // Ignore the default value even if it's set explicitly.
298 if (resultMaybe != info.defaultValue)
299 {
300 *isDefault = false;
301 return resultMaybe;
302 }
303 }
304 else
305 {
306 // If we are willing to accept the default value when it's set explicitly,
307 // checking the HRESULT here is sufficient. E_FAIL is returned when the
308 // default is used.
309 if (SUCCEEDED(hr))
310 {
311 *isDefault = false;
312 return resultMaybe;
313 }
314 }
315 }
316
317 *isDefault = true;
318 return info.defaultValue;
319}
320
321//
322// Look up a DWORD config value.
323//
324// Arguments:
325// * info - see file:../inc/CLRConfig.h for details
326//
327// static
328DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info)
329{
330 // We pass false for 'acceptExplicitDefaultFromRegutil' to maintain the existing behavior of this function.
331 // Callers who don't need that behavior should switch to the other version of this function and pass true.
332 bool unused;
333 return GetConfigValue(info, false /* acceptExplicitDefaultFromRegutil */, &unused);
334}
335
336//
337// Look up a String config value.
338//
339// Arguments:
340// * info - see file:../inc/CLRConfig.h for details
341//
342// Return value:
343// * Pointer to the string value, if found. You own the string that's returned. Returns NULL if the value
344// is not found.
345//
346// static
347LPWSTR CLRConfig::GetConfigValue(const ConfigStringInfo & info)
348{
349 CONTRACTL
350 {
351 NOTHROW;
352 GC_NOTRIGGER;
353 FORBID_FAULT;
354 }
355 CONTRACTL_END;
356
357 LPWSTR result = NULL;
358
359 // TODO: We swallow OOM exception here. Is this OK?
360 FAULT_NOT_FATAL();
361
362 // If this fails, result will stay NULL.
363 GetConfigValue(info, &result);
364
365 return result;
366}
367
368//
369// Look up a string config value, passing it out through a pointer reference.
370//
371// Return value:
372// * Reports out of memory errors (HRESULT E_OUTOFMEMORY).
373//
374// Arguments:
375// * info - see file:../inc/CLRConfig.h for details
376// * outVal - Set to the result string. You own the string that's returned. Set to NULL if the value is
377// not found.
378//
379// static
380HRESULT CLRConfig::GetConfigValue(const ConfigStringInfo & info, __deref_out_z LPWSTR * outVal)
381{
382 CONTRACT(HRESULT) {
383 NOTHROW;
384 GC_NOTRIGGER;
385 INJECT_FAULT (CONTRACT_RETURN E_OUTOFMEMORY);
386 POSTCONDITION(CheckPointer(outVal, NULL_OK)); // TODO: Should this check be *outVal instead of outVal?
387 } CONTRACT_END;
388
389 LPWSTR result = NULL;
390
391
392 //
393 // Set up REGUTIL options.
394 //
395 REGUTIL::CORConfigLevel level = GetConfigLevel(info.options);
396 BOOL prependCOMPlus = !CheckLookupOption(info, DontPrependCOMPlus_);
397
398 //
399 // If we aren't favoring config files, we check REGUTIL here.
400 //
401 if(result == NULL && CheckLookupOption(info, FavorConfigFile) == FALSE)
402 {
403 result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPlus, level);
404 }
405
406 //
407 // Check config files through EEConfig.
408 //
409 if(result == NULL && // Check that we don't have a value from REGUTIL
410 CheckLookupOption(info, IgnoreConfigFiles) == FALSE && // Check that we aren't ignoring config files.
411 s_GetConfigValueCallback != NULL) // Check that GetConfigValueCallback function has been registered.
412 {
413 LPCWSTR pResult;
414
415 // EEConfig lookup options.
416 BOOL systemOnly = CheckLookupOption(info, ConfigFile_SystemOnly) ? TRUE : FALSE;
417 BOOL applicationFirst = CheckLookupOption(info, ConfigFile_ApplicationFirst) ? TRUE : FALSE;
418
419 if(SUCCEEDED(s_GetConfigValueCallback(info.name, &pResult, systemOnly, applicationFirst)) && pResult != NULL)
420 {
421 size_t len = wcslen(pResult) + 1;
422 result = new (nothrow) WCHAR[len];
423 if (result == NULL)
424 {
425 RETURN E_OUTOFMEMORY;
426 }
427 wcscpy_s(result, len, pResult);
428 }
429 }
430
431 //
432 // If we are favoring config files and we don't have a result from EEConfig, we check REGUTIL here.
433 //
434 if(result==NULL &&
435 CheckLookupOption(info, FavorConfigFile) == TRUE)
436 {
437 result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPlus, level);
438 }
439
440 if ((result != NULL) && CheckLookupOption(info, TrimWhiteSpaceFromStringValue))
441 {
442 // If this fails, result remains untouched, so we'll just return the untrimmed
443 // value.
444 LPWSTR wszTrimmedResult = NULL;
445 if (SUCCEEDED(TrimWhiteSpace(result, &wszTrimmedResult)) &&
446 (wszTrimmedResult != NULL))
447 {
448 // wszTrimmedResult should be the result we return. Delete the untrimmed
449 // result.
450 delete [] result;
451 result = wszTrimmedResult;
452 }
453 }
454
455 *outVal = result;
456 RETURN S_OK;
457}
458
459//
460// Check whether an option is specified (e.g. explicitly listed) in any location
461//
462// Arguments:
463// * name - the name field of the desired ConfigDWORDInfo/ConfigStringInfo
464//
465// static
466BOOL CLRConfig::IsConfigOptionSpecified(LPCWSTR name)
467{
468 CONTRACTL
469 {
470 NOTHROW;
471 GC_NOTRIGGER;
472 }
473 CONTRACTL_END;
474
475 // Check config files
476 {
477 LPCWSTR result = NULL;
478
479 if (s_GetConfigValueCallback != NULL &&
480 SUCCEEDED(s_GetConfigValueCallback(name, &result, FALSE, FALSE)) &&
481 result != NULL)
482 {
483 return TRUE;
484 }
485 }
486
487 // Check REGUTIL, both with and without the COMPlus_ prefix
488 {
489 LPWSTR result = NULL;
490
491 result = REGUTIL::GetConfigString_DontUse_(name, TRUE);
492 if (result != NULL)
493 {
494 FreeConfigString(result);
495 return TRUE;
496 }
497
498 result = REGUTIL::GetConfigString_DontUse_(name, FALSE);
499 if (result != NULL)
500 {
501 FreeConfigString(result);
502 return TRUE;
503 }
504
505 }
506
507 return FALSE;
508}
509
510//---------------------------------------------------------------------------------------
511//
512// Given an input string, returns a newly-allocated string equal to the input but with
513// leading and trailing whitespace trimmed off. If input is already trimmed, or if
514// trimming would result in an empty string, this function sets the output string to NULL
515//
516// Caller must free *pwszTrimmed if non-NULL
517//
518// Arguments:
519// * wszOrig - String to trim
520// * pwszTrimmed - [out]: On return, points to newly allocated, trimmed string (or
521// NULL)
522//
523// Return Value:
524// HRESULT indicating success or failure.
525//
526HRESULT CLRConfig::TrimWhiteSpace(LPCWSTR wszOrig, __deref_out_z LPWSTR * pwszTrimmed)
527{
528 CONTRACTL
529 {
530 NOTHROW;
531 GC_NOTRIGGER;
532 }
533 CONTRACTL_END;
534
535 _ASSERTE(wszOrig != NULL);
536 _ASSERTE(pwszTrimmed != NULL);
537
538 // In case we return early, set [out] to NULL by default
539 *pwszTrimmed = NULL;
540
541 // Get pointers into internal string that show where to do the trimming.
542 size_t cchOrig = wcslen(wszOrig);
543 if (!FitsIn<DWORD>(cchOrig))
544 return COR_E_OVERFLOW;
545 DWORD cchAfterTrim = (DWORD) cchOrig;
546 LPCWSTR wszAfterTrim = wszOrig;
547 ::TrimWhiteSpace(&wszAfterTrim, &cchAfterTrim);
548
549 // Is input string already trimmed? If so, save an allocation and just return.
550 if ((wszOrig == wszAfterTrim) && (cchOrig == cchAfterTrim))
551 {
552 // Yup, just return success
553 return S_OK;
554 }
555
556 if (cchAfterTrim == 0)
557 {
558 // After trimming, there's nothing left, so just return NULL
559 return S_OK;
560 }
561
562 // Create a new buffer to hold a copy of the trimmed string. Caller will be
563 // responsible for this buffer if we return it.
564 NewArrayHolder<WCHAR> wszTrimmedCopy(new (nothrow) WCHAR[cchAfterTrim + 1]);
565 if (wszTrimmedCopy == NULL)
566 {
567 return E_OUTOFMEMORY;
568 }
569
570 errno_t err = wcsncpy_s(wszTrimmedCopy, cchAfterTrim + 1, wszAfterTrim, cchAfterTrim);
571 if (err != 0)
572 {
573 return E_FAIL;
574 }
575
576 // Successfully made a copy of the trimmed string. Return it. Caller will be responsible for
577 // deleting it.
578 wszTrimmedCopy.SuppressRelease();
579 *pwszTrimmed = wszTrimmedCopy;
580 return S_OK;
581}
582
583
584//
585// Deallocation function for code:CLRConfig::FreeConfigString
586//
587void CLRConfig::FreeConfigString(__in_z LPWSTR str)
588{
589 LIMITED_METHOD_CONTRACT;
590
591 delete [] str;
592}
593
594//
595// Register EEConfig's GetConfigValueCallback function so CLRConfig can look in config files.
596//
597//static
598void CLRConfig::RegisterGetConfigValueCallback(GetConfigValueFunction func)
599{
600 LIMITED_METHOD_CONTRACT;
601 s_GetConfigValueCallback = func;
602}
603
604//
605// Helper method to translate LookupOptions to REGUTIL::CORConfigLevel.
606//
607//static
608REGUTIL::CORConfigLevel CLRConfig::GetConfigLevel(LookupOptions options)
609{
610 LIMITED_METHOD_CONTRACT;
611
612 REGUTIL::CORConfigLevel level = (REGUTIL::CORConfigLevel) 0;
613
614 if(CheckLookupOption(options, IgnoreEnv) == FALSE)
615 level = static_cast<REGUTIL::CORConfigLevel>(level | REGUTIL::COR_CONFIG_ENV);
616
617 if(CheckLookupOption(options, IgnoreHKCU) == FALSE)
618 level = static_cast<REGUTIL::CORConfigLevel>(level | REGUTIL::COR_CONFIG_USER);
619
620 if(CheckLookupOption(options, IgnoreHKLM) == FALSE)
621 level = static_cast<REGUTIL::CORConfigLevel>(level | REGUTIL::COR_CONFIG_MACHINE);
622
623 return level;
624}
625