Skip to content
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

Is it possible to cancel SshCommand? #1023

Closed
MistyKuu opened this issue Oct 28, 2022 · 2 comments · Fixed by #1345
Closed

Is it possible to cancel SshCommand? #1023

MistyKuu opened this issue Oct 28, 2022 · 2 comments · Fixed by #1345

Comments

@MistyKuu
Copy link

There is CancelAsync method but it seems to only cancel connection between client and server. Is there a way to stop the command from running? Similarly to CTRL+C while running in BASH shell.

@lifeincha0s
Copy link

No, using RunCommand, the Execute method blocks until all data is returned from the command, so nothing can be sent to stop the command. You need to use CreateShellStream to kill a running process, which allows writing data while reading data. You can then send Ctrl^C (\x3) to the output stream and kill the process.

@bsmithcompsci
Copy link

I solved this after a long day trying to force this to cancel, by hijacking the SSHCommand via reflection. This is nasty, please contributors/maintainers make a proper fix for this, this hurts production code.

Answer

token.Register(() => {
  FieldInfo? _sessionErrorOccuredWaitHandleField = sshCommand.GetType().GetField("_sessionErrorOccuredWaitHandle", BindingFlags.NonPublic | BindingFlags.Instance);
  if (_sessionErrorOccuredWaitHandleField != null)
  {
      object? _sessionErrorOccuredWaitHandle = _sessionErrorOccuredWaitHandleField.GetValue(sshCommand);
      MethodInfo[] methods = _sessionErrorOccuredWaitHandleField.FieldType.GetMethods();
      MethodInfo? Set = _sessionErrorOccuredWaitHandleField.FieldType.GetMethod("Set");
      if (Set != null)
          Set.Invoke(_sessionErrorOccuredWaitHandle, new object[] { });
  }
   sshCommand.CancelAsync();
   client.Disconnect();
});

Diagnoses

To start, what's the issue. EndExecute() blocks the thread. This as expected to do, but we should be able to have a CancellationToken to cancel a asynchronous task.

Using CancelAsync() just calls a dispose.... not greatly helpful.
image

Okay, let's kill the client Disconnect()... nothing.... Well, let's understand how the Client communicates to the SSHCommand; we can see that the ctor of SSHCommand will append delegates for client callbacks.
image
And we can see that, Session_Disconnect() calls something called _sessionErrorOccuredWaitHandle.Set(), which is interesting.
image
So, I believe there's a bug on the disposal of SSHCommand, when it comes to _isDisposed I cannot find exactly why, this may be the case.

Okay, so lets get our hands a little dirty. Let's touch the AsyncWaitHandle variable on sshCommand var. Well, we cannot simply Close() on the AsyncWaitHandle.... because that also will just do nothing....

But... while diving into SSH.NET code, I found something interesting that I can hijack with some reflection.
DONT TRY AT HOME
image

I can get around this problem today with some nasty hacks, but I hope in the future SSH.NET has a fix for this.
I hope this diagnoses was informational. Thanks, and take care!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants