|
@@ -14,6 +14,8 @@ namespace Renci.SshNet
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
public class Shell : IDisposable
|
|
public class Shell : IDisposable
|
|
|
{
|
|
{
|
|
|
|
|
+ private const int DefaultBufferSize = 1024;
|
|
|
|
|
+
|
|
|
private readonly ISession _session;
|
|
private readonly ISession _session;
|
|
|
private readonly string _terminalName;
|
|
private readonly string _terminalName;
|
|
|
private readonly uint _columns;
|
|
private readonly uint _columns;
|
|
@@ -24,6 +26,7 @@ namespace Renci.SshNet
|
|
|
private readonly Stream _outputStream;
|
|
private readonly Stream _outputStream;
|
|
|
private readonly Stream _extendedOutputStream;
|
|
private readonly Stream _extendedOutputStream;
|
|
|
private readonly int _bufferSize;
|
|
private readonly int _bufferSize;
|
|
|
|
|
+ private readonly bool _noTerminal;
|
|
|
private ManualResetEvent _dataReaderTaskCompleted;
|
|
private ManualResetEvent _dataReaderTaskCompleted;
|
|
|
private IChannelSession _channel;
|
|
private IChannelSession _channel;
|
|
|
private AutoResetEvent _channelClosedWaitHandle;
|
|
private AutoResetEvent _channelClosedWaitHandle;
|
|
@@ -77,24 +80,66 @@ namespace Renci.SshNet
|
|
|
/// <param name="terminalModes">The terminal modes.</param>
|
|
/// <param name="terminalModes">The terminal modes.</param>
|
|
|
/// <param name="bufferSize">Size of the buffer for output stream.</param>
|
|
/// <param name="bufferSize">Size of the buffer for output stream.</param>
|
|
|
internal Shell(ISession session, Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModes, int bufferSize)
|
|
internal Shell(ISession session, Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModes, int bufferSize)
|
|
|
|
|
+ : this(session, input, output, extendedOutput, bufferSize, noTerminal: false)
|
|
|
{
|
|
{
|
|
|
- _session = session;
|
|
|
|
|
- _input = input;
|
|
|
|
|
- _outputStream = output;
|
|
|
|
|
- _extendedOutputStream = extendedOutput;
|
|
|
|
|
_terminalName = terminalName;
|
|
_terminalName = terminalName;
|
|
|
_columns = columns;
|
|
_columns = columns;
|
|
|
_rows = rows;
|
|
_rows = rows;
|
|
|
_width = width;
|
|
_width = width;
|
|
|
_height = height;
|
|
_height = height;
|
|
|
_terminalModes = terminalModes;
|
|
_terminalModes = terminalModes;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Initializes a new instance of the <see cref="Shell"/> class.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="session">The session.</param>
|
|
|
|
|
+ /// <param name="input">The input.</param>
|
|
|
|
|
+ /// <param name="output">The output.</param>
|
|
|
|
|
+ /// <param name="extendedOutput">The extended output.</param>
|
|
|
|
|
+ /// <param name="bufferSize">Size of the buffer for output stream.</param>
|
|
|
|
|
+ internal Shell(ISession session, Stream input, Stream output, Stream extendedOutput, int bufferSize)
|
|
|
|
|
+ : this(session, input, output, extendedOutput, bufferSize, noTerminal: true)
|
|
|
|
|
+ {
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// Initializes a new instance of the <see cref="Shell"/> class.
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ /// <param name="session">The session.</param>
|
|
|
|
|
+ /// <param name="input">The input.</param>
|
|
|
|
|
+ /// <param name="output">The output.</param>
|
|
|
|
|
+ /// <param name="extendedOutput">The extended output.</param>
|
|
|
|
|
+ /// <param name="bufferSize">Size of the buffer for output stream.</param>
|
|
|
|
|
+ /// <param name="noTerminal">Disables pseudo terminal allocation or not.</param>
|
|
|
|
|
+ private Shell(ISession session, Stream input, Stream output, Stream extendedOutput, int bufferSize, bool noTerminal)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (bufferSize == -1)
|
|
|
|
|
+ {
|
|
|
|
|
+ bufferSize = DefaultBufferSize;
|
|
|
|
|
+ }
|
|
|
|
|
+#if NET8_0_OR_GREATER
|
|
|
|
|
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(bufferSize);
|
|
|
|
|
+#else
|
|
|
|
|
+ if (bufferSize <= 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ throw new ArgumentOutOfRangeException(nameof(bufferSize));
|
|
|
|
|
+ }
|
|
|
|
|
+#endif
|
|
|
|
|
+ _session = session;
|
|
|
|
|
+ _input = input;
|
|
|
|
|
+ _outputStream = output;
|
|
|
|
|
+ _extendedOutputStream = extendedOutput;
|
|
|
_bufferSize = bufferSize;
|
|
_bufferSize = bufferSize;
|
|
|
|
|
+ _noTerminal = noTerminal;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// Starts this shell.
|
|
/// Starts this shell.
|
|
|
/// </summary>
|
|
/// </summary>
|
|
|
/// <exception cref="SshException">Shell is started.</exception>
|
|
/// <exception cref="SshException">Shell is started.</exception>
|
|
|
|
|
+ /// <exception cref="SshException">The pseudo-terminal request was not accepted by the server.</exception>
|
|
|
|
|
+ /// <exception cref="SshException">The request to start a shell was not accepted by the server.</exception>
|
|
|
public void Start()
|
|
public void Start()
|
|
|
{
|
|
{
|
|
|
if (IsStarted)
|
|
if (IsStarted)
|
|
@@ -112,8 +157,18 @@ namespace Renci.SshNet
|
|
|
_session.ErrorOccured += Session_ErrorOccured;
|
|
_session.ErrorOccured += Session_ErrorOccured;
|
|
|
|
|
|
|
|
_channel.Open();
|
|
_channel.Open();
|
|
|
- _ = _channel.SendPseudoTerminalRequest(_terminalName, _columns, _rows, _width, _height, _terminalModes);
|
|
|
|
|
- _ = _channel.SendShellRequest();
|
|
|
|
|
|
|
+ if (!_noTerminal)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!_channel.SendPseudoTerminalRequest(_terminalName, _columns, _rows, _width, _height, _terminalModes))
|
|
|
|
|
+ {
|
|
|
|
|
+ throw new SshException("The pseudo-terminal request was not accepted by the server. Consult the server log for more information.");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!_channel.SendShellRequest())
|
|
|
|
|
+ {
|
|
|
|
|
+ throw new SshException("The request to start a shell was not accepted by the server. Consult the server log for more information.");
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
_channelClosedWaitHandle = new AutoResetEvent(initialState: false);
|
|
_channelClosedWaitHandle = new AutoResetEvent(initialState: false);
|
|
|
|
|
|