diff --git a/src/Renci.SshNet/CommandSignal.cs b/src/Renci.SshNet/CommandSignal.cs
new file mode 100644
index 000000000..5d37c24c6
--- /dev/null
+++ b/src/Renci.SshNet/CommandSignal.cs
@@ -0,0 +1,74 @@
+namespace Renci.SshNet
+{
+ ///
+ /// The ssh compatible POSIX/ANSI signals with their libc compatible values.
+ ///
+#pragma warning disable CA1720 // Identifier contains type name
+ public enum CommandSignal
+ {
+ ///
+ /// Hangup (POSIX).
+ ///
+ HUP = 1,
+
+ ///
+ /// Interrupt (ANSI).
+ ///
+ INT = 2,
+
+ ///
+ /// Quit (POSIX).
+ ///
+ QUIT = 3,
+
+ ///
+ /// Illegal instruction (ANSI).
+ ///
+ ILL = 4,
+
+ ///
+ /// Abort (ANSI).
+ ///
+ ABRT = 6,
+
+ ///
+ /// Floating-point exception (ANSI).
+ ///
+ FPE = 8,
+
+ ///
+ /// Kill, unblockable (POSIX).
+ ///
+ KILL = 9,
+
+ ///
+ /// User-defined signal 1 (POSIX).
+ ///
+ USR1 = 10,
+
+ ///
+ /// Segmentation violation (ANSI).
+ ///
+ SEGV = 11,
+
+ ///
+ /// User-defined signal 2 (POSIX).
+ ///
+ USR2 = 12,
+
+ ///
+ /// Broken pipe (POSIX).
+ ///
+ PIPE = 13,
+
+ ///
+ /// Alarm clock (POSIX).
+ ///
+ ALRM = 14,
+
+ ///
+ /// Termination (ANSI).
+ ///
+ TERM = 15,
+ }
+}
diff --git a/src/Renci.SshNet/SshCommand.cs b/src/Renci.SshNet/SshCommand.cs
index ce1042244..48864c5b6 100644
--- a/src/Renci.SshNet/SshCommand.cs
+++ b/src/Renci.SshNet/SshCommand.cs
@@ -478,6 +478,74 @@ public void CancelAsync(bool forceKill = false, int millisecondsTimeout = 500)
}
}
+ private static string? GetSignalName(CommandSignal signal)
+ {
+#if NETCOREAPP
+ return Enum.GetName(signal);
+#else
+
+ // Boxes signal, but Enum.GetName does not have a non-boxing overload prior to .NET Core.
+ return Enum.GetName(typeof(CommandSignal), signal);
+#endif
+ }
+
+ ///
+ /// Tries to send a POSIX/ANSI signal to the remote process executing the command, such as SIGINT or SIGTERM.
+ ///
+ /// The signal to send.
+ /// If the signal was sent.
+ public bool TrySendSignal(CommandSignal signal)
+ {
+ var signalName = GetSignalName(signal);
+ if (signalName is null)
+ {
+ return false;
+ }
+
+ if (_tcs is null || _tcs.Task.IsCompleted || _channel?.IsOpen != true)
+ {
+ return false;
+ }
+
+ try
+ {
+ // Try to send the cancellation signal.
+ return _channel.SendSignalRequest(signalName);
+ }
+ catch (Exception)
+ {
+ // Exception can be ignored since we are in a Try method
+ // Possible exceptions here: InvalidOperationException, SshConnectionException, SshOperationTimeoutException
+ }
+
+ return false;
+ }
+
+ ///
+ /// Tries to send a POSIX/ANSI signal to the remote process executing the command, such as SIGINT or SIGTERM.
+ ///
+ /// The signal to send.
+ /// Signal was not a valid CommandSignal.
+ /// The client is not connected.
+ /// The operation timed out.
+ /// The size of the packet exceeds the maximum size defined by the protocol.
+ /// Command has not been started.
+ public void SendSignal(CommandSignal signal)
+ {
+ var signalName = GetSignalName(signal);
+ if (signalName is null)
+ {
+ throw new ArgumentException("Signal was not a valid CommandSignal.");
+ }
+
+ if (_tcs is null || _tcs.Task.IsCompleted || _channel?.IsOpen != true)
+ {
+ throw new InvalidOperationException("Command has not been started.");
+ }
+
+ _ = _channel.SendSignalRequest(signalName);
+ }
+
///
/// Executes the command specified by .
///