Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions src/Renci.SshNet/CommandSignals.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
namespace Renci.SshNet
{
/// <summary>
/// The ssh compatible standard POSIX/ANSI signals.
/// </summary>
public static class CommandSignals
{
/// <summary>
/// Hangup (POSIX).
/// </summary>
public const string SIGHUP = "HUP";

/// <summary>
/// Interrupt (ANSI).
/// </summary>
public const string SIGINT = "INT";

/// <summary>
/// Quit (POSIX).
/// </summary>
public const string SIGQUIT = "QUIT";

/// <summary>
/// Illegal instruction (ANSI).
/// </summary>
public const string SIGILL = "ILL";

/// <summary>
/// Abort (ANSI).
/// </summary>
public const string SIGABRT = "ABRT";

/// <summary>
/// Floating-point exception (ANSI).
/// </summary>
public const string SIGFPE = "FPE";

/// <summary>
/// Kill, unblockable (POSIX).
/// </summary>
public const string SIGKILL = "KILL";

/// <summary>
/// User-defined signal 1 (POSIX).
/// </summary>
public const string SIGUSR1 = "USR1";

/// <summary>
/// Segmentation violation (ANSI).
/// </summary>
public const string SIGSEGV = "SEGV";

/// <summary>
/// User-defined signal 2 (POSIX).
/// </summary>
public const string SIGUSR2 = "USR2";

/// <summary>
/// Broken pipe (POSIX).
/// </summary>
public const string SIGPIPE = "PIPE";

/// <summary>
/// Alarm clock (POSIX).
/// </summary>
public const string SIGALRM = "ALRM";

/// <summary>
/// Termination (ANSI).
/// </summary>
public const string SIGTERM = "TERM";
}
}
55 changes: 55 additions & 0 deletions src/Renci.SshNet/SshCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,61 @@ public void CancelAsync(bool forceKill = false, int millisecondsTimeout = 500)
}
}

/// <summary>
/// Tries to send a POSIX/ANSI signal to the remote process executing the command, such as SIGTERM or any of the <see cref="CommandSignals"/>.
/// </summary>
/// <param name="signal">The signal to send. See <see cref="CommandSignals"/> for a standard list of signals.</param>
/// <returns>If the signal was sent.</returns>
public bool TrySendSignal(string signal)
{
if (signal 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(signal);
}
catch (Exception)
{
// Exception can be ignored since we are in a Try method
// Possible exceptions here: InvalidOperationException, SshConnectionException, SshOperationTimeoutException
}

return false;
}

/// <summary>
/// Tries to send a POSIX/ANSI signal to the remote process executing the command, such as SIGTERM or any of the <see cref="CommandSignals"/>.
/// </summary>
/// <param name="signal">The signal to send. See <see cref="CommandSignals"/> for a standard list of signals.</param>
/// <exception cref="ArgumentException">Signal was not a valid CommandSignal.</exception>
/// <exception cref="SshConnectionException">The client is not connected.</exception>
/// <exception cref="SshOperationTimeoutException">The operation timed out.</exception>
/// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the protocol.</exception>
Comment on lines +518 to +519
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// <exception cref="SshOperationTimeoutException">The operation timed out.</exception>
/// <exception cref="InvalidOperationException">The size of the packet exceeds the maximum size defined by the protocol.</exception>

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why exactly would these be removed? They are exceptions from the underlying ssh calls.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I am not seeing where SshOperationTimeoutException would be thrown in this path
  2. You can leave it I guess, and maybe the library is inconsistent in its documentation, but I don't think this is documented (publicly) on any other path where it could happen, and it doesn't seem to have been a problem

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just followed the call stack:

  • SShCommand.SendSignal()
  • ChannelSession.SendSignalRequest()
  • ChannelSession.SendMessage() => missing documentation of exceptions
  • Session.SendMessage() => documented as throwing them
  • Session.WaitOnHandle()
  • Session.WaitOnHandle() => throws SshOperationTimeoutException

I think its better to have a few calls that have all of them rather than all of them are missing ;)

/// <exception cref="InvalidOperationException">Command has not been started.</exception>
public void SendSignal(string signal)
{
if (signal 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(signal);
}

/// <summary>
/// Executes the command specified by <see cref="CommandText"/>.
/// </summary>
Expand Down