Jelajahi Sumber

Allow cancel long running async executed commands
Add BreakRequestInfo for possible future support

olegkap_cp 14 tahun lalu
induk
melakukan
148be6f50e

+ 4 - 1
Renci.SshClient/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj

@@ -295,6 +295,9 @@
     <Compile Include="..\Renci.SshNet\Messages\Connection\ChannelOpen\X11ChannelOpenInfo.cs">
       <Link>Messages\Connection\ChannelOpen\X11ChannelOpenInfo.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Messages\Connection\ChannelRequest\BreakRequestInfo.cs">
+      <Link>Messages\Connection\ChannelRequest\BreakRequestInfo.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Messages\Connection\ChannelRequest\ChannelRequestMessage.cs">
       <Link>Messages\Connection\ChannelRequest\ChannelRequestMessage.cs</Link>
     </Compile>
@@ -712,7 +715,7 @@
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <ProjectExtensions>
     <VisualStudio>
-      <UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
+      <UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
     </VisualStudio>
   </ProjectExtensions>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

+ 8 - 2
Renci.SshClient/Renci.SshNet/Channels/Channel.cs

@@ -197,13 +197,19 @@ namespace Renci.SshNet.Channels
         }
 
         /// <summary>
-        /// Closes the channel.
+        /// Sends the SSH_MSG_CHANNEL_EOF message.
         /// </summary>
-        public virtual void Close()
+        internal void SendEof()
         {
             //  Send EOF message first when channel need to be closed
             this.SendMessage(new ChannelEofMessage(this.RemoteChannelNumber));
+        }
 
+        /// <summary>
+        /// Closes the channel.
+        /// </summary>
+        public virtual void Close()
+        {
             //  Send message to close the channel on the server
             this.SendMessage(new ChannelCloseMessage(this.RemoteChannelNumber));
 

+ 8 - 0
Renci.SshClient/Renci.SshNet/Channels/ChannelDirectTcpip.cs

@@ -137,6 +137,14 @@ namespace Renci.SshNet.Channels
                 throw exception;
         }
 
+        public override void Close()
+        {
+            //  Send EOF message first when channel need to be closed
+            this.SendMessage(new ChannelEofMessage(this.RemoteChannelNumber));
+
+            base.Close();
+        }
+
         /// <summary>
         /// Called when channel data is received.
         /// </summary>

+ 8 - 2
Renci.SshClient/Renci.SshNet/Channels/ChannelForwardedTcpip.cs

@@ -106,13 +106,19 @@ namespace Renci.SshNet.Channels
                 }
             }
 
-            this.SendMessage(new ChannelEofMessage(this.RemoteChannelNumber));
-
             this.Close();
         }
 
         partial void OpenSocket(string connectedHost, uint connectedPort);
 
+        public override void Close()
+        {
+            //  Send EOF message first when channel need to be closed
+            this.SendMessage(new ChannelEofMessage(this.RemoteChannelNumber));
+
+            base.Close();
+        }
+
         /// <summary>
         /// Called when channel data is received.
         /// </summary>

+ 16 - 1
Renci.SshClient/Renci.SshNet/Channels/ChannelSession.cs

@@ -189,6 +189,22 @@ namespace Renci.SshNet.Channels
             return this._channelRequestSucces;
         }
 
+        /// <summary>
+        /// Sends the exec request.
+        /// </summary>
+        /// <param name="breakLength">Length of the break.</param>
+        /// <returns>true if request was successful; otherwise false.</returns>
+        public bool SendBreakRequest(uint breakLength)
+        {
+            this._channelRequestResponse.Reset();
+
+            this.SendMessage(new ChannelRequestMessage(this.RemoteChannelNumber, new BreakRequestInfo(breakLength)));
+
+            this._channelRequestResponse.WaitOne();
+
+            return this._channelRequestSucces;
+        }
+
         /// <summary>
         /// Sends the subsystem request.
         /// </summary>
@@ -301,7 +317,6 @@ namespace Renci.SshNet.Channels
             return this._channelRequestSucces;
         }
 
-
         /// <summary>
         /// Called when channel request was successful
         /// </summary>

+ 72 - 0
Renci.SshClient/Renci.SshNet/Messages/Connection/ChannelRequest/BreakRequestInfo.cs

@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Renci.SshNet.Messages.Connection
+{
+    /// <summary>
+    /// Represents "break" type channel request information
+    /// </summary>
+    internal class BreakRequestInfo : RequestInfo
+    {
+        /// <summary>
+        /// Channel request name
+        /// </summary>
+        public const string NAME = "break";
+
+        /// <summary>
+        /// Gets the name of the request.
+        /// </summary>
+        /// <value>
+        /// The name of the request.
+        /// </value>
+        public override string RequestName
+        {
+            get { return BreakRequestInfo.NAME; }
+        }
+
+        /// <summary>
+        /// Gets break length in milliseconds.
+        /// </summary>
+        public UInt32 BreakLength { get; private set; }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ExecRequestInfo"/> class.
+        /// </summary>
+        public BreakRequestInfo()
+        {
+            this.WantReply = true;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ExecRequestInfo"/> class.
+        /// </summary>
+        /// <param name="breakLength">Length of the break.</param>
+        public BreakRequestInfo(UInt32 breakLength)
+            : this()
+        {
+            this.BreakLength = breakLength;
+        }
+
+        /// <summary>
+        /// Called when type specific data need to be loaded.
+        /// </summary>
+        protected override void LoadData()
+        {
+            base.LoadData();
+
+            this.BreakLength = this.ReadUInt32();
+        }
+
+        /// <summary>
+        /// Called when type specific data need to be saved.
+        /// </summary>
+        protected override void SaveData()
+        {
+            base.SaveData();
+
+            this.Write(this.BreakLength);
+        }
+    }
+}

+ 1 - 0
Renci.SshClient/Renci.SshNet/Renci.SshNet.csproj

@@ -154,6 +154,7 @@
     <Compile Include="Messages\Connection\ChannelOpen\ForwardedTcpipChannelInfo.cs" />
     <Compile Include="Messages\Connection\ChannelOpen\SessionChannelOpenInfo.cs" />
     <Compile Include="Messages\Connection\ChannelOpen\X11ChannelOpenInfo.cs" />
+    <Compile Include="Messages\Connection\ChannelRequest\BreakRequestInfo.cs" />
     <Compile Include="Messages\Connection\ChannelRequest\ChannelRequestMessage.cs" />
     <Compile Include="Messages\Connection\ChannelRequest\EndOfWriteRequestInfo.cs" />
     <Compile Include="Messages\Connection\ChannelRequest\EnvironmentVariableRequestInfo.cs" />

+ 6 - 0
Renci.SshClient/Renci.SshNet/Shell.cs

@@ -198,6 +198,8 @@ namespace Renci.SshNet
             //  If channel is open then close it to cause Channel_Closed method to be called
             if (this._channel != null && this._channel.IsOpen)
             {
+                this._channel.SendEof();
+
                 this._channel.Close();
             }
         }
@@ -245,7 +247,11 @@ namespace Renci.SshNet
             }
 
             if (this._channel.IsOpen)
+            {
+                this._channel.SendEof();
+
                 this._channel.Close();
+            }
 
             this._channelClosedWaitHandle.Set();
 

+ 16 - 25
Renci.SshClient/Renci.SshNet/SshCommand.cs

@@ -198,6 +198,7 @@ namespace Renci.SshNet
         public IAsyncResult BeginExecute(string commandText, AsyncCallback callback, object state)
         {
             this.CommandText = commandText;
+
             return BeginExecute(callback, state);
         }
 
@@ -218,7 +219,12 @@ namespace Renci.SshNet
                         //  Make sure that operation completed if not wait for it to finish
                         this.WaitHandle(this._asyncResult.AsyncWaitHandle);
 
-                        this._channel.Close();
+                        if (this._channel.IsOpen)
+                        {
+                            this._channel.SendEof();
+
+                            this._channel.Close();
+                        }
 
                         this._channel = null;
 
@@ -244,30 +250,15 @@ namespace Renci.SshNet
         }
 
         /// <summary>
-        /// Cancels command execution in asynchronous scenarios. CURRENTLY NOT IMPLEMENTED.
+        /// Cancels command execution in asynchronous scenarios. 
         /// </summary>
-        //public void Cancel()
-        //{
-        //    if (this._channel != null && this._channel.IsOpen)
-        //    {
-        //        //this._channel.SendData(Encoding.ASCII.GetBytes("~."));
-        //        this._channel.SendExecRequest("\0x03");
-
-        //        //this._channel.SendSignalRequest("ABRT");
-        //        //this._channel.SendSignalRequest("ALRM");
-        //        //this._channel.SendSignalRequest("FPE");
-        //        //this._channel.SendSignalRequest("HUP");
-        //        //this._channel.SendSignalRequest("ILL");
-        //        //this._channel.SendSignalRequest("INT");
-        //        //this._channel.SendSignalRequest("PIPE");
-        //        //this._channel.SendSignalRequest("QUIT");
-        //        //this._channel.SendSignalRequest("SEGV");
-        //        //this._channel.SendSignalRequest("TERM");
-        //        //this._channel.SendSignalRequest("SEGV");
-        //        //this._channel.SendSignalRequest("USR1");
-        //        //this._channel.SendSignalRequest("USR2");
-        //    }
-        //}
+        public void CancelAsync()
+        {
+            if (this._channel != null && this._channel.IsOpen && this._asyncResult != null)
+            {
+                this._channel.Close();
+            }
+        }
 
         /// <summary>
         /// Executes the specified command text.
@@ -279,6 +270,7 @@ namespace Renci.SshNet
         public string Execute(string commandText)
         {
             this.CommandText = commandText;
+
             return this.Execute();
         }
 
@@ -385,7 +377,6 @@ namespace Renci.SshNet
             if (this.OutputStream != null)
             {
                 this.OutputStream.Write(e.Data, 0, e.Data.Length);
-                //this._outputSteamWriter.Write(this._encoding.GetString(e.Data, 0, e.Data.Length));
                 this.OutputStream.Flush();
             }
 

+ 2 - 0
Renci.SshClient/Renci.SshNet/SubsystemSession.cs

@@ -90,6 +90,8 @@ namespace Renci.SshNet.Sftp
         /// </summary>
         public void Disconnect()
         {
+            this._channel.SendEof();
+
             this._channel.Close();
         }