// /*************************************************************************** // The Disc Image Chef // ---------------------------------------------------------------------------- // // Filename : DetectOS.cs // Author(s) : Natalia Portillo // // Component : Interop services. // // --[ Description ] ---------------------------------------------------------- // // Detects underlying operating system. // // --[ License ] -------------------------------------------------------------- // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // ---------------------------------------------------------------------------- // Copyright © 2011-2018 Natalia Portillo // ****************************************************************************/ using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; namespace DiscImageChef.Interop { public static class DetectOS { public static readonly bool IsMono = Type.GetType("Mono.Runtime") != null; /// Checks if the underlying runtime runs in 64-bit mode public static readonly bool Is64Bit = IntPtr.Size == 8; /// Checks if the underlying runtime runs in 32-bit mode public static readonly bool Is32Bit = IntPtr.Size == 4; [DllImport("libc", SetLastError = true)] static extern int uname(out utsname name); [DllImport("libc", SetLastError = true, EntryPoint = "sysctlbyname", CharSet = CharSet.Ansi)] static extern int OSX_sysctlbyname(string name, IntPtr oldp, IntPtr oldlenp, IntPtr newp, uint newlen); /// Gets the real platform ID, not the incomplete .NET framework one /// Platform ID /// Unhandled exception public static PlatformID GetRealPlatformID() { if((int)Environment.OSVersion.Platform < 4 || (int)Environment.OSVersion.Platform == 5) return (PlatformID)(int)Environment.OSVersion.Platform; int error = uname(out utsname unixname); if(error != 0) throw new Exception($"Unhandled exception calling uname: {Marshal.GetLastWin32Error()}"); switch(unixname.sysname) { // TODO: Differentiate Linux, Android, Tizen case "Linux": { #if __ANDROID__ return PlatformID.Android; #else return PlatformID.Linux; #endif } case "Darwin": { int osxError; IntPtr pLen = Marshal.AllocHGlobal(sizeof(int)); osxError = OSX_sysctlbyname("hw.machine", IntPtr.Zero, pLen, IntPtr.Zero, 0); if(osxError != 0) { Marshal.FreeHGlobal(pLen); throw new Exception($"Unhandled exception calling uname: {Marshal.GetLastWin32Error()}"); } int length = Marshal.ReadInt32(pLen); IntPtr pStr = Marshal.AllocHGlobal(length); osxError = OSX_sysctlbyname("hw.machine", pStr, pLen, IntPtr.Zero, 0); if(osxError != 0) { Marshal.FreeHGlobal(pStr); Marshal.FreeHGlobal(pLen); throw new Exception($"Unhandled exception calling uname: {Marshal.GetLastWin32Error()}"); } string machine = Marshal.PtrToStringAnsi(pStr); Marshal.FreeHGlobal(pStr); Marshal.FreeHGlobal(pLen); if(machine != null && (machine.StartsWith("iPad", StringComparison.Ordinal) || machine.StartsWith("iPod", StringComparison.Ordinal) || machine.StartsWith("iPhone", StringComparison.Ordinal))) return PlatformID.iOS; return PlatformID.MacOSX; } case "GNU": return PlatformID.Hurd; case "FreeBSD": case "GNU/kFreeBSD": return PlatformID.FreeBSD; case "DragonFly": return PlatformID.DragonFly; case "Haiku": return PlatformID.Haiku; case "HP-UX": return PlatformID.HPUX; case "AIX": return PlatformID.AIX; case "OS400": return PlatformID.OS400; case "IRIX": case "IRIX64": return PlatformID.IRIX; case "Minix": return PlatformID.Minix; case "NetBSD": return PlatformID.NetBSD; case "NONSTOP_KERNEL": return PlatformID.NonStop; case "OpenBSD": return PlatformID.OpenBSD; case "QNX": return PlatformID.QNX; case "SINIX-Y": return PlatformID.SINIX; case "SunOS": return PlatformID.Solaris; case "OSF1": return PlatformID.Tru64; case "ULTRIX": return PlatformID.Ultrix; case "SCO_SV": return PlatformID.OpenServer; case "UnixWare": return PlatformID.UnixWare; case "Interix": case "UWIN-W7": return PlatformID.Win32NT; default: { if(unixname.sysname.StartsWith("CYGWIN_NT", StringComparison.Ordinal) || unixname.sysname.StartsWith("MINGW32_NT", StringComparison.Ordinal) || unixname.sysname.StartsWith("MSYS_NT", StringComparison.Ordinal) || unixname.sysname.StartsWith("UWIN", StringComparison.Ordinal)) return PlatformID.Win32NT; return PlatformID.Unknown; } } } /// Gets a string for the current operating system REAL version (handles Darwin 1.4 and Windows 10 falsifying) /// Current operating system version public static string GetVersion() { string environ = Environment.OSVersion.Version.ToString(); switch(GetRealPlatformID()) { case PlatformID.MacOSX: if(Environment.OSVersion.Version.Major != 1) return $"10.{Environment.OSVersion.Version.Major - 4}.{Environment.OSVersion.Version.Minor}"; switch(Environment.OSVersion.Version.Minor) { case 3: return "10.0"; case 4: return "10.1"; } goto default; case PlatformID.Win32NT: // From Windows 8.1 the reported version is simply falsified... if((Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Major >= 2) || Environment.OSVersion.Version.Major > 6) return FileVersionInfo. GetVersionInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "KERNEL32.DLL")).ProductVersion; return environ; default: return environ; } } /// From a platform ID and version returns a human-readable version /// Platform ID /// Version number /// Operating system name public static string GetPlatformName(PlatformID id, string version = null) { switch(id) { case PlatformID.AIX: return "AIX"; case PlatformID.Android: return "Android"; case PlatformID.DragonFly: return "DragonFly BSD"; case PlatformID.FreeBSD: return "FreeBSD"; case PlatformID.Haiku: return "Haiku"; case PlatformID.HPUX: return "HP/UX"; case PlatformID.Hurd: return "Hurd"; case PlatformID.iOS: return "iOS"; case PlatformID.IRIX: return "IRIX"; case PlatformID.Linux: return "Linux"; case PlatformID.MacOSX: if(string.IsNullOrEmpty(version)) return "macOS"; string[] pieces = version.Split('.'); if(pieces.Length < 2 || !int.TryParse(pieces[1], out int minor)) return "macOS"; if(minor >= 12) return "macOS"; if(minor >= 8) return "OS X"; return "Mac OS X"; case PlatformID.Minix: return "MINIX"; case PlatformID.NetBSD: return "NetBSD"; case PlatformID.NonStop: return "NonStop OS"; case PlatformID.OpenBSD: return "OpenBSD"; case PlatformID.OpenServer: return "SCO OpenServer"; case PlatformID.OS400: return "OS/400"; case PlatformID.PlayStation3: return "Sony CellOS"; case PlatformID.PlayStation4: return "Sony Orbis OS"; case PlatformID.QNX: return "QNX"; case PlatformID.SINIX: return "SINIX"; case PlatformID.Solaris: return "Sun Solaris"; case PlatformID.Tizen: return "Samsung Tizen"; case PlatformID.Tru64: return "Tru64 UNIX"; case PlatformID.Ultrix: return "Ultrix"; case PlatformID.Unix: return "UNIX"; case PlatformID.UnixWare: return "SCO UnixWare"; case PlatformID.Wii: return "Nintendo Wii"; case PlatformID.WiiU: return "Nintendo Wii U"; case PlatformID.Win32NT: if(string.IsNullOrEmpty(version)) return "Windows NT/2000/XP/Vista/7/10"; if(version.StartsWith("3.", StringComparison.Ordinal) || version.StartsWith("4.", StringComparison.Ordinal)) return "Windows NT"; if(version.StartsWith("5.0", StringComparison.Ordinal)) return "Windows 2000"; if(version.StartsWith("5.1", StringComparison.Ordinal)) return "Windows XP"; if(version.StartsWith("5.2", StringComparison.Ordinal)) return "Windows 2003"; if(version.StartsWith("6.0", StringComparison.Ordinal)) return "Windows Vista"; if(version.StartsWith("6.1", StringComparison.Ordinal)) return "Windows 7"; if(version.StartsWith("6.2", StringComparison.Ordinal)) return "Windows 8"; if(version.StartsWith("6.3", StringComparison.Ordinal)) return "Windows 8.1"; if(version.StartsWith("10.0", StringComparison.Ordinal)) return "Windows 10"; return "Windows NT/2000/XP/Vista/7/10"; case PlatformID.Win32S: return "Windows 3.x with win32s"; case PlatformID.Win32Windows: if(string.IsNullOrEmpty(version)) return "Windows 9x/Me"; if(version.StartsWith("4.0", StringComparison.Ordinal)) return "Windows 95"; if(version.StartsWith("4.10.2222", StringComparison.Ordinal)) return "Windows 98 SE"; if(version.StartsWith("4.1", StringComparison.Ordinal)) return "Windows 98"; if(version.StartsWith("4.9", StringComparison.Ordinal)) return "Windows Me"; return "Windows 9x/Me"; case PlatformID.WinCE: return "Windows CE/Mobile"; case PlatformID.WindowsPhone: return "Windows Phone"; case PlatformID.Xbox: return "Xbox OS"; case PlatformID.zOS: return "z/OS"; default: return id.ToString(); } } /// POSIX uname structure, size from OSX, big enough to handle extra fields [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] struct utsname { /// System name [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public readonly string sysname; /// Node name [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public readonly string nodename; /// Release level [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public readonly string release; /// Version level [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public readonly string version; /// Hardware level [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public readonly string machine; } } }