// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Diagnostics; using System.Runtime.InteropServices; namespace UnrealBuildTool { /// /// Host platform abstraction /// public abstract class BuildHostPlatform { private static BuildHostPlatform CurrentPlatform; private static bool bIsMac = File.Exists("/System/Library/CoreServices/SystemVersion.plist"); /// /// Returns the name of platform UBT is running on. Internal use only. If you need access this this enum, use BuildHostPlatform.Current.Platform */ /// private static UnrealTargetPlatform GetRuntimePlatform() { PlatformID Platform = Environment.OSVersion.Platform; switch (Platform) { case PlatformID.Win32NT: return UnrealTargetPlatform.Win64; case PlatformID.Unix: return bIsMac ? UnrealTargetPlatform.Mac : UnrealTargetPlatform.Linux; case PlatformID.MacOSX: return UnrealTargetPlatform.Mac; default: throw new BuildException("Unhandled runtime platform " + Platform); } } /// /// Host platform singleton. /// static public BuildHostPlatform Current { get { if (CurrentPlatform == null) { switch (GetRuntimePlatform()) { case UnrealTargetPlatform.Win64: CurrentPlatform = new WindowsBuildHostPlatform(); break; case UnrealTargetPlatform.Mac: CurrentPlatform = new MacBuildHostPlatform(); break; case UnrealTargetPlatform.Linux: CurrentPlatform = new LinuxBuildHostPlatform(); break; } } return CurrentPlatform; } } /// /// Gets the current host platform type. /// abstract public UnrealTargetPlatform Platform { get; } /// /// Checks the API version of a dynamic library /// /// Filename of the library /// API version of -1 if not found. abstract public int GetDllApiVersion(string Filename); /// /// Class that holds information about a running process /// public class ProcessInfo { /// /// Process ID /// public int PID; /// /// Name of the process /// public string Name; /// /// Filename of the process binary /// public string Filename; public ProcessInfo(int InPID, string InName, string InFilename, string[] InModules) { PID = InPID; Name = InName; Filename = InFilename; } public ProcessInfo(Process Proc) { PID = Proc.Id; Name = Proc.ProcessName; Filename = Path.GetFullPath(Proc.MainModule.FileName); } public override string ToString() { return String.Format("{0}, {1}", Name, Filename); } } /// /// Gets all currently running processes. /// /// public virtual ProcessInfo[] GetProcesses() { var AllProcesses = Process.GetProcesses(); var Result = new List(AllProcesses.Length); foreach (var Proc in AllProcesses) { try { if (!Proc.HasExited) { Result.Add(new ProcessInfo(Proc)); } } catch {} } return Result.ToArray(); } /// /// Gets a process by name. /// /// Name of the process to get information for. /// public virtual ProcessInfo GetProcessByName(string Name) { var AllProcess = GetProcesses(); foreach (var Info in AllProcess) { if (Info.Name == Name) { return Info; } } return null; } /// /// Gets processes by name. /// /// Name of the process to get information for. /// public virtual ProcessInfo[] GetProcessesByName(string Name) { var AllProcess = GetProcesses(); var Result = new List(); foreach (var Info in AllProcess) { if (Info.Name == Name) { Result.Add(Info); } } return Result.ToArray(); } /// /// Gets the filenames of all modules associated with a process /// /// Process ID /// Filename of the binary associated with the process. /// An array of all module filenames associated with the process. Can be empty of the process is no longer running. public virtual string[] GetProcessModules(int PID, string Filename) { List Modules = new List(); try { var Proc = Process.GetProcessById(PID); if (Proc != null) { foreach (var Module in Proc.Modules.Cast()) { Modules.Add(Path.GetFullPath(Module.FileName)); } } } catch { } return Modules.ToArray(); } } class WindowsBuildHostPlatform : BuildHostPlatform { public override UnrealTargetPlatform Platform { get { return UnrealTargetPlatform.Win64; } } [DllImport("kernel32.dll", SetLastError = true)] static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags); [DllImport("kernel32.dll", SetLastError = true)] static extern IntPtr FindResource(IntPtr hModule, string lpName, string lpType); [DllImport("kernel32.dll")] static extern IntPtr FindResource(IntPtr hModule, IntPtr lpID, IntPtr lpType); [DllImport("kernel32.dll", SetLastError = true)] static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo); [DllImport("kernel32.dll", SetLastError = true)] static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo); [DllImport("kernel32.dll", SetLastError = true)] static extern IntPtr LockResource(IntPtr hResData); [DllImport("kernel32.dll", SetLastError = true)] static extern void UnlockResource(IntPtr hResInfo); [DllImport("kernel32.dll", SetLastError = true)] static extern void FreeLibrary(IntPtr hModule); [Flags] enum LoadLibraryFlags : uint { DONT_RESOLVE_DLL_REFERENCES = 0x00000001, LOAD_LIBRARY_AS_DATAFILE = 0x00000002, LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008, LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010, LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040, LOAD_LIBRARY_REQUIRE_SIGNED_TARGET = 0x00000080 } enum ResourceType { RT_CURSOR = 1, RT_BITMAP = 2, RT_ICON = 3, RT_MENU = 4, RT_DIALOG = 5, RT_STRING = 6, RT_FONTDIR = 7, RT_FONT = 8, RT_ACCELERATOR = 9, RT_RCDATA = 10, RT_MESSAGETABLE = 11 } public override int GetDllApiVersion(string Filename) { int Result = -1; try { const int ID_MODULE_API_VERSION_RESOURCE = 191; // Retrieves the embedded API version from a DLL IntPtr hModule = LoadLibraryEx(Filename, IntPtr.Zero, (uint)LoadLibraryFlags.LOAD_LIBRARY_AS_DATAFILE); if (hModule != IntPtr.Zero) { IntPtr hResInfo = FindResource(hModule, new IntPtr(ID_MODULE_API_VERSION_RESOURCE), new IntPtr((int)ResourceType.RT_RCDATA)); if (hResInfo != IntPtr.Zero) { IntPtr hResGlobal = LoadResource(hModule, hResInfo); if (hResGlobal != IntPtr.Zero) { IntPtr pResData = LockResource(hResGlobal); if (pResData != IntPtr.Zero) { uint Length = SizeofResource(hModule, hResInfo); if (Length > 0) { var Str = Marshal.PtrToStringAnsi(pResData); Result = Int32.Parse(Str); } } } } FreeLibrary(hModule); } } catch (Exception Ex) { Log.TraceWarning("Failed to get DLL API version for {0}. Exception: {1}", Filename, Ex.Message); } return Result; } } class MacBuildHostPlatform : BuildHostPlatform { public override UnrealTargetPlatform Platform { get { return UnrealTargetPlatform.Mac; } } public override int GetDllApiVersion(string Filename) { // @TODO: Implement GetDllApiVersion for Mac return -1; } /// /// Currently Mono returns incomplete process names in Process.GetProcesses() so we need to parse 'ps' output. /// /// public override ProcessInfo[] GetProcesses() { var Result = new List(); var StartInfo = new ProcessStartInfo (); StartInfo.FileName = "ps"; StartInfo.Arguments = "-eaw -o pid,comm"; StartInfo.CreateNoWindow = true; StartInfo.UseShellExecute = false; StartInfo.RedirectStandardOutput = true; var Proc = new Process (); Proc.StartInfo = StartInfo; try { Proc.Start(); Proc.WaitForExit(); for (string Line = Proc.StandardOutput.ReadLine(); Line != null; Line = Proc.StandardOutput.ReadLine()) { Line = Line.Trim(); int PIDEnd = Line.IndexOf(' '); var PIDString = Line.Substring(0, PIDEnd); if (PIDString != "PID") { var Filename = Line.Substring(PIDEnd + 1); var Pid = Int32.Parse(PIDString); try { var ExistingProc = Process.GetProcessById(Pid); if (ExistingProc != null && Pid != Process.GetCurrentProcess().Id && ExistingProc.HasExited == false) { var ProcInfo = new ProcessInfo(ExistingProc); ProcInfo.Name = Path.GetFileName(Filename); ProcInfo.Filename = Filename; Result.Add(ProcInfo); } } catch {} } } } catch {} return Result.ToArray(); } /// /// Currently Mono returns incomplete list of modules for Process.Modules so we need to parse vmmap output. /// /// /// /// public override string[] GetProcessModules(int PID, string Filename) { HashSet Modules = new HashSet(); // Add the process file name to the module list. This is to make it compatible with the results of Process.Modules on Windows. Modules.Add(Filename); var StartInfo = new ProcessStartInfo(); StartInfo.FileName = "vmmap"; StartInfo.Arguments = String.Format("{0} -w", PID); StartInfo.CreateNoWindow = true; StartInfo.UseShellExecute = false; StartInfo.RedirectStandardOutput = true; var Proc = new Process(); Proc.StartInfo = StartInfo; try { Proc.Start(); // Start processing output before vmmap exits otherwise it's going to hang while (!Proc.WaitForExit(1)) { ProcessVMMapOutput(Proc, Modules); } ProcessVMMapOutput(Proc, Modules); } catch { } return Modules.ToArray(); } private void ProcessVMMapOutput(Process Proc, HashSet Modules) { for (string Line = Proc.StandardOutput.ReadLine(); Line != null; Line = Proc.StandardOutput.ReadLine()) { Line = Line.Trim(); if (Line.EndsWith(".dylib")) { const int SharingModeLength = 6; int SMStart = Line.IndexOf("SM="); int PathStart = SMStart + SharingModeLength; string Module = Line.Substring(PathStart).Trim(); if (!Modules.Contains(Module)) { Modules.Add(Module); } } } } } class LinuxBuildHostPlatform : BuildHostPlatform { public override UnrealTargetPlatform Platform { get { return UnrealTargetPlatform.Linux; } } public override int GetDllApiVersion(string Filename) { // @TODO: Implement GetDllApiVersion for Linux return -1; } /// /// Currently Mono returns incomplete process names in Process.GetProcesses() so we need to use /proc /// (also, Mono locks up during process traversal sometimes, trying to open /dev/snd/pcm*) /// /// public override ProcessInfo[] GetProcesses() { // @TODO: Implement for Linux return new List().ToArray(); } /// /// Currently Mono returns incomplete list of modules for Process.Modules so we need to parse /proc/PID/maps. /// (also, Mono locks up during process traversal sometimes, trying to open /dev/snd/pcm*) /// /// /// /// public override string[] GetProcessModules(int PID, string Filename) { // @TODO: Implement for Linux return new List().ToArray(); } } }