From 223ad2e4215c237ee15da7e28f84a6e766711ba0 Mon Sep 17 00:00:00 2001 From: Julien Roncaglia Date: Sun, 16 Oct 2016 01:52:33 +0200 Subject: [PATCH] Use only managed code --- src/Paket.Bootstrapper/ConsoleRunner.cs | 39 +++++ .../ConsoleRunnerStrategies/ConsoleRunner.cs | 33 ---- .../ConsoleRunnerStrategies/IConsoleRunner.cs | 10 -- .../UnixMonoConsoleRunner.cs | 164 ------------------ .../ConsoleRunnerStrategies/UnixPInvoke.cs | 49 ------ .../WindowsConsoleRunner.cs | 71 -------- .../ConsoleRunnerStrategies/WindowsPInvoke.cs | 101 ----------- .../Paket.Bootstrapper.csproj | 13 +- src/Paket.Bootstrapper/Program.cs | 11 +- .../WindowsProcessArguments.cs | 2 +- 10 files changed, 47 insertions(+), 446 deletions(-) create mode 100644 src/Paket.Bootstrapper/ConsoleRunner.cs delete mode 100644 src/Paket.Bootstrapper/ConsoleRunnerStrategies/ConsoleRunner.cs delete mode 100644 src/Paket.Bootstrapper/ConsoleRunnerStrategies/IConsoleRunner.cs delete mode 100644 src/Paket.Bootstrapper/ConsoleRunnerStrategies/UnixMonoConsoleRunner.cs delete mode 100644 src/Paket.Bootstrapper/ConsoleRunnerStrategies/UnixPInvoke.cs delete mode 100644 src/Paket.Bootstrapper/ConsoleRunnerStrategies/WindowsConsoleRunner.cs delete mode 100644 src/Paket.Bootstrapper/ConsoleRunnerStrategies/WindowsPInvoke.cs rename src/Paket.Bootstrapper/{ConsoleRunnerStrategies => }/WindowsProcessArguments.cs (97%) diff --git a/src/Paket.Bootstrapper/ConsoleRunner.cs b/src/Paket.Bootstrapper/ConsoleRunner.cs new file mode 100644 index 0000000000..d3e0f049e3 --- /dev/null +++ b/src/Paket.Bootstrapper/ConsoleRunner.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace Paket.Bootstrapper +{ + class ConsoleRunner + { + static readonly Version VersionWithFromBootstrapper = new Version("3.23.2"); + + static IEnumerable SetBootstrapperArgument(string program, IEnumerable arguments) + { + var versionInfo = FileVersionInfo.GetVersionInfo(program); + var version = new Version(versionInfo.FileVersion); + return version >= VersionWithFromBootstrapper + ? new[] {"--from-bootstrapper"}.Concat(arguments) + : arguments; + } + + public static int Run(string program, IEnumerable arguments) + { + arguments = SetBootstrapperArgument(program, arguments); + var argString = WindowsProcessArguments.ToString(arguments); + var process = new Process + { + StartInfo = + { + FileName = program, + Arguments = argString, + UseShellExecute = false + } + }; + process.Start(); + process.WaitForExit(); + return process.ExitCode; + } + } +} \ No newline at end of file diff --git a/src/Paket.Bootstrapper/ConsoleRunnerStrategies/ConsoleRunner.cs b/src/Paket.Bootstrapper/ConsoleRunnerStrategies/ConsoleRunner.cs deleted file mode 100644 index 6755934fb4..0000000000 --- a/src/Paket.Bootstrapper/ConsoleRunnerStrategies/ConsoleRunner.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Paket.Bootstrapper.ConsoleRunnerStrategies -{ - class ConsoleRunner : IConsoleRunner - { - public bool IsSupported => runner != null; - - private static readonly IConsoleRunner[] Runners = { - new WindowsConsoleRunner(), - new UnixMonoConsoleRunner() - }; - - private readonly IConsoleRunner runner; - - public ConsoleRunner() - { - runner = Runners.FirstOrDefault(r => r.IsSupported); - } - - public int Run(string program, IEnumerable arguments) - { - if (runner == null) - { - throw new InvalidOperationException("No supported runner found"); - } - - return runner.Run(program, arguments); - } - } -} \ No newline at end of file diff --git a/src/Paket.Bootstrapper/ConsoleRunnerStrategies/IConsoleRunner.cs b/src/Paket.Bootstrapper/ConsoleRunnerStrategies/IConsoleRunner.cs deleted file mode 100644 index 909fa3181a..0000000000 --- a/src/Paket.Bootstrapper/ConsoleRunnerStrategies/IConsoleRunner.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Paket.Bootstrapper.ConsoleRunnerStrategies -{ - interface IConsoleRunner - { - bool IsSupported { get; } - int Run(string program, IEnumerable arguments); - } -} \ No newline at end of file diff --git a/src/Paket.Bootstrapper/ConsoleRunnerStrategies/UnixMonoConsoleRunner.cs b/src/Paket.Bootstrapper/ConsoleRunnerStrategies/UnixMonoConsoleRunner.cs deleted file mode 100644 index 608f7def1a..0000000000 --- a/src/Paket.Bootstrapper/ConsoleRunnerStrategies/UnixMonoConsoleRunner.cs +++ /dev/null @@ -1,164 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Text; -using static Paket.Bootstrapper.ConsoleRunnerStrategies.UnixPInvoke; - -namespace Paket.Bootstrapper.ConsoleRunnerStrategies -{ - class UnixMonoConsoleRunner : IConsoleRunner - { - private const int EXECV_FAILED_EXIT_STATUS = 0xFF; - private static readonly bool IsMono = Type.GetType("Mono.Runtime") != null; - - private static readonly PlatformID[] Platforms = - { - PlatformID.Unix, - PlatformID.MacOSX - }; - - public bool IsSupported => IsMono && Platforms.Contains(Environment.OSVersion.Platform); - - public unsafe int Run(string program, IEnumerable arguments) - { - // This whole method is doing dangerous things. - // - // We can't use 'execv' directly as it would completly replace our process, that would result in mono not - // call it's tty_teardown (https://github.com/mono/mono/blob/0bcbe39b148bb498742fc68416f8293ccd350fb6/mono/metadata/console-unix.c#L202) - // method and the console might look weird. Also we can't execute code anymore after that. - // - // The solution is to to use the traditional 'fork' pattern but mono doesn't compile it's GC with - // '#define HANDLE_FORK' it seem so it's dangerous. Reason is that the child side of a fork only get one - // thread all other threads don't exists anymore, including a potential GC thread that could have some lock - // held. - // - // Luckily that would only be a problem if we needed GC to be functional in the child process, but we don't - // so we preallocate everything. The runtime could theorically still require the GC but we use a CER to - // completly disable this behavior. - var finalArguments = new[] {"mono", program}.Concat(arguments).ToArray(); - - // Unix environment are using UTF-8, we prealocate everything and pin the memory we will need - using (var path = new Utf8UnmanagedString("mono")) - using (var argv = new Utf8UnmanagedStringArray(finalArguments)) - { - // Try to ensure that the GC isn't running, in theory we won't trigger it during our CER block but - // better safe than sorry. - GC.Collect(); - GC.WaitForPendingFinalizers(); - GC.Collect(); - GC.WaitForPendingFinalizers(); - - int childPid; - // Start a CER as we need to avoid any allocations by the runtime itself inside of the forked child, - // none of the GC infrastructure is running there, it would freeze the process. - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - } - finally - { - childPid = fork(); - if (childPid == 0) - { - // If execvp succeed it will replace our process. - execvp(path.Pointer, argv.Pointer); - - // If execvp failed we can't do anything much, especially not let mono continue, thinking that - // it can run managed code. So we kill our process. - _exit(EXECV_FAILED_EXIT_STATUS); - } - } - - int status; - if (waitpid(childPid, out status, 0) != -1) - { - if (WIFEXITED(status)) - { - var exitCode = WEXITSTATUS(status); - if (exitCode != EXECV_FAILED_EXIT_STATUS) - { - return exitCode; - } - } - } - - throw new InvalidOperationException("Unable to run under mono"); - } - } - - private unsafe class Utf8UnmanagedString : IDisposable - { - private readonly GCHandle handle; - - public Utf8UnmanagedString(string str) - { - var count = Encoding.UTF8.GetByteCount(str); - var bytes = new byte[count + 1]; - Encoding.UTF8.GetBytes(str, 0, str.Length, bytes, 0); - handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); - } - - public byte* Pointer => (byte*) handle.AddrOfPinnedObject().ToPointer(); - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~Utf8UnmanagedString() - { - Dispose(false); - } - - private void Dispose(bool disposing) - { - handle.Free(); - } - } - - private unsafe class Utf8UnmanagedStringArray : IDisposable - { - private readonly GCHandle handle; - private readonly Utf8UnmanagedString[] unmanagedStrings; - - public Utf8UnmanagedStringArray(string[] strings) - { - unmanagedStrings = strings.Select(s => new Utf8UnmanagedString(s)).ToArray(); - var unmanagedPointers = new byte*[unmanagedStrings.Length + 1]; - for (var i = 0; i < unmanagedStrings.Length; i++) - { - unmanagedPointers[i] = unmanagedStrings[i].Pointer; - } - handle = GCHandle.Alloc(unmanagedPointers, GCHandleType.Pinned); - } - - public byte** Pointer => (byte**) handle.AddrOfPinnedObject().ToPointer(); - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - ~Utf8UnmanagedStringArray() - { - Dispose(false); - } - - private void Dispose(bool disposing) - { - handle.Free(); - if (disposing) - { - foreach (var s in unmanagedStrings) - { - s.Dispose(); - } - } - } - } - } -} \ No newline at end of file diff --git a/src/Paket.Bootstrapper/ConsoleRunnerStrategies/UnixPInvoke.cs b/src/Paket.Bootstrapper/ConsoleRunnerStrategies/UnixPInvoke.cs deleted file mode 100644 index da7a7129bc..0000000000 --- a/src/Paket.Bootstrapper/ConsoleRunnerStrategies/UnixPInvoke.cs +++ /dev/null @@ -1,49 +0,0 @@ -// From the mono project https://github.com/mono/mono/blob/master/mcs/class/Mono.Posix/Mono.Unix.Native/Syscall.cs -// (C) 2003 Novell, Inc. -// (C) 2004-2006 Jonathan Pryor -// - -// -// 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. -// - -using System.Runtime.InteropServices; - -namespace Paket.Bootstrapper.ConsoleRunnerStrategies -{ - class UnixPInvoke - { - [DllImport ("libc", SetLastError=true)] - public static extern unsafe int execvp (byte* path, byte** argv); - - [DllImport ("libc", SetLastError=true)] - public static extern int fork (); - - [DllImport ("libc", SetLastError=true)] - public static extern int waitpid (int pid, out int status, int options); - - [DllImport ("libc", SetLastError=true)] - public static extern int _exit (int status); - - public static int WEXITSTATUS(int status) => (status & 0xff00) >> 8; - public static int WTERMSIG(int status) => status & 0x7f; - public static bool WIFEXITED(int status) => WTERMSIG(status) == 0; - } -} \ No newline at end of file diff --git a/src/Paket.Bootstrapper/ConsoleRunnerStrategies/WindowsConsoleRunner.cs b/src/Paket.Bootstrapper/ConsoleRunnerStrategies/WindowsConsoleRunner.cs deleted file mode 100644 index 01d6871954..0000000000 --- a/src/Paket.Bootstrapper/ConsoleRunnerStrategies/WindowsConsoleRunner.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using static Paket.Bootstrapper.ConsoleRunnerStrategies.WindowsPInvoke; - -namespace Paket.Bootstrapper.ConsoleRunnerStrategies -{ - class WindowsConsoleRunner : IConsoleRunner - { - private static readonly PlatformID[] Platforms = { - PlatformID.Win32NT, - PlatformID.Win32Windows - }; - - public bool IsSupported => Platforms.Contains(Environment.OSVersion.Platform); - - public unsafe int Run(string program, IEnumerable arguments) - { - program = Path.GetFullPath(program); - - var argString = WindowsProcessArguments.ToString(new [] { program }.Concat(arguments)); - var startupInfo = STARTUPINFO.Create(); - - bool result; - int exitCode = 0; - PROCESS_INFORMATION processInfo; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - } - finally - { - // We must use PInvoke instead of Process.Start because we want to pass our console handles untouched - // and it's an unsupported scenario. - result = CreateProcess( - program, - argString, - null, - null, - true, - 0, - null, - null, - ref startupInfo, - out processInfo); - - if (result) - { - var hProcess = new SafeObjectHandle(processInfo.hProcess); - var hThread = new SafeObjectHandle(processInfo.hThread); - WaitForSingleObject(hProcess, -1); - - GetExitCodeProcess(processInfo.hProcess, out exitCode); - - hProcess.Close(); - hThread.Close(); - } - } - - if (!result) - { - throw new Win32Exception(); - } - - return exitCode; - } - } -} diff --git a/src/Paket.Bootstrapper/ConsoleRunnerStrategies/WindowsPInvoke.cs b/src/Paket.Bootstrapper/ConsoleRunnerStrategies/WindowsPInvoke.cs deleted file mode 100644 index 3009eb8828..0000000000 --- a/src/Paket.Bootstrapper/ConsoleRunnerStrategies/WindowsPInvoke.cs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) to owners found in https://github.com/AArnott/pinvoke/blob/master/COPYRIGHT.md. All rights reserved. -// Licensed under the MIT license. See https://github.com/AArnott/pinvoke/blob/master/LICENSE.txt for full license information. - -using System; -using System.Runtime.InteropServices; - -namespace Paket.Bootstrapper.ConsoleRunnerStrategies -{ - class WindowsPInvoke - { - public static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); - - [DllImport("Kernel32", SetLastError = true)] - public static extern bool CloseHandle(IntPtr hObject); - - public class SafeObjectHandle : SafeHandle - { - public static readonly SafeObjectHandle Invalid = new SafeObjectHandle(); - public static readonly SafeObjectHandle Null = new SafeObjectHandle(IntPtr.Zero, false); - - public SafeObjectHandle() - : base(INVALID_HANDLE_VALUE, true) - { - } - - public SafeObjectHandle(IntPtr preexistingHandle, bool ownsHandle = true) - : base(INVALID_HANDLE_VALUE, ownsHandle) - { - SetHandle(preexistingHandle); - } - - public override bool IsInvalid => handle == INVALID_HANDLE_VALUE || handle == IntPtr.Zero; - protected override bool ReleaseHandle() => CloseHandle(handle); - } - - [StructLayout(LayoutKind.Sequential)] - public struct STARTUPINFO - { - public int cb; - public string lpReserved; - public string lpDesktop; - public string lpTitle; - public int dwX; - public int dwY; - public int dwXSize; - public int dwYSize; - public int dwXCountChars; - public int dwYCountChars; - public uint dwFillAttribute; - public uint dwFlags; - public ushort wShowWindow; - public ushort cbReserved2; - public IntPtr lpReserved2; - public SafeObjectHandle hStdInput; - public SafeObjectHandle hStdOutput; - public SafeObjectHandle hStdError; - public static STARTUPINFO Create() - { - return new STARTUPINFO - { - cb = Marshal.SizeOf(typeof(STARTUPINFO)), - hStdInput = SafeObjectHandle.Null, - hStdOutput = SafeObjectHandle.Null, - hStdError = SafeObjectHandle.Null, - }; - } - } - - [StructLayout(LayoutKind.Sequential)] - public struct PROCESS_INFORMATION - { - public IntPtr hProcess; - public IntPtr hThread; - public int dwProcessId; - public int dwThreadId; - } - - [DllImport("Kernel32", SetLastError = true, CharSet = CharSet.Unicode)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern unsafe bool CreateProcess( - string lpApplicationName, - string lpCommandLine, - void* lpProcessAttributes, - void* lpThreadAttributes, - [MarshalAs(UnmanagedType.Bool)] bool bInheritHandles, - uint dwCreationFlags, - void* lpEnvironment, - string lpCurrentDirectory, - ref STARTUPINFO lpStartupInfo, - out PROCESS_INFORMATION lpProcessInformation); - - [DllImport("Kernel32", SetLastError = true)] - public static extern uint WaitForSingleObject( - SafeHandle hHandle, - int dwMilliseconds); - - [DllImport("Kernel32", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetExitCodeProcess(IntPtr hProcess, out int lpExitCode); - } -} \ No newline at end of file diff --git a/src/Paket.Bootstrapper/Paket.Bootstrapper.csproj b/src/Paket.Bootstrapper/Paket.Bootstrapper.csproj index 96400c0929..07b8b7fbce 100644 --- a/src/Paket.Bootstrapper/Paket.Bootstrapper.csproj +++ b/src/Paket.Bootstrapper/Paket.Bootstrapper.csproj @@ -22,7 +22,7 @@ DEBUG;TRACE prompt 4 - true + false AnyCPU @@ -32,7 +32,7 @@ TRACE prompt 4 - true + false @@ -53,13 +53,8 @@ - - - - - - - + + diff --git a/src/Paket.Bootstrapper/Program.cs b/src/Paket.Bootstrapper/Program.cs index 3958c4fde3..73a7da80a9 100644 --- a/src/Paket.Bootstrapper/Program.cs +++ b/src/Paket.Bootstrapper/Program.cs @@ -1,11 +1,11 @@ using System; using System.Configuration; +using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.CompilerServices; -using Paket.Bootstrapper.ConsoleRunnerStrategies; using Paket.Bootstrapper.DownloadStrategies; using Paket.Bootstrapper.HelperProxies; @@ -35,11 +35,6 @@ static void Main(string[] args) ConsoleImpl.WriteDebug(BootstrapperHelper.HelpText); return; } - if (options.Run && !ConsoleRunner.IsSupported) - { - ConsoleImpl.WriteError("The current platform isn't supported by --run"); - return; - } ConsoleImpl.IsSilent = options.Silent; if (options.UnprocessedCommandArgs.Any()) @@ -50,7 +45,7 @@ static void Main(string[] args) StartPaketBootstrapping(effectiveStrategy, options.DownloadArguments, new FileProxy(), () => OnSuccessfulDownload(options)); } - static void OnSuccessfulDownload(BootstrapperOptions options) + private static void OnSuccessfulDownload(BootstrapperOptions options) { if (options.Run && File.Exists(options.DownloadArguments.Target)) { @@ -62,7 +57,7 @@ static void OnSuccessfulDownload(BootstrapperOptions options) } catch (Exception e) { - ConsoleImpl.WriteError("Running paket failed with: {0}", e.Message); + ConsoleImpl.WriteError("Running paket failed with: {0}", e); } } } diff --git a/src/Paket.Bootstrapper/ConsoleRunnerStrategies/WindowsProcessArguments.cs b/src/Paket.Bootstrapper/WindowsProcessArguments.cs similarity index 97% rename from src/Paket.Bootstrapper/ConsoleRunnerStrategies/WindowsProcessArguments.cs rename to src/Paket.Bootstrapper/WindowsProcessArguments.cs index f46ce70078..8afbc73316 100644 --- a/src/Paket.Bootstrapper/ConsoleRunnerStrategies/WindowsProcessArguments.cs +++ b/src/Paket.Bootstrapper/WindowsProcessArguments.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Text; -namespace Paket.Bootstrapper.ConsoleRunnerStrategies +namespace Paket.Bootstrapper { static class WindowsProcessArguments {