fix(config-writer): SIGHUP via libc kill(2) instead of /bin/kill

The aspnet:9.0 runtime base image doesn't ship procps, so
Process.Start(\"kill\", \"-HUP <pid>\") failed with ENOENT — the first
reconcile after boot wrote the new nats.conf to disk, then threw when
signalling, and subsequent reconciles saw the on-disk text already
matched the target and skipped the retry. Net effect: nats-server kept
running with whatever config it parsed at startup, and configuration
changes never took effect at runtime.

Switch to a direct P/Invoke to libc kill(2) so we don't depend on any
external binary.
This commit is contained in:
EugeneTes
2026-08-27 15:46:41 +00:00
parent ddaa43d924
commit b17af56d14

View File

@@ -1,5 +1,5 @@
using System.Diagnostics;
using System.Net.Http;
using System.Runtime.InteropServices;
using Microsoft.Extensions.Logging;
namespace VolcanicCore.Core.NatsConfig;
@@ -69,27 +69,26 @@ public sealed class ConfigWriter : IConfigWriter
return true;
}
// POSIX kill(2). Signalling via libc means we don't depend on /bin/kill
// being present — the aspnet:9.0 base image ships without procps, so
// Process.Start("kill", ...) fails at runtime with ENOENT and the config
// reload silently never happens.
[DllImport("libc", SetLastError = true, EntryPoint = "kill")]
private static extern int LibcKill(int pid, int sig);
private const int SIGHUP = 1;
private static void SendSighup(int processId)
{
// Cross-platform: use kill(1) on Linux; on Windows nats-server doesn't support hot reload.
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{
using var proc = new Process
{
StartInfo = new ProcessStartInfo("kill", $"-HUP {processId}")
{
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
},
};
proc.Start();
proc.WaitForExit(2000);
}
else
{
if (!OperatingSystem.IsLinux() && !OperatingSystem.IsMacOS())
throw new PlatformNotSupportedException(
"nats-server hot reload requires SIGHUP; Windows is not supported.");
var rc = LibcKill(processId, SIGHUP);
if (rc != 0)
{
var errno = Marshal.GetLastPInvokeError();
throw new InvalidOperationException(
$"kill(pid={processId}, SIGHUP) failed with errno={errno}");
}
}