-
Notifications
You must be signed in to change notification settings - Fork 382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix profile collection on non-Windows, add PS 7 profiles #1260
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -9,6 +9,7 @@ | |||||
using System.Linq; | ||||||
using System.Runtime.InteropServices; | ||||||
using System.Text; | ||||||
using System.Text.RegularExpressions; | ||||||
using Microsoft.Management.Infrastructure; | ||||||
using Microsoft.PowerShell.CrossCompatibility.Data; | ||||||
using Microsoft.PowerShell.CrossCompatibility.Utility; | ||||||
|
@@ -22,13 +23,74 @@ namespace Microsoft.PowerShell.CrossCompatibility.Collection | |||||
/// </summary> | ||||||
public class PlatformInformationCollector : IDisposable | ||||||
{ | ||||||
/// <summary> | ||||||
/// Collect all release info files into a lookup table in memory. | ||||||
/// Overrides pre-existing keys if there are duplicates. | ||||||
/// </summary> | ||||||
/// <returns>A dictionary with the keys and values of all the release info files on the machine.</returns> | ||||||
public static IReadOnlyDictionary<string, string> GetLinuxReleaseInfo() | ||||||
{ | ||||||
var dict = new Dictionary<string, string>(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe a more descriptive name could be useful? What about creating it in a case insensitive way? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A more descriptive name for the method? Not sure what to call it other than this, but open to suggestions. I thought about case-sensitivity, but ultimately this is Linux-specific and there's nothing to stop two keys being added that differ only by case There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh! Got it :) |
||||||
|
||||||
foreach (string path in s_releaseInfoPaths) | ||||||
{ | ||||||
try | ||||||
{ | ||||||
using (FileStream fileStream = File.OpenRead(path)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DRY:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no repetition here; |
||||||
using (var reader = new StreamReader(fileStream)) | ||||||
{ | ||||||
while (!reader.EndOfStream) | ||||||
{ | ||||||
string line = reader.ReadLine(); | ||||||
|
||||||
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#")) | ||||||
{ | ||||||
continue; | ||||||
} | ||||||
|
||||||
string[] elements = line.Split('='); | ||||||
dict[elements[0]] = Dequote(elements[1]); | ||||||
} | ||||||
} | ||||||
} | ||||||
catch (IOException) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe a comment why this could happen and is OK could be helpful. |
||||||
{ | ||||||
// Do nothing - just continue | ||||||
} | ||||||
} | ||||||
|
||||||
return dict; | ||||||
} | ||||||
|
||||||
// Paths on Linux to search for key/value-paired information about the OS. | ||||||
private static readonly IReadOnlyCollection<string> s_releaseInfoPaths = new string[] | ||||||
{ | ||||||
"/etc/lsb-release", | ||||||
"/etc/os-release", | ||||||
}; | ||||||
|
||||||
private static readonly IReadOnlyList<string> s_distributionIdKeys = new string[] | ||||||
{ | ||||||
"ID", | ||||||
"DISTRIB_ID" | ||||||
}; | ||||||
|
||||||
private static readonly IReadOnlyList<string> s_distributionVersionKeys = new string[] | ||||||
{ | ||||||
"VERSION_ID", | ||||||
"DISTRIB_RELEASE" | ||||||
}; | ||||||
|
||||||
private static readonly IReadOnlyList<string> s_distributionPrettyNameKeys = new string[] | ||||||
{ | ||||||
"PRETTY_NAME", | ||||||
"DISTRIB_DESCRIPTION" | ||||||
}; | ||||||
|
||||||
private static readonly Regex s_macOSNameRegex = new Regex( | ||||||
@"System Version: (.*?)(\(|$)", | ||||||
RegexOptions.Multiline | RegexOptions.Compiled); | ||||||
|
||||||
private readonly Lazy<Hashtable> _lazyPSVersionTable; | ||||||
|
||||||
private readonly Lazy<PowerShellVersion> _lazyPSVersion; | ||||||
|
@@ -129,16 +191,17 @@ public OperatingSystemData GetOperatingSystemData() | |||||
{ | ||||||
var osData = new OperatingSystemData() | ||||||
{ | ||||||
Description = GetOSDescription(), | ||||||
Architecture = GetOSArchitecture(), | ||||||
Family = GetOSFamily(), | ||||||
Name = GetOSName(), | ||||||
Platform = GetOSPlatform(), | ||||||
Version = GetOSVersion(), | ||||||
}; | ||||||
|
||||||
switch (osData.Family) | ||||||
{ | ||||||
case OSFamily.Windows: | ||||||
osData.Name = osData.Description; | ||||||
if (!string.IsNullOrEmpty(Environment.OSVersion.ServicePack)) | ||||||
{ | ||||||
osData.ServicePack = Environment.OSVersion.ServicePack; | ||||||
|
@@ -147,53 +210,83 @@ public OperatingSystemData GetOperatingSystemData() | |||||
break; | ||||||
|
||||||
case OSFamily.Linux: | ||||||
IReadOnlyDictionary<string, string> lsbInfo = GetLinuxReleaseInfo(); | ||||||
osData.DistributionId = lsbInfo["DistributionId"]; | ||||||
osData.DistributionVersion = lsbInfo["DistributionVersion"]; | ||||||
osData.DistributionPrettyName = lsbInfo["DistributionPrettyName"]; | ||||||
IReadOnlyDictionary<string, string> releaseInfo = GetLinuxReleaseInfo(); | ||||||
|
||||||
osData.DistributionId = GetEntryFromReleaseInfo(releaseInfo, s_distributionIdKeys); | ||||||
osData.DistributionVersion = GetEntryFromReleaseInfo(releaseInfo, s_distributionVersionKeys); | ||||||
osData.DistributionPrettyName = GetEntryFromReleaseInfo(releaseInfo, s_distributionPrettyNameKeys); | ||||||
osData.Name = osData.DistributionPrettyName; | ||||||
break; | ||||||
|
||||||
case OSFamily.MacOS: | ||||||
osData.Name = GetMacOSName(); | ||||||
break; | ||||||
} | ||||||
|
||||||
return osData; | ||||||
} | ||||||
|
||||||
/// <summary> | ||||||
/// Collect all release info files into a lookup table in memory. | ||||||
/// Overrides pre-existing keys if there are duplicates. | ||||||
/// </summary> | ||||||
/// <returns>A dictionary with the keys and values of all the release info files on the machine.</returns> | ||||||
public IReadOnlyDictionary<string, string> GetLinuxReleaseInfo() | ||||||
private string GetMacOSName() | ||||||
{ | ||||||
var dict = new Dictionary<string, string>(); | ||||||
|
||||||
foreach (string path in s_releaseInfoPaths) | ||||||
try | ||||||
{ | ||||||
try | ||||||
using (var spProcess = new Process()) | ||||||
{ | ||||||
using (FileStream fileStream = File.OpenRead(path)) | ||||||
using (var reader = new StreamReader(fileStream)) | ||||||
{ | ||||||
while (!reader.EndOfStream) | ||||||
{ | ||||||
string line = reader.ReadLine(); | ||||||
spProcess.StartInfo.UseShellExecute = false; | ||||||
spProcess.StartInfo.RedirectStandardOutput = true; | ||||||
spProcess.StartInfo.CreateNoWindow = true; | ||||||
spProcess.StartInfo.FileName = "/usr/sbin/system_profiler"; | ||||||
spProcess.StartInfo.Arguments = "SPSoftwareDataType"; | ||||||
|
||||||
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#")) | ||||||
{ | ||||||
continue; | ||||||
} | ||||||
spProcess.Start(); | ||||||
spProcess.WaitForExit(); | ||||||
|
||||||
string[] elements = line.Split('='); | ||||||
dict[elements[0]] = Dequote(elements[1]); | ||||||
} | ||||||
} | ||||||
} | ||||||
catch (IOException) | ||||||
{ | ||||||
// Do nothing - just continue | ||||||
string output = spProcess.StandardOutput.ReadToEnd(); | ||||||
return s_macOSNameRegex.Match(output).Groups[1].Value; | ||||||
} | ||||||
} | ||||||
catch | ||||||
{ | ||||||
return null; | ||||||
} | ||||||
} | ||||||
|
||||||
return dict; | ||||||
private string GetOSDescription() | ||||||
{ | ||||||
#if CoreCLR | ||||||
// This key was introduced in PowerShell 6 | ||||||
return (string)PSVersionTable["OS"]; | ||||||
#else | ||||||
if (_lazyWin32OperatingSystemInfo.IsValueCreated) | ||||||
{ | ||||||
return _lazyWin32OperatingSystemInfo.Value.OSName; | ||||||
} | ||||||
|
||||||
return RegistryCurrentVersionInfo.ProductName; | ||||||
#endif | ||||||
} | ||||||
|
||||||
private string GetOSVersion() | ||||||
{ | ||||||
#if CoreCLR | ||||||
// On Linux, we want to record the kernel branch, since this can differentiate Azure | ||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) | ||||||
{ | ||||||
return File.ReadAllText("/proc/sys/kernel/osrelease"); | ||||||
} | ||||||
#endif | ||||||
return Environment.OSVersion.Version.ToString(); | ||||||
} | ||||||
|
||||||
private string GetOSPlatform() | ||||||
{ | ||||||
#if CoreCLR | ||||||
if (PSVersion.Major >= 6) | ||||||
{ | ||||||
return (string)PSVersionTable["Platform"]; | ||||||
} | ||||||
#endif | ||||||
return "Win32NT"; | ||||||
} | ||||||
|
||||||
private OSFamily GetOSFamily() | ||||||
|
@@ -310,46 +403,6 @@ private uint GetWinSkuId() | |||||
return (uint)WindowsSku.Undefined; | ||||||
} | ||||||
|
||||||
private string GetOSName() | ||||||
{ | ||||||
#if CoreCLR | ||||||
// This key was introduced in PowerShell 6 | ||||||
if (PSVersion.Major >= 6) | ||||||
{ | ||||||
return (string)PSVersionTable["OS"]; | ||||||
} | ||||||
#endif | ||||||
if (_lazyWin32OperatingSystemInfo.IsValueCreated) | ||||||
{ | ||||||
return _lazyWin32OperatingSystemInfo.Value.OSName; | ||||||
} | ||||||
|
||||||
return RegistryCurrentVersionInfo.ProductName; | ||||||
} | ||||||
|
||||||
private string GetOSVersion() | ||||||
{ | ||||||
#if CoreCLR | ||||||
// On Linux, we want to record the kernel branch, since this can differentiate Azure | ||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) | ||||||
{ | ||||||
return File.ReadAllText("/proc/sys/kernel/osrelease"); | ||||||
} | ||||||
#endif | ||||||
return Environment.OSVersion.Version.ToString(); | ||||||
} | ||||||
|
||||||
private string GetOSPlatform() | ||||||
{ | ||||||
#if CoreCLR | ||||||
if (PSVersion.Major >= 6) | ||||||
{ | ||||||
return (string)PSVersionTable["Platform"]; | ||||||
} | ||||||
#endif | ||||||
return "Win32NT"; | ||||||
} | ||||||
|
||||||
private static CurrentVersionInfo ReadCurrentVersionFromRegistry() | ||||||
{ | ||||||
using (RegistryKey currentVersion = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion")) | ||||||
|
@@ -360,13 +413,18 @@ private static CurrentVersionInfo ReadCurrentVersionFromRegistry() | |||||
} | ||||||
} | ||||||
|
||||||
[DllImport("kernel32.dll")] | ||||||
private static extern bool GetProductInfo( | ||||||
int dwOSMajorVersion, | ||||||
int dwOSMinorVersion, | ||||||
int dwSpMajorVersion, | ||||||
int dwSpMinorVersion, | ||||||
out uint pdwReturnedProductType); | ||||||
private static string GetEntryFromReleaseInfo(IReadOnlyDictionary<string, string> releaseInfo, IEnumerable<string> possibleKeys) | ||||||
{ | ||||||
foreach (string key in possibleKeys) | ||||||
{ | ||||||
if (releaseInfo.TryGetValue(key, out string entry)) | ||||||
{ | ||||||
return entry; | ||||||
} | ||||||
} | ||||||
|
||||||
return null; | ||||||
} | ||||||
|
||||||
private static Win32OSCimInfo GetWin32OperatingSystemInfo() | ||||||
{ | ||||||
|
@@ -443,6 +501,15 @@ private static string Dequote(string s) | |||||
return sb.ToString(); | ||||||
} | ||||||
|
||||||
[DllImport("kernel32.dll")] | ||||||
private static extern bool GetProductInfo( | ||||||
int dwOSMajorVersion, | ||||||
int dwOSMinorVersion, | ||||||
int dwSpMajorVersion, | ||||||
int dwSpMinorVersion, | ||||||
out uint pdwReturnedProductType); | ||||||
|
||||||
|
||||||
#region IDisposable Support | ||||||
private bool disposedValue = false; // To detect redundant calls | ||||||
|
||||||
|
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am guessing the code works in such a way that and old schema (change only in minor version) is backwards compatible, i.e. old profiles still work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added a comment explaining this