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)) 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."); 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 public class ServiceDescriptor : IWinSWConfiguration
{ {
protected readonly XmlDocument dom = new(); protected readonly XmlDocument dom = new();
private readonly Dictionary<string, string> environmentVariables; private readonly Dictionary<string, string> environmentVariables;
public static DefaultWinSWSettings Defaults { get; } = new DefaultWinSWSettings(); public static DefaultWinSWSettings Defaults { get; } = new DefaultWinSWSettings();

View File

@ -3,6 +3,9 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
#if VNEXT
using System.IO.Pipes;
#endif
#if NET #if NET
using System.Reflection; using System.Reflection;
#endif #endif
@ -28,6 +31,10 @@ namespace WinSW
{ {
public static class Program public static class Program
{ {
#if VNEXT
private const string NoPipe = "-";
#endif
private static readonly ILog Log = LogManager.GetLogger(typeof(Program)); private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
public static int Main(string[] args) public static int Main(string[] args)
@ -127,7 +134,35 @@ namespace WinSW
_ = ConsoleApis.FreeConsole(); _ = ConsoleApis.FreeConsole();
_ = ConsoleApis.AttachConsole(ConsoleApis.ATTACH_PARENT_PROCESS); _ = 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); args = args.GetRange(1, args.Count - 1);
#endif
} }
else if (Environment.OSVersion.Version.Major == 5) else if (Environment.OSVersion.Version.Major == 5)
{ {
@ -565,18 +600,32 @@ namespace WinSW
{ {
using var current = Process.GetCurrentProcess(); 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 var startInfo = new ProcessStartInfo
{ {
UseShellExecute = true, UseShellExecute = true,
Verb = "runas", Verb = "runas",
FileName = current.MainModule!.FileName!, FileName = current.MainModule!.FileName!,
#if NET Arguments = arguments,
Arguments = "/elevated " + string.Join(' ', args),
#elif !NET20
Arguments = "/elevated " + string.Join(" ", args),
#else
Arguments = "/elevated " + string.Join(" ", args.ToArray()),
#endif
WindowStyle = ProcessWindowStyle.Hidden, WindowStyle = ProcessWindowStyle.Hidden,
}; };
@ -584,6 +633,26 @@ namespace WinSW
{ {
using var elevated = Process.Start(startInfo)!; 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(); elevated.WaitForExit();
Environment.Exit(elevated.ExitCode); Environment.Exit(elevated.ExitCode);
} }
@ -661,7 +730,7 @@ namespace WinSW
var process = ProcessApis.GetCurrentProcess(); var process = ProcessApis.GetCurrentProcess();
if (!ProcessApis.OpenProcessToken(process, TokenAccessLevels.Read, out var token)) if (!ProcessApis.OpenProcessToken(process, TokenAccessLevels.Read, out var token))
{ {
ThrowWin32Exception("Failed to open process token."); Throw.Command.Win32Exception("Failed to open process token.");
} }
try try
@ -673,7 +742,7 @@ namespace WinSW
sizeof(SecurityApis.TOKEN_ELEVATION), sizeof(SecurityApis.TOKEN_ELEVATION),
out _)) out _))
{ {
ThrowWin32Exception("Failed to get token information"); Throw.Command.Win32Exception("Failed to get token information");
} }
return elevation.TokenIsElevated != 0; return elevation.TokenIsElevated != 0;
@ -682,12 +751,6 @@ namespace WinSW
{ {
_ = HandleApis.CloseHandle(token); _ = HandleApis.CloseHandle(token);
} }
static void ThrowWin32Exception(string message)
{
var inner = new Win32Exception();
throw new Win32Exception(inner.NativeErrorCode, message + ' ' + inner.Message);
}
} }
private static string ReadPassword() private static string ReadPassword()