Redirect I/O of elevated process (#719)

pull/755/head
Next Turn 2020-12-26 20:04:25 +08:00 committed by NextTurn
parent 03930a23d2
commit 48015a5d7a
No known key found for this signature in database
GPG Key ID: 17A0D50ADDE1A0C4
3 changed files with 79 additions and 16 deletions

View File

@ -287,7 +287,6 @@ namespace WinSW.Configuration
{
if (!int.TryParse(this.ZipOlderThanNumDaysYamlLog, out int zipolderthannumdaysValue))
{
// FIXME: Remove the build env specific warning suppression from the codebase
throw new InvalidDataException("Roll-Size-Time Based rolling policy is specified but zipOlderThanNumDays does not match the int format found in configuration XML.");
}

View File

@ -17,6 +17,7 @@ namespace WinSW
public class ServiceDescriptor : IWinSWConfiguration
{
protected readonly XmlDocument dom = new();
private readonly Dictionary<string, string> environmentVariables;
public static DefaultWinSWSettings Defaults { get; } = new DefaultWinSWSettings();

View File

@ -3,6 +3,9 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
#if VNEXT
using System.IO.Pipes;
#endif
#if NET
using System.Reflection;
#endif
@ -28,6 +31,10 @@ namespace WinSW
{
public static class Program
{
#if VNEXT
private const string NoPipe = "-";
#endif
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
public static int Main(string[] args)
@ -127,7 +134,35 @@ namespace WinSW
_ = ConsoleApis.FreeConsole();
_ = ConsoleApis.AttachConsole(ConsoleApis.ATTACH_PARENT_PROCESS);
#if VNEXT
string stdinName = args[1];
if (stdinName != NoPipe)
{
var stdin = new NamedPipeClientStream(".", stdinName, PipeDirection.In, PipeOptions.Asynchronous);
stdin.Connect();
Console.SetIn(new StreamReader(stdin));
}
string stdoutName = args[2];
if (stdoutName != NoPipe)
{
var stdout = new NamedPipeClientStream(".", stdoutName, PipeDirection.Out, PipeOptions.Asynchronous);
stdout.Connect();
Console.SetOut(new StreamWriter(stdout) { AutoFlush = true });
}
string stderrName = args[3];
if (stderrName != NoPipe)
{
var stderr = new NamedPipeClientStream(".", stderrName, PipeDirection.Out, PipeOptions.Asynchronous);
stderr.Connect();
Console.SetError(new StreamWriter(stderr) { AutoFlush = true });
}
args = args.GetRange(4, args.Count - 4);
#else
args = args.GetRange(1, args.Count - 1);
#endif
}
else if (Environment.OSVersion.Version.Major == 5)
{
@ -565,18 +600,32 @@ namespace WinSW
{
using var current = Process.GetCurrentProcess();
#if VNEXT
string? stdinName = Console.IsInputRedirected ? Guid.NewGuid().ToString() : null;
string? stdoutName = Console.IsOutputRedirected ? Guid.NewGuid().ToString() : null;
string? stderrName = Console.IsErrorRedirected ? Guid.NewGuid().ToString() : null;
#endif
string arguments = "/elevated " +
#if VNEXT
" " + (stdinName ?? NoPipe) +
" " + (stdoutName ?? NoPipe) +
" " + (stderrName ?? NoPipe) +
#endif
#if NET
string.Join(' ', args);
#elif !NET20
string.Join(" ", args);
#else
string.Join(" ", args.ToArray());
#endif
var startInfo = new ProcessStartInfo
{
UseShellExecute = true,
Verb = "runas",
FileName = current.MainModule!.FileName!,
#if NET
Arguments = "/elevated " + string.Join(' ', args),
#elif !NET20
Arguments = "/elevated " + string.Join(" ", args),
#else
Arguments = "/elevated " + string.Join(" ", args.ToArray()),
#endif
Arguments = arguments,
WindowStyle = ProcessWindowStyle.Hidden,
};
@ -584,6 +633,26 @@ namespace WinSW
{
using var elevated = Process.Start(startInfo)!;
#if VNEXT
if (stdinName != null)
{
var stdin = new NamedPipeServerStream(stdinName, PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
stdin.WaitForConnectionAsync().ContinueWith(_ => Console.OpenStandardInput().CopyToAsync(stdin));
}
if (stdoutName != null)
{
var stdout = new NamedPipeServerStream(stdoutName, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
stdout.WaitForConnectionAsync().ContinueWith(_ => stdout.CopyToAsync(Console.OpenStandardOutput()));
}
if (stderrName != null)
{
var stderr = new NamedPipeServerStream(stderrName, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
stderr.WaitForConnectionAsync().ContinueWith(_ => stderr.CopyToAsync(Console.OpenStandardError()));
}
#endif
elevated.WaitForExit();
Environment.Exit(elevated.ExitCode);
}
@ -661,7 +730,7 @@ namespace WinSW
var process = ProcessApis.GetCurrentProcess();
if (!ProcessApis.OpenProcessToken(process, TokenAccessLevels.Read, out var token))
{
ThrowWin32Exception("Failed to open process token.");
Throw.Command.Win32Exception("Failed to open process token.");
}
try
@ -673,7 +742,7 @@ namespace WinSW
sizeof(SecurityApis.TOKEN_ELEVATION),
out _))
{
ThrowWin32Exception("Failed to get token information");
Throw.Command.Win32Exception("Failed to get token information");
}
return elevation.TokenIsElevated != 0;
@ -682,12 +751,6 @@ namespace WinSW
{
_ = HandleApis.CloseHandle(token);
}
static void ThrowWin32Exception(string message)
{
var inner = new Win32Exception();
throw new Win32Exception(inner.NativeErrorCode, message + ' ' + inner.Message);
}
}
private static string ReadPassword()