Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System / services / monitoring / system / diagnosticts / SharedUtils.cs
blob5b664f60ca1730600c6e3ceb1b09eb7e82660205
1 //------------------------------------------------------------------------------
2 // <copyright file="SharedUtils.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
7 namespace System.Diagnostics {
8 using System.Security.Permissions;
9 using System.Security;
10 using System.Threading;
11 using System.Text;
12 using Microsoft.Win32;
13 using System.Globalization;
14 using System.ComponentModel;
15 using System.Security.Principal;
16 using System.Security.AccessControl;
17 using System.Runtime.Versioning;
18 using System.Runtime.CompilerServices;
19 using System.Runtime.ConstrainedExecution;
20 using System.Runtime.InteropServices;
21 using Microsoft.Win32.SafeHandles;
22 using System.Diagnostics.CodeAnalysis;
24 internal static class SharedUtils {
26 internal const int UnknownEnvironment = 0;
27 internal const int W2kEnvironment = 1;
28 internal const int NtEnvironment = 2;
29 internal const int NonNtEnvironment = 3;
30 private static volatile int environment = UnknownEnvironment;
32 private static Object s_InternalSyncObject;
33 private static Object InternalSyncObject {
34 get {
35 if (s_InternalSyncObject == null) {
36 Object o = new Object();
37 Interlocked.CompareExchange(ref s_InternalSyncObject, o, null);
39 return s_InternalSyncObject;
43 internal static Win32Exception CreateSafeWin32Exception() {
44 return CreateSafeWin32Exception(0);
47 internal static Win32Exception CreateSafeWin32Exception(int error) {
48 Win32Exception newException = null;
49 // Need to assert SecurtiyPermission, otherwise Win32Exception
50 // will not be able to get the error message. At this point the right
51 // permissions have already been demanded.
52 SecurityPermission securityPermission = new SecurityPermission(PermissionState.Unrestricted);
53 securityPermission.Assert();
54 try {
55 if (error == 0)
56 newException = new Win32Exception();
57 else
58 newException = new Win32Exception(error);
60 finally {
61 SecurityPermission.RevertAssert();
64 return newException;
67 internal static int CurrentEnvironment {
68 get {
69 if (environment == UnknownEnvironment) {
70 lock (InternalSyncObject) {
71 if (environment == UnknownEnvironment) {
72 // Need to assert Environment permissions here
73 // the environment check is not exposed as a public method
74 if (Environment.OSVersion.Platform == PlatformID.Win32NT) {
75 if (Environment.OSVersion.Version.Major >= 5)
76 environment = W2kEnvironment;
77 else
78 environment = NtEnvironment;
80 else
81 environment = NonNtEnvironment;
86 return environment;
90 internal static void CheckEnvironment() {
91 if (CurrentEnvironment == NonNtEnvironment)
92 throw new PlatformNotSupportedException(SR.GetString(SR.WinNTRequired));
95 internal static void CheckNtEnvironment() {
96 if (CurrentEnvironment == NtEnvironment)
97 throw new PlatformNotSupportedException(SR.GetString(SR.Win2000Required));
100 [ResourceExposure(ResourceScope.Machine)]
101 [ResourceConsumption(ResourceScope.Machine)]
102 internal static void EnterMutex(string name, ref Mutex mutex) {
103 string mutexName = null;
104 if (CurrentEnvironment == W2kEnvironment)
105 mutexName = "Global\\" + name;
106 else
107 mutexName = name;
109 EnterMutexWithoutGlobal(mutexName, ref mutex);
112 [ResourceExposure(ResourceScope.Machine)]
113 [ResourceConsumption(ResourceScope.Machine)]
114 [SecurityPermission(SecurityAction.Assert, ControlPrincipal = true)]
115 [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "Microsoft: We pass fixed data into sec.AddAccessRule")]
116 internal static void EnterMutexWithoutGlobal(string mutexName, ref Mutex mutex) {
117 bool createdNew;
118 MutexSecurity sec = new MutexSecurity();
119 SecurityIdentifier everyoneSid = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
120 sec.AddAccessRule(new MutexAccessRule(everyoneSid, MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow));
122 Mutex tmpMutex = new Mutex(false, mutexName, out createdNew, sec);
124 SafeWaitForMutex(tmpMutex, ref mutex);
127 // We need to atomically attempt to acquire the mutex and record whether we took it (because we require thread affinity
128 // while the mutex is held and the two states must be kept in lock step). We can get atomicity with a CER, but we don't want
129 // to hold a CER over a call to WaitOne (this could cause deadlocks). The ---- is to provide a new API out of
130 // mscorlib that performs the wait and lets us know if it succeeded. But at this late stage we don't want to expose a new
131 // API out of mscorlib, so we'll build our own solution.
132 // We'll P/Invoke out to the WaitForSingleObject inside a CER, but use a timeout to ensure we can't block a thread abort for
133 // an unlimited time (we do this in an infinite loop so the semantic of acquiring the mutex is unchanged, the timeout is
134 // just to allow us to poll for abort). A limitation of CERs in Whidbey (and part of the problem that put us in this
135 // position in the first place) is that a CER root in a method will cause the entire method to delay thread aborts. So we
136 // need to carefully partition the real CER part of out logic in a sub-method (and ensure the jit doesn't inline on us).
137 [ResourceExposure(ResourceScope.Machine)]
138 [ResourceConsumption(ResourceScope.Machine)]
139 private static bool SafeWaitForMutex(Mutex mutexIn, ref Mutex mutexOut)
141 Debug.Assert(mutexOut == null, "You must pass in a null ref Mutex");
143 // Wait as long as necessary for the mutex.
144 while (true) {
146 // Attempt to acquire the mutex but timeout quickly if we can't.
147 if (!SafeWaitForMutexOnce(mutexIn, ref mutexOut))
148 return false;
149 if (mutexOut != null)
150 return true;
152 // We come out here to the outer method every so often so we're not in a CER and a thread abort can interrupt us.
153 // But the abort logic itself is poll based (in the this case) so we really need to check for a pending abort
154 // explicitly else the two timing windows will virtually never line up and we'll still end up stalling the abort
155 // attempt. Thread.Sleep checks for pending abort for us.
156 Thread.Sleep(0);
160 // The portion of SafeWaitForMutex that runs under a CER and thus must not block for a arbitrary period of time.
161 // This method must not be inlined (to stop the CER accidently spilling into the calling method).
162 [ResourceExposure(ResourceScope.Machine)]
163 [ResourceConsumption(ResourceScope.Machine)]
164 [MethodImplAttribute(MethodImplOptions.NoInlining)]
165 private static bool SafeWaitForMutexOnce(Mutex mutexIn, ref Mutex mutexOut)
167 bool ret;
169 RuntimeHelpers.PrepareConstrainedRegions();
170 try {} finally {
171 // Wait for the mutex for half a second (long enough to gain the mutex in most scenarios and short enough to avoid
172 // impacting a thread abort for too long).
173 // Holding a mutex requires us to keep thread affinity and announce ourselves as a critical region.
174 Thread.BeginCriticalRegion();
175 Thread.BeginThreadAffinity();
176 int result = WaitForSingleObjectDontCallThis(mutexIn.SafeWaitHandle, 500);
177 switch (result) {
179 case NativeMethods.WAIT_OBJECT_0:
180 case NativeMethods.WAIT_ABANDONED:
181 // Mutex was obtained, atomically record that fact.
182 mutexOut = mutexIn;
183 ret = true;
184 break;
186 case NativeMethods.WAIT_TIMEOUT:
187 // Couldn't get mutex yet, simply return and we'll try again later.
188 ret = true;
189 break;
191 default:
192 // Some sort of failure return immediately all the way to the caller of SafeWaitForMutex.
193 ret = false;
194 break;
197 // If we're not leaving with the Mutex we don't require thread affinity and we're not a critical region any more.
198 if (mutexOut == null) {
199 Thread.EndThreadAffinity();
200 Thread.EndCriticalRegion();
204 return ret;
207 // P/Invoke for the methods above. Don't call this from anywhere else.
208 [ResourceExposure(ResourceScope.Machine)]
209 [ResourceConsumption(ResourceScope.Machine)]
210 [System.Security.SuppressUnmanagedCodeSecurity]
211 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
212 [DllImport(ExternDll.Kernel32, ExactSpelling=true, SetLastError=true, EntryPoint="WaitForSingleObject")]
213 private static extern int WaitForSingleObjectDontCallThis(SafeWaitHandle handle, int timeout);
215 [ResourceExposure(ResourceScope.Machine)] // This is scoped to a Fx build dir.
216 [ResourceConsumption(ResourceScope.Machine)]
217 // What if an app is locked back? Why would we use this?
218 internal static string GetLatestBuildDllDirectory(string machineName) {
219 string dllDir = "";
220 RegistryKey baseKey = null;
221 RegistryKey complusReg = null;
223 //This property is retrieved only when creationg a new category,
224 // the calling code already demanded PerformanceCounterPermission.
225 // Therefore the assert below is safe.
227 //This property is retrieved only when creationg a new log,
228 // the calling code already demanded EventLogPermission.
229 // Therefore the assert below is safe.
231 RegistryPermission registryPermission = new RegistryPermission(PermissionState.Unrestricted);
232 registryPermission.Assert();
234 try {
235 if (machineName.Equals(".")) {
236 return GetLocalBuildDirectory();
238 else {
239 baseKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machineName);
241 if (baseKey == null)
242 throw new InvalidOperationException(SR.GetString(SR.RegKeyMissingShort, "HKEY_LOCAL_MACHINE", machineName));
244 complusReg = baseKey.OpenSubKey("SOFTWARE\\Microsoft\\.NETFramework");
245 if (complusReg != null) {
246 string installRoot = (string)complusReg.GetValue("InstallRoot");
247 if (installRoot != null && installRoot != String.Empty) {
248 // the "policy" subkey contains a v{major}.{minor} subkey for each version installed. There are also
249 // some extra subkeys like "standards" and "upgrades" we want to ignore.
251 // first we figure out what version we are...
252 string versionPrefix = "v" + Environment.Version.Major + "." + Environment.Version.Minor;
253 RegistryKey policyKey = complusReg.OpenSubKey("policy");
255 // This is the full version string of the install on the remote machine we want to use (for example "v2.0.50727")
256 string version = null;
258 if (policyKey != null) {
259 try {
261 // First check to see if there is a version of the runtime with the same minor and major number:
262 RegistryKey bestKey = policyKey.OpenSubKey(versionPrefix);
264 if (bestKey != null) {
265 try {
266 version = versionPrefix + "." + GetLargestBuildNumberFromKey(bestKey);
267 } finally {
268 bestKey.Close();
270 } else {
271 // There isn't an exact match for our version, so we will look for the largest version
272 // installed.
273 string[] majorVersions = policyKey.GetSubKeyNames();
274 int[] largestVersion = new int[] { -1, -1, -1 };
275 for (int i = 0; i < majorVersions.Length; i++) {
277 string majorVersion = majorVersions[i];
279 // If this looks like a key of the form v{something}.{something}, we should see if it's a usable build.
280 if (majorVersion.Length > 1 && majorVersion[0] == 'v' && majorVersion.Contains(".")) {
281 int[] currentVersion = new int[] { -1, -1, -1 };
283 string[] splitVersion = majorVersion.Substring(1).Split('.');
285 if(splitVersion.Length != 2) {
286 continue;
289 if (!Int32.TryParse(splitVersion[0], out currentVersion[0]) || !Int32.TryParse(splitVersion[1], out currentVersion[1])) {
290 continue;
293 RegistryKey k = policyKey.OpenSubKey(majorVersion);
294 if (k == null) {
295 // We may be able to use another subkey
296 continue;
298 try {
299 currentVersion[2] = GetLargestBuildNumberFromKey(k);
301 if (currentVersion[0] > largestVersion[0]
302 || ((currentVersion[0] == largestVersion[0]) && (currentVersion[1] > largestVersion[1]))) {
303 largestVersion = currentVersion;
305 } finally {
306 k.Close();
311 version = "v" + largestVersion[0] + "." + largestVersion[1] + "." + largestVersion[2];
313 } finally {
314 policyKey.Close();
317 if (version != null && version != String.Empty) {
318 StringBuilder installBuilder = new StringBuilder();
319 installBuilder.Append(installRoot);
320 if (!installRoot.EndsWith("\\", StringComparison.Ordinal))
321 installBuilder.Append("\\");
322 installBuilder.Append(version);
323 dllDir = installBuilder.ToString();
329 catch {
330 // ignore
332 finally {
333 if (complusReg != null)
334 complusReg.Close();
336 if (baseKey != null)
337 baseKey.Close();
339 RegistryPermission.RevertAssert();
342 return dllDir;
345 [ResourceExposure(ResourceScope.Machine)]
346 [ResourceConsumption(ResourceScope.Machine)]
347 private static int GetLargestBuildNumberFromKey(RegistryKey rootKey) {
348 int largestBuild = -1;
350 string[] minorVersions = rootKey.GetValueNames();
351 for (int i = 0; i < minorVersions.Length; i++) {
352 int o;
353 if (Int32.TryParse(minorVersions[i], out o)) {
354 largestBuild = (largestBuild > o) ? largestBuild : o;
358 return largestBuild;
361 [ResourceExposure(ResourceScope.Machine)]
362 [ResourceConsumption(ResourceScope.Machine)]
363 private static string GetLocalBuildDirectory() {
364 return RuntimeEnvironment.GetRuntimeDirectory();