|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Microsoft.DotNet.Cli.Utils;
internal enum Platform
{
Unknown = 0,
Windows = 1,
Linux = 2,
Darwin = 3,
FreeBSD = 4,
illumos = 5,
Solaris = 6,
Haiku = 7
}
internal static class RuntimeEnvironment
{
private static readonly Lazy<Platform> _platform = new(DetermineOSPlatform);
private static readonly Lazy<DistroInfo?> _distroInfo = new(LoadDistroInfo);
public static Platform OperatingSystemPlatform { get; } = GetOSPlatform();
public static string OperatingSystemVersion { get; } = GetOSVersion();
public static string OperatingSystem { get; } = GetOSName();
private class DistroInfo
{
public string? Id;
public string? VersionId;
}
private static string GetOSName()
{
switch (GetOSPlatform())
{
case Platform.Windows:
return nameof(Platform.Windows);
case Platform.Linux:
return GetDistroId() ?? nameof(Platform.Linux);
case Platform.Darwin:
return "Mac OS X";
case Platform.FreeBSD:
return nameof(Platform.FreeBSD);
case Platform.illumos:
return GetDistroId() ?? nameof(Platform.illumos);
case Platform.Solaris:
return nameof(Platform.Solaris);
case Platform.Haiku:
return nameof(Platform.Haiku);
default:
return nameof(Platform.Unknown);
}
}
private static string GetOSVersion()
{
switch (GetOSPlatform())
{
case Platform.Windows:
return Environment.OSVersion.Version.ToString(3);
case Platform.Linux:
case Platform.illumos:
return GetDistroVersionId() ?? string.Empty;
case Platform.Darwin:
return Environment.OSVersion.Version.ToString(2);
case Platform.Solaris:
// RuntimeInformation.OSDescription example on Solaris 11.3: SunOS 5.11 11.3
// we only need the major version; 11
return RuntimeInformation.OSDescription.Split(' ')[2].Split('.')[0];
case Platform.FreeBSD:
case Platform.Haiku:
// only the major version
return Environment.OSVersion.Version.ToString(1);
default:
return string.Empty;
}
}
private static Platform GetOSPlatform()
{
return _platform.Value;
}
private static string? GetDistroId()
{
return _distroInfo.Value?.Id;
}
private static string? GetDistroVersionId()
{
return _distroInfo.Value?.VersionId;
}
private static DistroInfo? LoadDistroInfo()
{
switch (GetOSPlatform())
{
case Platform.Linux:
return LoadDistroInfoFromLinux();
case Platform.illumos:
return LoadDistroInfoFromIllumos();
}
return null;
}
private static DistroInfo? LoadDistroInfoFromLinux()
{
DistroInfo? result = null;
// Sample os-release file:
// NAME="Ubuntu"
// VERSION = "14.04.3 LTS, Trusty Tahr"
// ID = ubuntu
// ID_LIKE = debian
// PRETTY_NAME = "Ubuntu 14.04.3 LTS"
// VERSION_ID = "14.04"
// HOME_URL = "http://www.ubuntu.com/"
// SUPPORT_URL = "http://help.ubuntu.com/"
// BUG_REPORT_URL = "http://bugs.launchpad.net/ubuntu/"
// We use ID and VERSION_ID
if (File.Exists("/etc/os-release"))
{
var lines = File.ReadAllLines("/etc/os-release");
result = new DistroInfo();
foreach (var line in lines)
{
if (line.StartsWith("ID=", StringComparison.Ordinal))
{
result.Id = line.Substring(3).Trim('"', '\'');
}
else if (line.StartsWith("VERSION_ID=", StringComparison.Ordinal))
{
result.VersionId = line.Substring(11).Trim('"', '\'');
}
}
}
if (result != null)
{
result = NormalizeDistroInfo(result);
}
return result;
}
private static DistroInfo? LoadDistroInfoFromIllumos()
{
DistroInfo? result = null;
// examples:
// on OmniOS
// SunOS 5.11 omnios-r151018-95eaa7e
// on OpenIndiana Hipster:
// SunOS 5.11 illumos-63878f749f
// on SmartOS:
// SunOS 5.11 joyent_20200408T231825Z
var versionDescription = RuntimeInformation.OSDescription.Split(' ')[2];
switch (versionDescription)
{
case string version when version.StartsWith("omnios"):
result = new DistroInfo
{
Id = "OmniOS",
VersionId = version.Substring("omnios-r".Length, 2) // e.g. 15
};
break;
case string version when version.StartsWith("joyent"):
result = new DistroInfo
{
Id = "SmartOS",
VersionId = version.Substring("joyent_".Length, 4) // e.g. 2020
};
break;
case string version when version.StartsWith("illumos"):
result = new DistroInfo
{
Id = "OpenIndiana"
// version-less
};
break;
}
return result;
}
// For some distros, we don't want to use the full version from VERSION_ID. One example is
// Red Hat Enterprise Linux, which includes a minor version in their VERSION_ID but minor
// versions are backwards compatable.
//
// In this case, we'll normalized RIDs like 'rhel.7.2' and 'rhel.7.3' to a generic
// 'rhel.7'. This brings RHEL in line with other distros like CentOS or Debian which
// don't put minor version numbers in their VERSION_ID fields because all minor versions
// are backwards compatible.
private static DistroInfo NormalizeDistroInfo(DistroInfo distroInfo)
{
// Handle if VersionId is null by just setting the index to -1.
int lastVersionNumberSeparatorIndex = distroInfo.VersionId?.IndexOf('.') ?? -1;
if (lastVersionNumberSeparatorIndex != -1 && distroInfo.Id == "alpine")
{
// For Alpine, the version reported has three components, so we need to find the second version separator
lastVersionNumberSeparatorIndex = distroInfo.VersionId?.IndexOf('.', lastVersionNumberSeparatorIndex + 1) ?? -1;
}
if (lastVersionNumberSeparatorIndex != -1 && (distroInfo.Id == "rhel" || distroInfo.Id == "alpine"))
{
distroInfo.VersionId = distroInfo.VersionId?.Substring(0, lastVersionNumberSeparatorIndex);
}
return distroInfo;
}
private static Platform DetermineOSPlatform()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return Platform.Windows;
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return Platform.Linux;
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return Platform.Darwin;
}
#if NET
if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD))
{
return Platform.FreeBSD;
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("ILLUMOS")))
{
return Platform.illumos;
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("SOLARIS")))
{
return Platform.Solaris;
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("HAIKU")))
{
return Platform.Haiku;
}
#endif
return Platform.Unknown;
}
}
|