1 #region Copyright notice and license
2 
3 // Copyright 2021 The gRPC Authors
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #endregion
18 
19 using System;
20 using System.Runtime.CompilerServices;
21 using System.Runtime.InteropServices;
22 
23 namespace Grpc.Core.Internal
24 {
25     /// <summary>
26     /// This source file is shared by both Grpc.Core and Grpc.Tools to avoid duplication
27     /// of platform detection code.
28     /// </summary>
29     internal static class CommonPlatformDetection
30     {
31         public enum OSKind { Unknown, Windows, Linux, MacOSX };
32         public enum CpuArchitecture { Unknown, X86, X64, Arm64 };
33 
GetOSKind()34         public static OSKind GetOSKind()
35         {
36 #if NETSTANDARD || NETCORE
37             if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
38             {
39                 return OSKind.Windows;
40             }
41             else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
42             {
43                 return OSKind.Linux;
44             }
45             else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
46             {
47                 return OSKind.MacOSX;
48             }
49             else
50             {
51                 return OSKind.Unknown;
52             }
53 #else
54             var platform = Environment.OSVersion.Platform;
55             if (platform == PlatformID.Win32NT || platform == PlatformID.Win32S || platform == PlatformID.Win32Windows)
56             {
57                 return OSKind.Windows;
58             }
59             else if (platform == PlatformID.Unix && GetUname() == "Darwin")
60             {
61                 // PlatformID.MacOSX is never returned, commonly used trick is to identify Mac is by using uname.
62                 return OSKind.MacOSX;
63             }
64             else if (platform == PlatformID.Unix)
65             {
66                 // on legacy .NET Framework, our detection options are limited, so we treat
67                 // all other unix systems as Linux.
68                 return OSKind.Linux;
69             }
70             else
71             {
72                 return OSKind.Unknown;
73             }
74 #endif
75         }
76 
GetProcessArchitecture()77         public static CpuArchitecture GetProcessArchitecture()
78         {
79 #if NETSTANDARD || NETCORE
80             switch (RuntimeInformation.ProcessArchitecture)
81             {
82                 case Architecture.X86:
83                     return CpuArchitecture.X86;
84                 case Architecture.X64:
85                     return CpuArchitecture.X64;
86                 case Architecture.Arm64:
87                     return CpuArchitecture.Arm64;
88                 // We do not support other architectures,
89                 // so we simply return "unrecognized".
90                 default:
91                    return CpuArchitecture.Unknown;
92             }
93 #else
94             // on legacy .NET Framework, RuntimeInformation is not available
95             // but our choice of supported architectures there
96             // is also very limited, so we simply take our best guess.
97             return Environment.Is64BitProcess ? CpuArchitecture.X64 : CpuArchitecture.X86;
98 #endif
99         }
100 
101         [DllImport("libc")]
uname(IntPtr buf)102         static extern int uname(IntPtr buf);
103 
104         // This code is copied from Grpc.Core/PlatformApis.cs
GetUname()105         static string GetUname()
106         {
107             var buffer = Marshal.AllocHGlobal(8192);
108             try
109             {
110                 if (uname(buffer) == 0)
111                 {
112                     return Marshal.PtrToStringAnsi(buffer);
113                 }
114                 return string.Empty;
115             }
116             catch
117             {
118                 return string.Empty;
119             }
120             finally
121             {
122                 if (buffer != IntPtr.Zero)
123                 {
124                     Marshal.FreeHGlobal(buffer);
125                 }
126             }
127         }
128     }
129 }
130