<PackageReference Include="SSH.NET" Version="2024.1.0" />

ShellStream

public class ShellStream : Stream
Contains operation for working with SSH Shell.
using Renci.SshNet.Abstractions; using Renci.SshNet.Channels; using Renci.SshNet.Common; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace Renci.SshNet { [System.Runtime.CompilerServices.NullableContext(1)] [System.Runtime.CompilerServices.Nullable(0)] public class ShellStream : Stream { private const int DefaultBufferSize = 1024; private readonly ISession _session; private readonly Encoding _encoding; private readonly IChannelSession _channel; private readonly byte[] _carriageReturnBytes; private readonly byte[] _lineFeedBytes; private readonly object _sync = new object(); private readonly byte[] _writeBuffer; private readonly bool _noTerminal; private int _writeLength; private byte[] _readBuffer; private int _readHead; private int _readTail; private bool _disposed; public bool DataAvailable { get { lock (_sync) { return _readTail != _readHead; } } } public override bool CanRead => true; public override bool CanSeek => false; public override bool CanWrite => !_disposed; public override long Length { get { lock (_sync) { return _readTail - _readHead; } } } public override long Position { get { return 0; } set { throw new NotSupportedException(); } } [System.Runtime.CompilerServices.Nullable(new byte[] { 2, 1 })] [method: System.Runtime.CompilerServices.Nullable(new byte[] { 2, 1 })] [field: System.Runtime.CompilerServices.Nullable(new byte[] { 2, 1 })] public event EventHandler<ShellDataEventArgs> DataReceived; [System.Runtime.CompilerServices.Nullable(new byte[] { 2, 1 })] [method: System.Runtime.CompilerServices.Nullable(new byte[] { 2, 1 })] [field: System.Runtime.CompilerServices.Nullable(new byte[] { 2, 1 })] public event EventHandler<ExceptionEventArgs> ErrorOccurred; [System.Runtime.CompilerServices.Nullable(new byte[] { 2, 1 })] [method: System.Runtime.CompilerServices.Nullable(new byte[] { 2, 1 })] [field: System.Runtime.CompilerServices.Nullable(new byte[] { 2, 1 })] public event EventHandler<EventArgs> Closed; [Conditional("DEBUG")] private void AssertValid() { } internal ShellStream(ISession session, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModeValues, int bufferSize) : this(session, bufferSize, false) { try { _channel.Open(); if (!_channel.SendPseudoTerminalRequest(terminalName, columns, rows, width, height, terminalModeValues)) 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."); } catch { Dispose(); throw; } } internal ShellStream(ISession session, int bufferSize) : this(session, bufferSize, true) { try { _channel.Open(); 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."); } catch { Dispose(); throw; } } private ShellStream(ISession session, int bufferSize, bool noTerminal) { if (bufferSize == -1) bufferSize = 1024; if (bufferSize <= 0) throw new ArgumentOutOfRangeException("bufferSize"); _encoding = session.ConnectionInfo.Encoding; _session = session; _carriageReturnBytes = _encoding.GetBytes("\r"); _lineFeedBytes = _encoding.GetBytes("\n"); _channel = _session.CreateChannelSession(); _channel.DataReceived += Channel_DataReceived; _channel.Closed += Channel_Closed; _session.Disconnected += Session_Disconnected; _session.ErrorOccured += Session_ErrorOccured; _readBuffer = new byte[bufferSize]; _writeBuffer = new byte[bufferSize]; _noTerminal = noTerminal; } public override void Flush() { ThrowIfDisposed(); if (_writeLength > 0) { _channel.SendData(_writeBuffer, 0, _writeLength); _writeLength = 0; } } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } public void Expect(params ExpectAction[] expectActions) { Expect(Timeout.InfiniteTimeSpan, expectActions); } public void Expect(TimeSpan timeout, params ExpectAction[] expectActions) { ExpectRegex(timeout, -1, expectActions); } public void Expect(TimeSpan timeout, int lookback, params ExpectAction[] expectActions) { ExpectRegex(timeout, lookback, expectActions); } [return: System.Runtime.CompilerServices.Nullable(2)] public string Expect(string text) { return Expect(text, Timeout.InfiniteTimeSpan, -1); } [return: System.Runtime.CompilerServices.Nullable(2)] public string Expect(string text, TimeSpan timeout, int lookback = -1) { ValidateTimeout(timeout); ValidateLookback(lookback); DateTime d = DateTime.Now.Add(timeout); byte[] bytes = _encoding.GetBytes(text); lock (_sync) { while (true) { int num = (lookback == -1) ? _readHead : Math.Max(_readTail - lookback, _readHead); int num2 = _readBuffer.AsSpan(num, _readTail - num).IndexOf(bytes); if (num2 >= 0) { string string = _encoding.GetString(_readBuffer, _readHead, num - _readHead + num2 + bytes.Length); _readHead = num + num2 + bytes.Length; return string; } if (_disposed) return null; if (timeout == Timeout.InfiniteTimeSpan) Monitor.Wait(_sync); else { TimeSpan timeSpan = d - DateTime.Now; if (timeSpan < TimeSpan.Zero || !Monitor.Wait(_sync, timeSpan)) break; } } return null; } } [return: System.Runtime.CompilerServices.Nullable(2)] public string Expect(Regex regex) { return Expect(regex, Timeout.InfiniteTimeSpan, -1); } [return: System.Runtime.CompilerServices.Nullable(2)] public string Expect(Regex regex, TimeSpan timeout, int lookback = -1) { return ExpectRegex(timeout, lookback, new ExpectAction[1] { new ExpectAction(regex, delegate { }) }); } [return: System.Runtime.CompilerServices.Nullable(2)] private string ExpectRegex(TimeSpan timeout, int lookback, ExpectAction[] expectActions) { ValidateTimeout(timeout); ValidateLookback(lookback); DateTime d = DateTime.Now.Add(timeout); lock (_sync) { while (true) { string string = _encoding.GetString(_readBuffer, _readHead, _readTail - _readHead); int startat = (lookback != -1) ? Math.Max(string.Length - lookback, 0) : 0; foreach (ExpectAction expectAction in expectActions) { Match match = expectAction.Expect.Match(string, startat); if (match.Success) { string text = string.Substring(0, match.Index + match.Length); _readHead += _encoding.GetByteCount(text); expectAction.Action(text); return text; } } if (_disposed) return null; if (timeout == Timeout.InfiniteTimeSpan) Monitor.Wait(_sync); else { TimeSpan timeSpan = d - DateTime.Now; if (timeSpan < TimeSpan.Zero || !Monitor.Wait(_sync, timeSpan)) break; } } return null; } } public IAsyncResult BeginExpect(params ExpectAction[] expectActions) { return BeginExpect(Timeout.InfiniteTimeSpan, null, null, expectActions); } public IAsyncResult BeginExpect([System.Runtime.CompilerServices.Nullable(2)] AsyncCallback callback, params ExpectAction[] expectActions) { return BeginExpect(Timeout.InfiniteTimeSpan, callback, null, expectActions); } public IAsyncResult BeginExpect([System.Runtime.CompilerServices.Nullable(2)] AsyncCallback callback, [System.Runtime.CompilerServices.Nullable(2)] object state, params ExpectAction[] expectActions) { return BeginExpect(Timeout.InfiniteTimeSpan, callback, state, expectActions); } public IAsyncResult BeginExpect(TimeSpan timeout, [System.Runtime.CompilerServices.Nullable(2)] AsyncCallback callback, [System.Runtime.CompilerServices.Nullable(2)] object state, params ExpectAction[] expectActions) { return BeginExpect(timeout, -1, callback, state, expectActions); } public IAsyncResult BeginExpect(TimeSpan timeout, int lookback, [System.Runtime.CompilerServices.Nullable(2)] AsyncCallback callback, [System.Runtime.CompilerServices.Nullable(2)] object state, params ExpectAction[] expectActions) { return TaskToAsyncResult.Begin(Task.Run(() => ExpectRegex(timeout, lookback, expectActions)), callback, state); } [return: System.Runtime.CompilerServices.Nullable(2)] public string EndExpect(IAsyncResult asyncResult) { return TaskToAsyncResult.End<string>(asyncResult); } [System.Runtime.CompilerServices.NullableContext(2)] public string ReadLine() { return ReadLine(Timeout.InfiniteTimeSpan); } [System.Runtime.CompilerServices.NullableContext(2)] public string ReadLine(TimeSpan timeout) { ValidateTimeout(timeout); DateTime d = DateTime.Now.Add(timeout); lock (_sync) { while (true) { int num = _readBuffer.AsSpan(_readHead, _readTail - _readHead).IndexOf(_carriageReturnBytes); if (num >= 0) { int num2 = (num + _carriageReturnBytes.Length + _lineFeedBytes.Length <= _readTail - _readHead) ? _readBuffer.AsSpan(_readHead, num + _carriageReturnBytes.Length + _lineFeedBytes.Length).IndexOf(_lineFeedBytes) : _readBuffer.AsSpan(_readHead, num).IndexOf(_lineFeedBytes); if (num2 >= 0 && num2 < num) { string string = _encoding.GetString(_readBuffer, _readHead, num2); _readHead += num2 + _lineFeedBytes.Length; return string; } if (num2 != num + _carriageReturnBytes.Length) { string string2 = _encoding.GetString(_readBuffer, _readHead, num); _readHead += num + _carriageReturnBytes.Length; return string2; } string string3 = _encoding.GetString(_readBuffer, _readHead, num); _readHead += num + _carriageReturnBytes.Length + _lineFeedBytes.Length; return string3; } int num3 = _readBuffer.AsSpan(_readHead, _readTail - _readHead).IndexOf(_lineFeedBytes); if (num3 >= 0) { string string4 = _encoding.GetString(_readBuffer, _readHead, num3); _readHead += num3 + _lineFeedBytes.Length; return string4; } if (_disposed) { string result = (_readHead == _readTail) ? null : _encoding.GetString(_readBuffer, _readHead, _readTail - _readHead); _readHead = (_readTail = 0); return result; } if (timeout == Timeout.InfiniteTimeSpan) Monitor.Wait(_sync); else { TimeSpan timeSpan = d - DateTime.Now; if (timeSpan < TimeSpan.Zero || !Monitor.Wait(_sync, timeSpan)) break; } } return null; } } private static void ValidateTimeout(TimeSpan timeout) { if (timeout < TimeSpan.Zero && timeout != Timeout.InfiniteTimeSpan) throw new ArgumentOutOfRangeException("timeout", "Value must be non-negative or equal to -1 millisecond (for infinite timeout)"); } private static void ValidateLookback(int lookback) { if (lookback <= 0 && lookback != -1) throw new ArgumentOutOfRangeException("lookback", "Value must be positive or equal to -1 (for no window)"); } private void ThrowIfDisposed() { if (_disposed) throw new ObjectDisposedException(GetType().FullName); } public string Read() { lock (_sync) { string string = _encoding.GetString(_readBuffer, _readHead, _readTail - _readHead); _readHead = (_readTail = 0); return string; } } public override int Read(byte[] buffer, int offset, int count) { lock (_sync) { while (_readHead == _readTail && !_disposed) { Monitor.Wait(_sync); } int num = Math.Min(count, _readTail - _readHead); Buffer.BlockCopy(_readBuffer, _readHead, buffer, offset, num); _readHead += num; return num; } } [System.Runtime.CompilerServices.NullableContext(2)] public void Write(string text) { if (text != null) { byte[] bytes = _encoding.GetBytes(text); Write(bytes, 0, bytes.Length); Flush(); } } public override void Write(byte[] buffer, int offset, int count) { ThrowIfDisposed(); while (count > 0) { if (_writeLength == _writeBuffer.Length) Flush(); int num = Math.Min(count, _writeBuffer.Length - _writeLength); Buffer.BlockCopy(buffer, offset, _writeBuffer, _writeLength, num); offset += num; count -= num; _writeLength += num; } } public void WriteLine(string line) { Write(line + (_noTerminal ? "\n" : "\r")); } protected override void Dispose(bool disposing) { if (!disposing) base.Dispose(disposing); else { lock (_sync) { if (_disposed) return; _disposed = true; _session.Disconnected -= Session_Disconnected; _session.ErrorOccured -= Session_ErrorOccured; _channel.DataReceived -= Channel_DataReceived; _channel.Closed -= Channel_Closed; _channel.Dispose(); Monitor.PulseAll(_sync); } base.Dispose(disposing); } } private void Session_ErrorOccured([System.Runtime.CompilerServices.Nullable(2)] object sender, ExceptionEventArgs e) { this.ErrorOccurred?.Invoke(this, e); } private void Session_Disconnected([System.Runtime.CompilerServices.Nullable(2)] object sender, EventArgs e) { Dispose(); } private void Channel_Closed([System.Runtime.CompilerServices.Nullable(2)] object sender, ChannelEventArgs e) { Dispose(); if (this.Closed != null) ThreadAbstraction.ExecuteThread(delegate { this.Closed?.Invoke(this, EventArgs.Empty); }); } private void Channel_DataReceived([System.Runtime.CompilerServices.Nullable(2)] object sender, ChannelDataEventArgs e) { lock (_sync) { if (_readBuffer.Length - _readTail >= e.Data.Length) { Buffer.BlockCopy(e.Data, 0, _readBuffer, _readTail, e.Data.Length); _readTail += e.Data.Length; } else { int num = _readTail - _readHead + e.Data.Length; if (num <= _readBuffer.Length) Buffer.BlockCopy(_readBuffer, _readHead, _readBuffer, 0, _readTail - _readHead); else { byte[] array = new byte[Math.Max(num, _readBuffer.Length * 2)]; Buffer.BlockCopy(_readBuffer, _readHead, array, 0, _readTail - _readHead); _readBuffer = array; } Buffer.BlockCopy(e.Data, 0, _readBuffer, _readTail - _readHead, e.Data.Length); _readHead = 0; _readTail = num; } Monitor.PulseAll(_sync); } this.DataReceived?.Invoke(this, new ShellDataEventArgs(e.Data)); } } }