Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 5.6k
Implement SafeProcessHandle.Kill and Signal#126313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
4d2bc4a5d3f5797c03c8c0ea3ac4b9c3c3a3b8fa5108a597275f7da81815c43bddd72bd8532425bdc146d603d31493466cFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,21 +1,13 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| using System; | ||
| using System.Runtime.InteropServices; | ||
| internal static partial class Interop | ||
| { | ||
| internal static partial class Sys | ||
| { | ||
| internal enum Signals : int | ||
| { | ||
| None = 0, | ||
| SIGKILL = 9, | ||
| SIGSTOP = 19 | ||
| } | ||
| [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_Kill", SetLastError = true)] | ||
| internal static partial int Kill(int pid, Signals signal); | ||
| internal static partial int Kill(int pid, int signal); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -2,6 +2,7 @@ | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| using System; | ||
| using System.ComponentModel; | ||
| using System.Diagnostics; | ||
| using System.Runtime.InteropServices; | ||
| using System.Security; | ||
| @@ -330,5 +331,31 @@ Interop.Shell32.SE_ERR_DDEBUSY or | ||
| } | ||
| private int GetProcessIdCore() => Interop.Kernel32.GetProcessId(this); | ||
| private bool SignalCore(PosixSignal signal) | ||
| { | ||
| // On Windows, only SIGKILL is supported, mapped to TerminateProcess. | ||
| if (signal != PosixSignal.SIGKILL) | ||
| { | ||
| throw new PlatformNotSupportedException(); | ||
adamsitnik marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| if (!Interop.Kernel32.TerminateProcess(this, -1)) | ||
adamsitnik marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| { | ||
adamsitnik marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| int errorCode = Marshal.GetLastWin32Error(); | ||
| // Return false if the process has already exited. | ||
| if (errorCode == Interop.Errors.ERROR_ACCESS_DENIED && | ||
| Interop.Kernel32.GetExitCodeProcess(this, out int exitCode) && | ||
| exitCode != Interop.Kernel32.HandleOptions.STILL_ACTIVE) | ||
| { | ||
| return false; | ||
| } | ||
| throw new Win32Exception(errorCode); | ||
| } | ||
| return true; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -11,9 +11,11 @@ | ||
| ===========================================================*/ | ||
| using System; | ||
| using System.ComponentModel; | ||
| using System.Diagnostics; | ||
| using System.Runtime.Serialization; | ||
| using System.Runtime.Versioning; | ||
| using System.Runtime.InteropServices; | ||
| namespace Microsoft.Win32.SafeHandles | ||
| { | ||
| @@ -123,6 +125,48 @@ public static SafeProcessHandle Start(ProcessStartInfo startInfo) | ||
| return StartCore(startInfo, childInputHandle, childOutputHandle, childErrorHandle); | ||
| } | ||
| /// <summary> | ||
| /// Sends a request to the OS to terminate the process. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// This method does not throw if the process has already exited. | ||
| /// On Windows, the handle must have <c>PROCESS_TERMINATE</c> access. | ||
jkotas marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| /// </remarks> | ||
| /// <exception cref="InvalidOperationException">The handle is invalid.</exception> | ||
| /// <exception cref="Win32Exception">The process could not be terminated.</exception> | ||
| [UnsupportedOSPlatform("ios")] | ||
| [UnsupportedOSPlatform("tvos")] | ||
| [SupportedOSPlatform("maccatalyst")] | ||
adamsitnik marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| public void Kill() | ||
| { | ||
| Validate(); | ||
| SignalCore(PosixSignal.SIGKILL); | ||
jkotas marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| /// <summary> | ||
| /// Sends a signal to the process. | ||
| /// </summary> | ||
| /// <param name="signal">The signal to send.</param> | ||
| /// <returns> | ||
| /// <see langword="true"/> if the signal was sent successfully; | ||
| /// <see langword="false"/> if the process has already exited (or never existed) and the signal was not delivered. | ||
| /// </returns> | ||
| /// <remarks> | ||
| /// On Windows, only <see cref="PosixSignal.SIGKILL"/> is supported and is mapped to <see cref="Kill"/>. | ||
| /// On Windows, the handle must have <c>PROCESS_TERMINATE</c> access. | ||
| /// </remarks> | ||
| /// <exception cref="InvalidOperationException">The handle is invalid.</exception> | ||
| /// <exception cref="PlatformNotSupportedException">The specified signal is not supported on this platform.</exception> | ||
| /// <exception cref="Win32Exception">The signal could not be sent.</exception> | ||
| [UnsupportedOSPlatform("ios")] | ||
| [UnsupportedOSPlatform("tvos")] | ||
| [SupportedOSPlatform("maccatalyst")] | ||
| public bool Signal(PosixSignal signal) | ||
| { | ||
| Validate(); | ||
| return SignalCore(signal); | ||
| } | ||
| private void Validate() | ||
| { | ||
| if (IsInvalid) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -909,18 +909,12 @@ public async Task Kill_ExitedNonChildProcess_DoesNotThrow(bool killTree) | ||
| using (Process nonChildProcess = CreateNonChildProcess()) | ||
| { | ||
| // Kill the process. | ||
| int rv = kill(nonChildProcess.Id, SIGKILL); | ||
| Assert.Equal(0, rv); | ||
| Assert.True(nonChildProcess.SafeHandle.Signal(PosixSignal.SIGKILL)); | ||
| // Wait until the process is reaped. | ||
| while (rv == 0) | ||
| while (!nonChildProcess.HasExited) | ||
| { | ||
| rv = kill(nonChildProcess.Id, 0); | ||
| if (rv == 0) | ||
| { | ||
| // process still exists, wait some time. | ||
| await Task.Delay(100); | ||
| } | ||
| await Task.Delay(100); | ||
| DateTime now = DateTime.UtcNow; | ||
| if (start.Ticks + (Helpers.PassingTestTimeoutMilliseconds * 10_000) <= now.Ticks) | ||
| @@ -1024,11 +1018,6 @@ private static unsafe HashSet<uint> GetGroups() | ||
| [DllImport("libc")] | ||
| private static extern unsafe int setgroups(int length, uint* groups); | ||
| private const int SIGKILL = 9; | ||
| [DllImport("libc", SetLastError = true)] | ||
| private static extern int kill(int pid, int sig); | ||
| [DllImport("libc", SetLastError = true)] | ||
| private static extern int open(string pathname, int flags); | ||
| @@ -1062,14 +1051,7 @@ private static string StartAndReadToEnd(string filename, string[] arguments) | ||
| } | ||
| } | ||
| private static void SendSignal(PosixSignal signal, int processId) | ||
| { | ||
| int result = kill(processId, Interop.Sys.GetPlatformSignalNumber(signal)); | ||
| if (result != 0) | ||
| { | ||
| throw new Win32Exception(Marshal.GetLastWin32Error(), $"Failed to send signal {signal} to process {processId}"); | ||
| } | ||
| } | ||
| private static void SendSignal(PosixSignal signal, Process process) => Assert.True(process.SafeHandle.Signal(signal)); | ||
| private static unsafe void ReEnableCtrlCHandlerIfNeeded(PosixSignal signal) { } | ||
| @@ -1115,7 +1097,7 @@ public void ChildProcess_WithParentSignalHandler_CanReceiveSignals() | ||
| AssertRemoteProcessStandardOutputLine(childHandle, ChildReadyMessage, WaitInMS); | ||
| // Send SIGCONT to the child process | ||
| SendSignal(PosixSignal.SIGCONT, childHandle.Process.Id); | ||
adamsitnik marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| Assert.True(childHandle.Process.SafeHandle.Signal(PosixSignal.SIGCONT)); | ||
| Assert.True(childHandle.Process.WaitForExit(WaitInMS)); | ||
| Assert.Equal(RemotelyInvokable.SuccessExitCode, childHandle.Process.ExitCode); | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.