While developing dotnet-evergreen,
I needed to figure out a way to cleanly terminate a process in a cross-platform
way without resorting to a (somewhat violent) Process.Kill
.
In Ubuntu/macOS this was trivial and worked nicely by just running kill -s SIGINT [ProcessId]
as a new process, as documented on termination signals.
On Windows, this was far trickier. I read on Ctrl+C an Ctrl+Break signals,
on GenerateConsoleCtrlEvent,
looked at how ASP.NET Core hosting
handles its application lifetime,
found (shocking!) an answer on StackOverflow,
and determined that approach I needed was
FreeConsole ->
AttachConsole ->
GenerateConsoleCtrlEvent then wait for Process.HasExited.
Since this is somewhat involved and I wanted to make it cross-platform and easily reusable, I created the dotnet-stop tool which I can now depend on and invoke from any other dotnet tool. The whole tool fits nicely in a single C# 9 top-level Program.cs.
On Windows, invoking a separate tool for this was unavoidable (as far as I could manage), since I couldn’t re-acquire the console after signaling the external process, which left the calling tool in a broken state (wouldn’t even respond to Ctrl+C at that point). But this was perfectly acceptable for my dotnet-evergreen scenario anyway.
Usage is trivial, with a couple options to tweak how the stopping is done:
dotnet-stop
Sends the SIGINT (Ctrl+C) signal to a process to gracefully stop it.
Usage:
dotnet stop [options] <id>
Arguments:
<id> ID of the process to stop.
Options:
-t, --timeout <timeout> Optional timeout in milliseconds to wait for the process to exit.
-q, --quiet Do not display any output. [default: False]
--version Show version information
-?, -h, --help Show help and usage information
/kzu dev↻d