9
9
using System . Linq ;
10
10
using System . Runtime . InteropServices ;
11
11
using System . Text ;
12
+ using System . Text . RegularExpressions ;
12
13
using Microsoft . Management . Infrastructure ;
13
14
using Microsoft . PowerShell . CrossCompatibility . Data ;
14
15
using Microsoft . PowerShell . CrossCompatibility . Utility ;
@@ -22,13 +23,74 @@ namespace Microsoft.PowerShell.CrossCompatibility.Collection
22
23
/// </summary>
23
24
public class PlatformInformationCollector : IDisposable
24
25
{
26
+ /// <summary>
27
+ /// Collect all release info files into a lookup table in memory.
28
+ /// Overrides pre-existing keys if there are duplicates.
29
+ /// </summary>
30
+ /// <returns>A dictionary with the keys and values of all the release info files on the machine.</returns>
31
+ public static IReadOnlyDictionary < string , string > GetLinuxReleaseInfo ( )
32
+ {
33
+ var dict = new Dictionary < string , string > ( ) ;
34
+
35
+ foreach ( string path in s_releaseInfoPaths )
36
+ {
37
+ try
38
+ {
39
+ using ( FileStream fileStream = File . OpenRead ( path ) )
40
+ using ( var reader = new StreamReader ( fileStream ) )
41
+ {
42
+ while ( ! reader . EndOfStream )
43
+ {
44
+ string line = reader . ReadLine ( ) ;
45
+
46
+ if ( string . IsNullOrWhiteSpace ( line ) || line . StartsWith ( "#" ) )
47
+ {
48
+ continue ;
49
+ }
50
+
51
+ string [ ] elements = line . Split ( '=' ) ;
52
+ dict [ elements [ 0 ] ] = Dequote ( elements [ 1 ] ) ;
53
+ }
54
+ }
55
+ }
56
+ catch ( IOException )
57
+ {
58
+ // Do nothing - just continue
59
+ }
60
+ }
61
+
62
+ return dict ;
63
+ }
64
+
25
65
// Paths on Linux to search for key/value-paired information about the OS.
26
66
private static readonly IReadOnlyCollection < string > s_releaseInfoPaths = new string [ ]
27
67
{
28
68
"/etc/lsb-release" ,
29
69
"/etc/os-release" ,
30
70
} ;
31
71
72
+ private static readonly IReadOnlyList < string > s_distributionIdKeys = new string [ ]
73
+ {
74
+ "ID" ,
75
+ "DISTRIB_ID"
76
+ } ;
77
+
78
+ private static readonly IReadOnlyList < string > s_distributionVersionKeys = new string [ ]
79
+ {
80
+ "VERSION_ID" ,
81
+ "DISTRIB_RELEASE"
82
+ } ;
83
+
84
+ private static readonly IReadOnlyList < string > s_distributionPrettyNameKeys = new string [ ]
85
+ {
86
+ "PRETTY_NAME" ,
87
+ "DISTRIB_DESCRIPTION"
88
+ } ;
89
+
90
+ private static readonly Regex s_macOSNameRegex = new Regex (
91
+ @"System Version: (.*?)(\(|$)" ,
92
+ RegexOptions . Multiline | RegexOptions . Compiled ) ;
93
+
32
94
private readonly Lazy < Hashtable > _lazyPSVersionTable ;
33
95
34
96
private readonly Lazy < PowerShellVersion > _lazyPSVersion ;
@@ -129,16 +191,17 @@ public OperatingSystemData GetOperatingSystemData()
129
191
{
130
192
var osData = new OperatingSystemData ( )
131
193
{
194
+ Description = GetOSDescription ( ) ,
132
195
Architecture = GetOSArchitecture ( ) ,
133
196
Family = GetOSFamily ( ) ,
134
- Name = GetOSName ( ) ,
135
197
Platform = GetOSPlatform ( ) ,
136
198
Version = GetOSVersion ( ) ,
137
199
} ;
138
200
139
201
switch ( osData . Family )
140
202
{
141
203
case OSFamily . Windows :
204
+ osData . Name = osData . Description ;
142
205
if ( ! string . IsNullOrEmpty ( Environment . OSVersion . ServicePack ) )
143
206
{
144
207
osData . ServicePack = Environment . OSVersion . ServicePack ;
@@ -147,53 +210,83 @@ public OperatingSystemData GetOperatingSystemData()
147
210
break ;
148
211
149
212
case OSFamily . Linux :
150
- IReadOnlyDictionary < string , string > lsbInfo = GetLinuxReleaseInfo ( ) ;
151
- osData . DistributionId = lsbInfo [ "DistributionId" ] ;
152
- osData . DistributionVersion = lsbInfo [ "DistributionVersion" ] ;
153
- osData . DistributionPrettyName = lsbInfo [ "DistributionPrettyName" ] ;
213
+ IReadOnlyDictionary < string , string > releaseInfo = GetLinuxReleaseInfo ( ) ;
214
+
215
+ osData . DistributionId = GetEntryFromReleaseInfo ( releaseInfo , s_distributionIdKeys ) ;
216
+ osData . DistributionVersion = GetEntryFromReleaseInfo ( releaseInfo , s_distributionVersionKeys ) ;
217
+ osData . DistributionPrettyName = GetEntryFromReleaseInfo ( releaseInfo , s_distributionPrettyNameKeys ) ;
218
+ osData . Name = osData . DistributionPrettyName ;
219
+ break ;
220
+
221
+ case OSFamily . MacOS :
222
+ osData . Name = GetMacOSName ( ) ;
154
223
break ;
155
224
}
156
225
157
226
return osData ;
158
227
}
159
228
160
- /// <summary>
161
- /// Collect all release info files into a lookup table in memory.
162
- /// Overrides pre-existing keys if there are duplicates.
163
- /// </summary>
164
- /// <returns>A dictionary with the keys and values of all the release info files on the machine.</returns>
165
- public IReadOnlyDictionary < string , string > GetLinuxReleaseInfo ( )
229
+ private string GetMacOSName ( )
166
230
{
167
- var dict = new Dictionary < string , string > ( ) ;
168
-
169
- foreach ( string path in s_releaseInfoPaths )
231
+ try
170
232
{
171
- try
233
+ using ( var spProcess = new Process ( ) )
172
234
{
173
- using ( FileStream fileStream = File . OpenRead ( path ) )
174
- using ( var reader = new StreamReader ( fileStream ) )
175
- {
176
- while ( ! reader . EndOfStream )
177
- {
178
- string line = reader . ReadLine ( ) ;
235
+ spProcess . StartInfo . UseShellExecute = false ;
236
+ spProcess . StartInfo . RedirectStandardOutput = true ;
237
+ spProcess . StartInfo . CreateNoWindow = true ;
238
+ spProcess . StartInfo . FileName = "/usr/sbin/system_profiler" ;
239
+ spProcess . StartInfo . Arguments = "SPSoftwareDataType" ;
179
240
180
- if ( string . IsNullOrWhiteSpace ( line ) || line . StartsWith ( "#" ) )
181
- {
182
- continue ;
183
- }
241
+ spProcess . Start ( ) ;
242
+ spProcess . WaitForExit ( ) ;
184
243
185
- string [ ] elements = line . Split ( '=' ) ;
186
- dict [ elements [ 0 ] ] = Dequote ( elements [ 1 ] ) ;
187
- }
188
- }
189
- }
190
- catch ( IOException )
191
- {
192
- // Do nothing - just continue
244
+ string output = spProcess . StandardOutput . ReadToEnd ( ) ;
245
+ return s_macOSNameRegex . Match ( output ) . Groups [ 1 ] . Value ;
193
246
}
194
247
}
248
+ catch
249
+ {
250
+ return null ;
251
+ }
252
+ }
195
253
196
- return dict ;
254
+ private string GetOSDescription ( )
255
+ {
256
+ #if CoreCLR
257
+ // This key was introduced in PowerShell 6
258
+ return ( string ) PSVersionTable [ "OS" ] ;
259
+ #else
260
+ if ( _lazyWin32OperatingSystemInfo . IsValueCreated )
261
+ {
262
+ return _lazyWin32OperatingSystemInfo . Value . OSName ;
263
+ }
264
+
265
+ return RegistryCurrentVersionInfo . ProductName ;
266
+ #endif
267
+ }
268
+
269
+ private string GetOSVersion ( )
270
+ {
271
+ #if CoreCLR
272
+ // On Linux, we want to record the kernel branch, since this can differentiate Azure
273
+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
274
+ {
275
+ return File . ReadAllText ( "/proc/sys/kernel/osrelease" ) ;
276
+ }
277
+ #endif
278
+ return Environment . OSVersion . Version . ToString ( ) ;
279
+ }
280
+
281
+ private string GetOSPlatform ( )
282
+ {
283
+ #if CoreCLR
284
+ if ( PSVersion . Major >= 6 )
285
+ {
286
+ return ( string ) PSVersionTable [ "Platform" ] ;
287
+ }
288
+ #endif
289
+ return "Win32NT" ;
197
290
}
198
291
199
292
private OSFamily GetOSFamily ( )
@@ -310,46 +403,6 @@ private uint GetWinSkuId()
310
403
return ( uint ) WindowsSku . Undefined ;
311
404
}
312
405
313
- private string GetOSName ( )
314
- {
315
- #if CoreCLR
316
- // This key was introduced in PowerShell 6
317
- if ( PSVersion . Major >= 6 )
318
- {
319
- return ( string ) PSVersionTable [ "OS" ] ;
320
- }
321
- #endif
322
- if ( _lazyWin32OperatingSystemInfo . IsValueCreated )
323
- {
324
- return _lazyWin32OperatingSystemInfo . Value . OSName ;
325
- }
326
-
327
- return RegistryCurrentVersionInfo . ProductName ;
328
- }
329
-
330
- private string GetOSVersion ( )
331
- {
332
- #if CoreCLR
333
- // On Linux, we want to record the kernel branch, since this can differentiate Azure
334
- if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
335
- {
336
- return File . ReadAllText ( "/proc/sys/kernel/osrelease" ) ;
337
- }
338
- #endif
339
- return Environment . OSVersion . Version . ToString ( ) ;
340
- }
341
-
342
- private string GetOSPlatform ( )
343
- {
344
- #if CoreCLR
345
- if ( PSVersion . Major >= 6 )
346
- {
347
- return ( string ) PSVersionTable [ "Platform" ] ;
348
- }
349
- #endif
350
- return "Win32NT" ;
351
- }
352
-
353
406
private static CurrentVersionInfo ReadCurrentVersionFromRegistry ( )
354
407
{
355
408
using ( RegistryKey currentVersion = Registry . LocalMachine . OpenSubKey ( @"SOFTWARE\Microsoft\Windows NT\CurrentVersion" ) )
@@ -360,13 +413,18 @@ private static CurrentVersionInfo ReadCurrentVersionFromRegistry()
360
413
}
361
414
}
362
415
363
- [ DllImport ( "kernel32.dll" ) ]
364
- private static extern bool GetProductInfo (
365
- int dwOSMajorVersion ,
366
- int dwOSMinorVersion ,
367
- int dwSpMajorVersion ,
368
- int dwSpMinorVersion ,
369
- out uint pdwReturnedProductType ) ;
416
+ private static string GetEntryFromReleaseInfo ( IReadOnlyDictionary < string , string > releaseInfo , IEnumerable < string > possibleKeys )
417
+ {
418
+ foreach ( string key in possibleKeys )
419
+ {
420
+ if ( releaseInfo . TryGetValue ( key , out string entry ) )
421
+ {
422
+ return entry ;
423
+ }
424
+ }
425
+
426
+ return null ;
427
+ }
370
428
371
429
private static Win32OSCimInfo GetWin32OperatingSystemInfo ( )
372
430
{
@@ -443,6 +501,15 @@ private static string Dequote(string s)
443
501
return sb . ToString ( ) ;
444
502
}
445
503
504
+ [ DllImport ( "kernel32.dll" ) ]
505
+ private static extern bool GetProductInfo (
506
+ int dwOSMajorVersion ,
507
+ int dwOSMinorVersion ,
508
+ int dwSpMajorVersion ,
509
+ int dwSpMinorVersion ,
510
+ out uint pdwReturnedProductType ) ;
511
+
512
+
446
513
#region IDisposable Support
447
514
private bool disposedValue = false ; // To detect redundant calls
448
515
0 commit comments