mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 12:30:32 +00:00
221 lines
7 KiB
C#
221 lines
7 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.IO.Pipes;
|
|
using System.Management.Automation.Runspaces;
|
|
using System.Net.Sockets;
|
|
using System.Reflection;
|
|
using System.Security.AccessControl;
|
|
using System.Security.Principal;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Cosmos.Debug.HyperVServer
|
|
{
|
|
internal sealed class Client : IDisposable
|
|
{
|
|
private const string CreateVm = "CreateVm.ps1";
|
|
private const string RemoveVm = "RemoveVm.ps1";
|
|
private const string StartVm = "StartVm.ps1";
|
|
private const string StopVm = "StopVm.ps1";
|
|
|
|
private readonly Runspace _runspace;
|
|
private readonly TcpClient _tcpClient;
|
|
|
|
private Process _vmClientProcess;
|
|
|
|
private NamedPipeClientStream _namedPipeClient;
|
|
private NamedPipeServerStream _namedPipeServer;
|
|
|
|
public Client(TcpClient tcpClient)
|
|
{
|
|
_runspace = RunspaceFactory.CreateRunspace();
|
|
_runspace.Open();
|
|
|
|
_tcpClient = tcpClient;
|
|
}
|
|
|
|
public async Task RunAsync()
|
|
{
|
|
_namedPipeClient = new NamedPipeClientStream(
|
|
".",
|
|
"CosmosSerialHyperV",
|
|
PipeDirection.InOut,
|
|
PipeOptions.Asynchronous);
|
|
|
|
var pipeSecurity = new PipeSecurity();
|
|
|
|
var identity = WindowsIdentity.GetCurrent().User;
|
|
|
|
var pipeAccessRule = new PipeAccessRule(
|
|
identity,
|
|
PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance,
|
|
AccessControlType.Allow);
|
|
|
|
pipeSecurity.AddAccessRule(pipeAccessRule);
|
|
pipeSecurity.SetOwner(identity);
|
|
|
|
_namedPipeServer = new NamedPipeServerStream(
|
|
"CosmosSerial",
|
|
PipeDirection.InOut,
|
|
1,
|
|
PipeTransmissionMode.Byte,
|
|
PipeOptions.Asynchronous,
|
|
256,
|
|
256,
|
|
pipeSecurity,
|
|
HandleInheritability.None);
|
|
|
|
var namedPipeConnectionTask = Task.Run(WaitForNamedPipeConnections);
|
|
|
|
using (var reader = new StreamReader(_tcpClient.GetStream()))
|
|
{
|
|
while (_tcpClient.Connected)
|
|
{
|
|
if (_tcpClient.Available > 0)
|
|
{
|
|
var command = await reader.ReadLineAsync().ConfigureAwait(false);
|
|
var parts = command.Split(';');
|
|
|
|
switch (parts[0])
|
|
{
|
|
case "CreateVirtualMachine":
|
|
CreateVirtualMachine(parts[1], parts[2], parts[3]);
|
|
break;
|
|
case "RemoveVirtualMachine":
|
|
RemoveVirtualMachine(parts[1]);
|
|
break;
|
|
case "StartVirtualMachine":
|
|
StartVirtualMachine(parts[1]);
|
|
break;
|
|
case "StopVirtualMachine":
|
|
StopVirtualMachine(parts[1]);
|
|
break;
|
|
}
|
|
|
|
reader.BaseStream.WriteByte(0x20);
|
|
}
|
|
else
|
|
{
|
|
await Task.Delay(1000).ConfigureAwait(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
await namedPipeConnectionTask.ConfigureAwait(false);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_runspace.Dispose();
|
|
_tcpClient.Dispose();
|
|
|
|
_namedPipeClient.Dispose();
|
|
_namedPipeServer.Dispose();
|
|
}
|
|
|
|
private void CreateVirtualMachine(string vmName, string hardDiskPath, string isoPath) =>
|
|
RunScript(
|
|
CreateVm,
|
|
("vmName", vmName),
|
|
("hardDiskPath", hardDiskPath),
|
|
("isoPath", isoPath));
|
|
|
|
private void RemoveVirtualMachine(string vmName) =>
|
|
RunScript(
|
|
RemoveVm,
|
|
("vmName", vmName));
|
|
|
|
private void StartVirtualMachine(string vmName)
|
|
{
|
|
_vmClientProcess = Process.Start("vmconnect", $"\"localhost\" \"{vmName}\"");
|
|
|
|
RunScript(
|
|
StartVm,
|
|
("vmName", vmName));
|
|
}
|
|
|
|
private void StopVirtualMachine(string vmName)
|
|
{
|
|
if (!_vmClientProcess.HasExited)
|
|
{
|
|
_vmClientProcess.Kill();
|
|
}
|
|
|
|
RunScript(
|
|
StopVm,
|
|
("vmName", vmName));
|
|
}
|
|
|
|
private void RunScript(string scriptName, params (string Name, object Value)[] parameters)
|
|
{
|
|
using (var pipeline = _runspace.CreatePipeline())
|
|
{
|
|
var assemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
|
var scriptPath = Path.Combine(assemblyDirectory, "scripts", scriptName);
|
|
var command = new Command(File.ReadAllText(scriptPath), true);
|
|
|
|
foreach (var (name, value) in parameters)
|
|
{
|
|
command.Parameters.Add(name, value);
|
|
}
|
|
|
|
pipeline.Commands.Add(command);
|
|
|
|
_ = pipeline.Invoke();
|
|
}
|
|
}
|
|
|
|
private async Task WaitForNamedPipeConnections()
|
|
{
|
|
await Task.WhenAll(
|
|
_namedPipeServer.WaitForConnectionAsync(),
|
|
_namedPipeClient.ConnectAsync()
|
|
).ConfigureAwait(false);
|
|
|
|
await Task.WhenAll(
|
|
Task.Run(ReadPipeAsync),
|
|
Task.Run(WritePipeAsync)
|
|
).ConfigureAwait(false);
|
|
}
|
|
|
|
private async Task ReadPipeAsync()
|
|
{
|
|
var buffer = new byte[256];
|
|
|
|
while (_namedPipeClient.IsConnected
|
|
&& _namedPipeServer.IsConnected)
|
|
{
|
|
var byteCount = await _namedPipeClient.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
|
|
|
|
if (byteCount > 0)
|
|
{
|
|
await _namedPipeServer.WriteAsync(buffer, 0, byteCount).ConfigureAwait(false);
|
|
}
|
|
else
|
|
{
|
|
await Task.Delay(500).ConfigureAwait(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task WritePipeAsync()
|
|
{
|
|
var buffer = new byte[256];
|
|
|
|
while (_namedPipeClient.IsConnected
|
|
&& _namedPipeServer.IsConnected)
|
|
{
|
|
var byteCount = await _namedPipeServer.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
|
|
|
|
if (byteCount > 0)
|
|
{
|
|
await _namedPipeClient.WriteAsync(buffer, 0, byteCount).ConfigureAwait(false);
|
|
}
|
|
else
|
|
{
|
|
await Task.Delay(500).ConfigureAwait(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|