1d9e3afadd
Server-002: the gateway never terminated leftover MxGateway.Worker.exe processes at startup, contradicting gateway.md and CLAUDE.md. Added IRunningProcessInspector/SystemRunningProcessInspector, OrphanWorkerTerminator, and OrphanWorkerCleanupHostedService (best-effort, runs before sessions are accepted); updated gateway.md to describe the implemented behavior. Server-004: API-key scopes were persisted verbatim with no validation. Added GatewayScopes.All/IsKnown; the CLI parser and dashboard create path now reject unknown scope strings. Server-005: a non-SqlException/InvalidOperationException fault on the initial Galaxy hierarchy load faulted the BackgroundService. ExecuteAsync now catches all non-cancellation exceptions on first load and RefreshCoreAsync broadens its catch so the cache records Stale/Unavailable instead. Server-006: OpenSessionAsync incremented the open-sessions gauge before alarm auto-subscribe; an auto-subscribe failure leaked the gauge. The catch path now calls SessionRemoved() when the gauge was incremented. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
56 lines
1.6 KiB
C#
56 lines
1.6 KiB
C#
using System.Diagnostics;
|
|
|
|
namespace MxGateway.Server.Workers;
|
|
|
|
/// <summary>
|
|
/// <see cref="IRunningProcessInspector"/> backed by <see cref="Process"/>.
|
|
/// </summary>
|
|
public sealed class SystemRunningProcessInspector : IRunningProcessInspector
|
|
{
|
|
/// <inheritdoc />
|
|
public IReadOnlyList<RunningProcessInfo> GetProcessesByName(string processName)
|
|
{
|
|
List<RunningProcessInfo> results = [];
|
|
Process[] processes = Process.GetProcessesByName(processName);
|
|
try
|
|
{
|
|
foreach (Process process in processes)
|
|
{
|
|
results.Add(new RunningProcessInfo(process.Id, TryGetExecutablePath(process)));
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
foreach (Process process in processes)
|
|
{
|
|
process.Dispose();
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Kill(int processId)
|
|
{
|
|
using Process process = Process.GetProcessById(processId);
|
|
process.Kill(entireProcessTree: true);
|
|
}
|
|
|
|
private static string? TryGetExecutablePath(Process process)
|
|
{
|
|
try
|
|
{
|
|
return process.MainModule?.FileName;
|
|
}
|
|
catch (Exception exception) when (exception is InvalidOperationException
|
|
or System.ComponentModel.Win32Exception
|
|
or NotSupportedException)
|
|
{
|
|
// Access to the main module can be denied (e.g. a 64-bit gateway
|
|
// querying a 32-bit worker, or a process owned by another user).
|
|
return null;
|
|
}
|
|
}
|
|
}
|