Skip to content

Commit f0c05f1

Browse files
author
Robert Holt
committed
Fix platform information collection on *nix
1 parent e206b32 commit f0c05f1

File tree

3 files changed

+162
-84
lines changed

3 files changed

+162
-84
lines changed

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Collection/PlatformInformationCollector.cs

Lines changed: 148 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Linq;
1010
using System.Runtime.InteropServices;
1111
using System.Text;
12+
using System.Text.RegularExpressions;
1213
using Microsoft.Management.Infrastructure;
1314
using Microsoft.PowerShell.CrossCompatibility.Data;
1415
using Microsoft.PowerShell.CrossCompatibility.Utility;
@@ -22,13 +23,74 @@ namespace Microsoft.PowerShell.CrossCompatibility.Collection
2223
/// </summary>
2324
public class PlatformInformationCollector : IDisposable
2425
{
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+
2565
// Paths on Linux to search for key/value-paired information about the OS.
2666
private static readonly IReadOnlyCollection<string> s_releaseInfoPaths = new string[]
2767
{
2868
"/etc/lsb-release",
2969
"/etc/os-release",
3070
};
3171

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+
3294
private readonly Lazy<Hashtable> _lazyPSVersionTable;
3395

3496
private readonly Lazy<PowerShellVersion> _lazyPSVersion;
@@ -129,16 +191,17 @@ public OperatingSystemData GetOperatingSystemData()
129191
{
130192
var osData = new OperatingSystemData()
131193
{
194+
Description = GetOSDescription(),
132195
Architecture = GetOSArchitecture(),
133196
Family = GetOSFamily(),
134-
Name = GetOSName(),
135197
Platform = GetOSPlatform(),
136198
Version = GetOSVersion(),
137199
};
138200

139201
switch (osData.Family)
140202
{
141203
case OSFamily.Windows:
204+
osData.Name = osData.Description;
142205
if (!string.IsNullOrEmpty(Environment.OSVersion.ServicePack))
143206
{
144207
osData.ServicePack = Environment.OSVersion.ServicePack;
@@ -147,53 +210,83 @@ public OperatingSystemData GetOperatingSystemData()
147210
break;
148211

149212
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();
154223
break;
155224
}
156225

157226
return osData;
158227
}
159228

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()
166230
{
167-
var dict = new Dictionary<string, string>();
168-
169-
foreach (string path in s_releaseInfoPaths)
231+
try
170232
{
171-
try
233+
using (var spProcess = new Process())
172234
{
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";
179240

180-
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#"))
181-
{
182-
continue;
183-
}
241+
spProcess.Start();
242+
spProcess.WaitForExit();
184243

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;
193246
}
194247
}
248+
catch
249+
{
250+
return null;
251+
}
252+
}
195253

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";
197290
}
198291

199292
private OSFamily GetOSFamily()
@@ -310,46 +403,6 @@ private uint GetWinSkuId()
310403
return (uint)WindowsSku.Undefined;
311404
}
312405

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-
353406
private static CurrentVersionInfo ReadCurrentVersionFromRegistry()
354407
{
355408
using (RegistryKey currentVersion = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"))
@@ -360,13 +413,18 @@ private static CurrentVersionInfo ReadCurrentVersionFromRegistry()
360413
}
361414
}
362415

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+
}
370428

371429
private static Win32OSCimInfo GetWin32OperatingSystemInfo()
372430
{
@@ -443,6 +501,15 @@ private static string Dequote(string s)
443501
return sb.ToString();
444502
}
445503

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+
446513
#region IDisposable Support
447514
private bool disposedValue = false; // To detect redundant calls
448515

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Data/Platform/OperatingSystemData.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,18 @@ namespace Microsoft.PowerShell.CrossCompatibility.Data
1515
public class OperatingSystemData : ICloneable
1616
{
1717
/// <summary>
18-
/// The name of the operating system as
19-
/// reported by $PSVersionTable.
18+
/// The name of the operating system.
2019
/// </summary>
2120
[DataMember]
2221
public string Name { get; set; }
2322

23+
/// <summary>
24+
/// The description of the operating system as
25+
/// reported by $PSVersionTable.
26+
/// </summary>
27+
[DataMember]
28+
public string Description { get; set; }
29+
2430
/// <summary>
2531
/// The platform as reported by
2632
/// $PSVersionTable.

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Query/Platform/OperatingSystemData.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,15 @@ public OperatingSystemData(OperatingSystemDataMut operatingSystemData)
2323
}
2424

2525
/// <summary>
26-
/// The name of the operating system as reported by $PSVersionTable.OS.
26+
/// The name of the operating system.
2727
/// </summary>
2828
public string Name => _operatingSystemData.Name;
2929

30+
/// <summary>
31+
/// The description of the operating system as reported by $PSVersionTable.OS.
32+
/// </summary>
33+
public string Description => _operatingSystemData.Description;
34+
3035
/// <summary>
3136
/// The name of the platform as reported by $PSVersionTable.Platform.
3237
/// </summary>

0 commit comments

Comments
 (0)