using System.Diagnostics;
using System.Text.Json;
namespace XFE.SeAgent.Cli;
public sealed record AgentEndpoint(string Version, int ProtocolVersion, string PipeName, int Pid, DateTimeOffset StartTimeUtc, string ExecutablePath, DateTimeOffset CreatedUtc);
public sealed record ProcessIdentity(string ExecutablePath, DateTimeOffset StartTimeUtc);
public interface IProcessInspector
{
ProcessIdentity? Inspect(int pid);
}
public sealed class LocalProcessInspector : IProcessInspector
{
public ProcessIdentity? Inspect(int pid)
{
try
{
using var process = Process.GetProcessById(pid);
if (process.HasExited || process.MainModule?.FileName is not string executable) return null;
return new(executable, new DateTimeOffset(process.StartTime.ToUniversalTime()));
}
catch (ArgumentException) { return null; }
catch (InvalidOperationException) { return null; }
catch (System.ComponentModel.Win32Exception) { return null; }
}
}
public sealed class EndpointDiscovery(IProcessInspector? inspector = null)
{
private readonly IProcessInspector inspector = inspector ?? new LocalProcessInspector();
private static readonly JsonSerializerOptions Json = new(JsonSerializerDefaults.Web);
public static string DefaultDirectory => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "XFE", "SpaceEngineersAgent", "endpoints");
public IReadOnlyList<AgentEndpoint> Discover(string? directory = null)
{
directory ??= DefaultDirectory;
if (!Directory.Exists(directory)) return [];
var found = new List<AgentEndpoint>();
foreach (string file in Directory.EnumerateFiles(directory, "*.json").Take(1024))
{
try
{
if (new FileInfo(file).Length > 65536) continue;
var endpoint = JsonSerializer.Deserialize<AgentEndpoint>(File.ReadAllText(file), Json);
if (endpoint == null || endpoint.ProtocolVersion != 1 || endpoint.Pid <= 0 || string.IsNullOrWhiteSpace(endpoint.Version) || endpoint.PipeName != "XFE.SE.Agent." + endpoint.Pid ||
!Path.IsPathFullyQualified(endpoint.ExecutablePath) || Path.GetFileNameWithoutExtension(file) != endpoint.Pid.ToString(System.Globalization.CultureInfo.InvariantCulture)) continue;
var process = inspector.Inspect(endpoint.Pid);
if (process == null || process.StartTimeUtc.UtcTicks != endpoint.StartTimeUtc.UtcTicks || !Path.GetFullPath(process.ExecutablePath).Equals(Path.GetFullPath(endpoint.ExecutablePath), StringComparison.OrdinalIgnoreCase)) continue;
found.Add(endpoint);
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or JsonException or ArgumentException) { }
}
return found.OrderBy(item => item.Pid).ToArray();
}
}
using System.Diagnostics;
using System.Text.Json;
namespace XFE.SeAgent.Cli;
public sealed record AgentEndpoint(string Version, int ProtocolVersion, string PipeName, int Pid, DateTimeOffset StartTimeUtc, string ExecutablePath, DateTimeOffset CreatedUtc);
public sealed record ProcessIdentity(string ExecutablePath, DateTimeOffset StartTimeUtc);
public interface IProcessInspector
{
ProcessIdentity? Inspect(int pid);
}
public sealed class LocalProcessInspector : IProcessInspector
{
public ProcessIdentity? Inspect(int pid)
{
try
{
using var process = Process.GetProcessById(pid);
if (process.HasExited || process.MainModule?.FileName is not string executable) return null;
return new(executable, new DateTimeOffset(process.StartTime.ToUniversalTime()));
}
catch (ArgumentException) { return null; }
catch (InvalidOperationException) { return null; }
catch (System.ComponentModel.Win32Exception) { return null; }
}
}
public sealed class EndpointDiscovery(IProcessInspector? inspector = null)
{
private readonly IProcessInspector inspector = inspector ?? new LocalProcessInspector();
private static readonly JsonSerializerOptions Json = new(JsonSerializerDefaults.Web);
public static string DefaultDirectory => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "XFE", "SpaceEngineersAgent", "endpoints");
public IReadOnlyList<AgentEndpoint> Discover(string? directory = null)
{
directory ??= DefaultDirectory;
if (!Directory.Exists(directory)) return [];
var found = new List<AgentEndpoint>();
foreach (string file in Directory.EnumerateFiles(directory, "*.json").Take(1024))
{
try
{
if (new FileInfo(file).Length > 65536) continue;
var endpoint = JsonSerializer.Deserialize<AgentEndpoint>(File.ReadAllText(file), Json);
if (endpoint == null || endpoint.ProtocolVersion != 1 || endpoint.Pid <= 0 || string.IsNullOrWhiteSpace(endpoint.Version) || endpoint.PipeName != "XFE.SE.Agent." + endpoint.Pid ||
!Path.IsPathFullyQualified(endpoint.ExecutablePath) || Path.GetFileNameWithoutExtension(file) != endpoint.Pid.ToString(System.Globalization.CultureInfo.InvariantCulture)) continue;
var process = inspector.Inspect(endpoint.Pid);
if (process == null || process.StartTimeUtc.UtcTicks != endpoint.StartTimeUtc.UtcTicks || !Path.GetFullPath(process.ExecutablePath).Equals(Path.GetFullPath(endpoint.ExecutablePath), StringComparison.OrdinalIgnoreCase)) continue;
found.Add(endpoint);
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or JsonException or ArgumentException) { }
}
return found.OrderBy(item => item.Pid).ToArray();
}
}