|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System.Diagnostics;
using System.IO.Pipes;
using System.Reflection;
using System.Runtime.Versioning;
namespace Microsoft.DotNet.Cli.Installer.Windows;
/// <summary>
/// Encapsulates information about elevation to support workload installations.
/// </summary>
[SupportedOSPlatform("windows")]
internal sealed class InstallClientElevationContext(ISynchronizingLogger logger) : InstallElevationContextBase
{
private readonly ISynchronizingLogger _log = logger;
private Process _serverProcess;
public override bool IsClient => true;
/// <summary>
/// Starts the elevated install server.
/// </summary>
public override void Elevate()
{
if (!IsElevated && !HasElevated)
{
// Use the path of the current host, otherwise we risk resolving against the wrong SDK version.
// To trigger UAC, UseShellExecute must be true and Verb must be "runas".
ProcessStartInfo startInfo = new($@"""{Environment.ProcessPath}""",
$@"""{Assembly.GetExecutingAssembly().Location}"" workload elevate")
{
Verb = "runas",
UseShellExecute = true,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
};
_log?.LogMessage($"Attempting to start the elevated command instance. {startInfo.FileName} {startInfo.Arguments}.");
_serverProcess = new Process
{
StartInfo = startInfo,
EnableRaisingEvents = true,
};
_serverProcess.Exited += ServerExited;
if (_serverProcess.Start())
{
InitializeDispatcher(new NamedPipeClientStream(".", WindowsUtils.CreatePipeName(_serverProcess.Id), PipeDirection.InOut));
Dispatcher.Connect();
// Add a pipe to the logger to allow the server to send log requests. This avoids having an elevated process writing
// to a less privileged location. It also simplifies troubleshooting because log events will be chronologically
// ordered in a single file.
_log.AddNamedPipe(WindowsUtils.CreatePipeName(_serverProcess.Id, "log"));
HasElevated = true;
_log.LogMessage("Elevated command instance started.");
}
else
{
_log?.LogMessage($"Failed to start the elevated command instance.");
throw new Exception("Failed to start the elevated command instance.");
}
}
}
private void ServerExited(object sender, EventArgs e)
{
_log?.LogMessage($"Elevated command instance has exited.");
}
}
|