119 lines
3.6 KiB
C#
119 lines
3.6 KiB
C#
|
|
using System.Diagnostics;
|
||
|
|
using System.Runtime.InteropServices;
|
||
|
|
using System.Text.Json;
|
||
|
|
|
||
|
|
namespace Quorate.HostExec;
|
||
|
|
|
||
|
|
internal static class Proxy
|
||
|
|
{
|
||
|
|
private static readonly JsonSerializerOptions Json = new()
|
||
|
|
{
|
||
|
|
PropertyNameCaseInsensitive = true
|
||
|
|
};
|
||
|
|
|
||
|
|
public static async Task<int> Run(string wrapper, IReadOnlyList<string> arguments)
|
||
|
|
{
|
||
|
|
var fullWrapper = Path.GetFullPath(wrapper);
|
||
|
|
var root = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(fullWrapper)!, "../../.."));
|
||
|
|
var config = Read($"{fullWrapper}.json");
|
||
|
|
|
||
|
|
if (config.Version != 1 || config.Kind != "gnu")
|
||
|
|
{
|
||
|
|
throw new InvalidDataException("unsupported proxy configuration");
|
||
|
|
}
|
||
|
|
|
||
|
|
var toolRoot = ToolRoot.Open(root);
|
||
|
|
var executable = toolRoot.Resolve(config.File);
|
||
|
|
|
||
|
|
if (!File.Exists(executable))
|
||
|
|
{
|
||
|
|
throw new FileNotFoundException("proxied tool is unavailable", executable);
|
||
|
|
}
|
||
|
|
|
||
|
|
var start = new ProcessStartInfo(executable)
|
||
|
|
{
|
||
|
|
UseShellExecute = false
|
||
|
|
};
|
||
|
|
|
||
|
|
foreach (var argument in WithSysroot(root, arguments))
|
||
|
|
{
|
||
|
|
start.ArgumentList.Add(argument);
|
||
|
|
}
|
||
|
|
|
||
|
|
using var stop = new CancellationTokenSource();
|
||
|
|
var signalCode = 143;
|
||
|
|
using var interrupt = PosixSignalRegistration.Create(PosixSignal.SIGINT, context =>
|
||
|
|
{
|
||
|
|
context.Cancel = true;
|
||
|
|
signalCode = 130;
|
||
|
|
stop.Cancel();
|
||
|
|
});
|
||
|
|
using var terminate = PosixSignalRegistration.Create(PosixSignal.SIGTERM, context =>
|
||
|
|
{
|
||
|
|
context.Cancel = true;
|
||
|
|
signalCode = 143;
|
||
|
|
stop.Cancel();
|
||
|
|
});
|
||
|
|
using var child = Process.Start(start) ?? throw new InvalidOperationException("proxied tool did not start");
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
await child.WaitForExitAsync(stop.Token).ConfigureAwait(false);
|
||
|
|
return child.ExitCode;
|
||
|
|
}
|
||
|
|
catch (OperationCanceledException) when (stop.IsCancellationRequested)
|
||
|
|
{
|
||
|
|
if (!child.HasExited)
|
||
|
|
{
|
||
|
|
child.Kill(true);
|
||
|
|
await child.WaitForExitAsync().ConfigureAwait(false);
|
||
|
|
}
|
||
|
|
|
||
|
|
return signalCode;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
internal static IReadOnlyList<string> WithSysroot(string root, IReadOnlyList<string> arguments)
|
||
|
|
{
|
||
|
|
var fullRoot = Path.GetFullPath(root);
|
||
|
|
var found = false;
|
||
|
|
|
||
|
|
for (var index = 0; index < arguments.Count; index++)
|
||
|
|
{
|
||
|
|
var argument = arguments[index];
|
||
|
|
string? value = null;
|
||
|
|
|
||
|
|
if (argument == "--sysroot")
|
||
|
|
{
|
||
|
|
if (++index >= arguments.Count)
|
||
|
|
{
|
||
|
|
throw new ArgumentException("sysroot value is missing", nameof(arguments));
|
||
|
|
}
|
||
|
|
|
||
|
|
value = arguments[index];
|
||
|
|
}
|
||
|
|
else if (argument.StartsWith("--sysroot=", StringComparison.Ordinal))
|
||
|
|
{
|
||
|
|
value = argument[10..];
|
||
|
|
}
|
||
|
|
|
||
|
|
if (value is not null)
|
||
|
|
{
|
||
|
|
if (!Path.GetFullPath(value).Equals(fullRoot, StringComparison.Ordinal))
|
||
|
|
{
|
||
|
|
throw new ArgumentException("conflicting sysroot", nameof(arguments));
|
||
|
|
}
|
||
|
|
|
||
|
|
found = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return found ? arguments : [$"--sysroot={fullRoot}", .. arguments];
|
||
|
|
}
|
||
|
|
|
||
|
|
private static ProxyFile Read(string path)
|
||
|
|
{
|
||
|
|
var value = JsonSerializer.Deserialize<ProxyFile>(File.ReadAllBytes(path), Json);
|
||
|
|
return value ?? throw new InvalidDataException("proxy configuration is empty");
|
||
|
|
}
|
||
|
|
}
|