// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.IO; using System.Runtime.InteropServices; namespace Internal.Microsoft.DotNet.PlatformAbstractions.Native { internal static class PlatformApis { private class DistroInfo { public string Id; public string VersionId; } private static readonly Lazy<Platform> _platform = new Lazy<Platform>(DetermineOSPlatform); private static readonly Lazy<DistroInfo> _distroInfo = new Lazy<DistroInfo>(LoadDistroInfo); private static readonly char[] _idTrimChars = [ '"', '\'' ]; public static string GetOSName() { switch (GetOSPlatform()) { case Platform.Windows: return "Windows"; case Platform.Linux: return GetDistroId() ?? "Linux"; case Platform.Darwin: return "Mac OS X"; default: return "Unknown"; } } public static string GetOSVersion() { switch (GetOSPlatform()) { case Platform.Windows: return NativeMethods.Windows.RtlGetVersion() ?? string.Empty; case Platform.Linux: return GetDistroVersionId() ?? string.Empty; case Platform.Darwin: return GetDarwinVersion() ?? string.Empty; default: return string.Empty; } } private static string GetDarwinVersion() { Version version; var kernelRelease = NativeMethods.Darwin.GetKernelRelease(); if (!Version.TryParse(kernelRelease, out version) || version.Major < 5) { // 10.0 covers all versions prior to Darwin 5 // Similarly, if the version is not a valid version number, but we have still detected that it is Darwin, we just assume // it is OS X 10.0 return "10.0"; } else { // Mac OS X 10.1 mapped to Darwin 5.x, and the mapping continues that way // So just subtract 4 from the Darwin version. // https://en.wikipedia.org/wiki/Darwin_%28operating_system%29 return $"10.{version.Major - 4}"; } } public 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() { // 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"); var result = new DistroInfo(); foreach (var line in lines) { if (line.StartsWith("ID=", StringComparison.Ordinal)) { result.Id = line.Substring(3).Trim(_idTrimChars); } else if (line.StartsWith("VERSION_ID=", StringComparison.Ordinal)) { result.VersionId = line.Substring(11).Trim(_idTrimChars); } } return NormalizeDistroInfo(result); } return null; } // 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 compatible. // // 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 minorVersionNumberSeparatorIndex = distroInfo.VersionId?.IndexOf('.') ?? -1; if (distroInfo.Id == "rhel" && minorVersionNumberSeparatorIndex != -1) { distroInfo.VersionId = distroInfo.VersionId.Substring(0, minorVersionNumberSeparatorIndex); } 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; } return Platform.Unknown; } } } |