# < Summary

Information

Class: | Renci.SshNet.Shell
Assembly: | Renci.SshNet
File(s): | \home\appveyor\projects\ssh-net\src\Renci.SshNet\Shell.cs

Line coverage
61%

Covered lines: | 92
Uncovered lines: | 57
Coverable lines: | 149
Total lines: | 307
Line coverage: | 61.7%

Branch coverage
34%

Covered branches: | 11
Total branches: | 32
Branch coverage: | 34.3%

Method coverage

Feature is only available for sponsors

Upgrade to PRO version

# Metrics

Method | Branch coverage | Cyclomatic complexity | Line coverage
--- | --- | --- | ---
get_IsStarted() | 100% | 1 | 100%
.ctor(...) | 100% | 1 | 100%
Start() | 50% | 6 | 85.71%
Stop() | 50% | 4 | 66.66%
Session_ErrorOccured(...) | 100% | 1 | 0%
RaiseError(...) | 0% | 2 | 0%
Session_Disconnected(...) | 100% | 1 | 0%
Channel_ExtendedDataReceived(...) | 0% | 2 | 0%
Channel_DataReceived(...) | 50% | 2 | 100%
Channel_Closed(...) | 50% | 4 | 72.72%
UnsubscribeFromSessionEvents(...) | 50% | 2 | 71.42%
Dispose() | 100% | 1 | 0%
Dispose(...) | 20% | 10 | 14.28%
Finalize() | 100% | 1 | 100%

# File(s)

## \home\appveyor\projects\ssh-net\src\Renci.SshNet\Shell.cs

| # | Line |  | Line coverage
--- | --- | --- | --- | ---
|  | `1` |  | `using System;`
|  | `2` |  | `using System.Collections.Generic;`
|  | `3` |  | `using System.IO;`
|  | `4` |  | `using System.Threading;`
|  | `5` |  | ``
|  | `6` |  | `using Renci.SshNet.Abstractions;`
|  | `7` |  | `using Renci.SshNet.Channels;`
|  | `8` |  | `using Renci.SshNet.Common;`
|  | `9` |  | ``
|  | `10` |  | `namespace Renci.SshNet`
|  | `11` |  | `{`
|  | `12` |  | `/// <summary>`
|  | `13` |  | `/// Represents instance of the SSH shell object.`
|  | `14` |  | `/// </summary>`
|  | `15` |  | `public class Shell : IDisposable`
|  | `16` |  | `{`
|  | `17` |  | `private readonly ISession _session;`
|  | `18` |  | `private readonly string _terminalName;`
|  | `19` |  | `private readonly uint _columns;`
|  | `20` |  | `private readonly uint _rows;`
|  | `21` |  | `private readonly uint _width;`
|  | `22` |  | `private readonly uint _height;`
|  | `23` |  | `private readonly IDictionary<TerminalModes, uint> _terminalModes;`
|  | `24` |  | `private readonly Stream _outputStream;`
|  | `25` |  | `private readonly Stream _extendedOutputStream;`
|  | `26` |  | `private readonly int _bufferSize;`
|  | `27` |  | `private ManualResetEvent _dataReaderTaskCompleted;`
|  | `28` |  | `private IChannelSession _channel;`
|  | `29` |  | `private AutoResetEvent _channelClosedWaitHandle;`
|  | `30` |  | `private Stream _input;`
|  | `31` |  | ``
|  | `32` |  | `/// <summary>`
|  | `33` |  | `/// Gets a value indicating whether this shell is started.`
|  | `34` |  | `/// </summary>`
|  | `35` |  | `/// <value>`
|  | `36` |  | `/// <see langword="true"/> if started is started; otherwise, <see langword="false"/>.`
|  | `37` |  | `/// </value>`
| 3 | `38` |  | `public bool IsStarted { get; private set; }`
|  | `39` |  | ``
|  | `40` |  | `/// <summary>`
|  | `41` |  | `/// Occurs when shell is starting.`
|  | `42` |  | `/// </summary>`
|  | `43` |  | `public event EventHandler<EventArgs> Starting;`
|  | `44` |  | ``
|  | `45` |  | `/// <summary>`
|  | `46` |  | `/// Occurs when shell is started.`
|  | `47` |  | `/// </summary>`
|  | `48` |  | `public event EventHandler<EventArgs> Started;`
|  | `49` |  | ``
|  | `50` |  | `/// <summary>`
|  | `51` |  | `/// Occurs when shell is stopping.`
|  | `52` |  | `/// </summary>`
|  | `53` |  | `public event EventHandler<EventArgs> Stopping;`
|  | `54` |  | ``
|  | `55` |  | `/// <summary>`
|  | `56` |  | `/// Occurs when shell is stopped.`
|  | `57` |  | `/// </summary>`
|  | `58` |  | `public event EventHandler<EventArgs> Stopped;`
|  | `59` |  | ``
|  | `60` |  | `/// <summary>`
|  | `61` |  | `/// Occurs when an error occurred.`
|  | `62` |  | `/// </summary>`
|  | `63` |  | `public event EventHandler<ExceptionEventArgs> ErrorOccurred;`
|  | `64` |  | ``
|  | `65` |  | `/// <summary>`
|  | `66` |  | `/// Initializes a new instance of the <see cref="Shell"/> class.`
|  | `67` |  | `/// </summary>`
|  | `68` |  | `/// <param name="session">The session.</param>`
|  | `69` |  | `/// <param name="input">The input.</param>`
|  | `70` |  | `/// <param name="output">The output.</param>`
|  | `71` |  | `/// <param name="extendedOutput">The extended output.</param>`
|  | `72` |  | `/// <param name="terminalName">Name of the terminal.</param>`
|  | `73` |  | `/// <param name="columns">The columns.</param>`
|  | `74` |  | `/// <param name="rows">The rows.</param>`
|  | `75` |  | `/// <param name="width">The width.</param>`
|  | `76` |  | `/// <param name="height">The height.</param>`
|  | `77` |  | `/// <param name="terminalModes">The terminal modes.</param>`
|  | `78` |  | `/// <param name="bufferSize">Size of the buffer for output stream.</param>`
| 1 | `79` |  | `internal Shell(ISession session, Stream input, Stream output, Stream extendedOutput, string terminalName, uint c`
| 1 | `80` |  | `{`
| 1 | `81` |  | `_session = session;`
| 1 | `82` |  | `_input = input;`
| 1 | `83` |  | `_outputStream = output;`
| 1 | `84` |  | `_extendedOutputStream = extendedOutput;`
| 1 | `85` |  | `_terminalName = terminalName;`
| 1 | `86` |  | `_columns = columns;`
| 1 | `87` |  | `_rows = rows;`
| 1 | `88` |  | `_width = width;`
| 1 | `89` |  | `_height = height;`
| 1 | `90` |  | `_terminalModes = terminalModes;`
| 1 | `91` |  | `_bufferSize = bufferSize;`
| 1 | `92` |  | `}`
|  | `93` |  | ``
|  | `94` |  | `/// <summary>`
|  | `95` |  | `/// Starts this shell.`
|  | `96` |  | `/// </summary>`
|  | `97` |  | `/// <exception cref="SshException">Shell is started.</exception>`
|  | `98` |  | `public void Start()`
| 1 | `99` |  | `{`
| 1 | `100` |  | `if (IsStarted)`
| 0 | `101` |  | `{`
| 0 | `102` |  | `throw new SshException("Shell is started.");`
|  | `103` |  | `}`
|  | `104` |  | ``
| 1 | `105` |  | `Starting?.Invoke(this, EventArgs.Empty);`
|  | `106` |  | ``
| 1 | `107` |  | `_channel = _session.CreateChannelSession();`
| 1 | `108` |  | `_channel.DataReceived += Channel_DataReceived;`
| 1 | `109` |  | `_channel.ExtendedDataReceived += Channel_ExtendedDataReceived;`
| 1 | `110` |  | `_channel.Closed += Channel_Closed;`
| 1 | `111` |  | `_session.Disconnected += Session_Disconnected;`
| 1 | `112` |  | `_session.ErrorOccured += Session_ErrorOccured;`
|  | `113` |  | ``
| 1 | `114` |  | `_channel.Open();`
| 1 | `115` |  | `_ = _channel.SendPseudoTerminalRequest(_terminalName, _columns, _rows, _width, _height, _terminalModes);`
| 1 | `116` |  | `_ = _channel.SendShellRequest();`
|  | `117` |  | ``
| 1 | `118` |  | `_channelClosedWaitHandle = new AutoResetEvent(initialState: false);`
|  | `119` |  | ``
|  | `120` |  | `// Start input stream listener`
| 1 | `121` |  | `_dataReaderTaskCompleted = new ManualResetEvent(initialState: false);`
| 1 | `122` |  | `ThreadAbstraction.ExecuteThread(() =>`
| 1 | `123` |  | `{`
| 1 | `124` |  | `try`
| 1 | `125` |  | `{`
| 1 | `126` |  | `var buffer = new byte[_bufferSize];`
| 1 | `127` |  | ``
| 7146 | `128` |  | `while (_channel.IsOpen)`
| 7145 | `129` |  | `{`
| 7145 | `130` |  | `var readTask = _input.ReadAsync(buffer, 0, buffer.Length);`
| 7145 | `131` |  | `var readWaitHandle = ((IAsyncResult) readTask).AsyncWaitHandle;`
| 1 | `132` |  | ``
| 7145 | `133` |  | `if (WaitHandle.WaitAny(new[] { readWaitHandle, _channelClosedWaitHandle }) == 0)`
| 7145 | `134` |  | `{`
| 7145 | `135` |  | `var read = readTask.GetAwaiter().GetResult();`
| 7145 | `136` |  | `_channel.SendData(buffer, 0, read);`
| 7145 | `137` |  | `continue;`
| 1 | `138` |  | `}`
| 1 | `139` |  | ``
| 0 | `140` |  | `break;`
| 1 | `141` |  | `}`
| 1 | `142` |  | `}`
| 0 | `143` |  | `catch (Exception exp)`
| 0 | `144` |  | `{`
| 0 | `145` |  | `RaiseError(new ExceptionEventArgs(exp));`
| 0 | `146` |  | `}`
| 1 | `147` |  | `finally`
| 1 | `148` |  | `{`
| 1 | `149` |  | `_ = _dataReaderTaskCompleted.Set();`
| 1 | `150` |  | `}`
| 2 | `151` |  | `});`
|  | `152` |  | ``
| 1 | `153` |  | `IsStarted = true;`
|  | `154` |  | ``
| 1 | `155` |  | `Started?.Invoke(this, EventArgs.Empty);`
| 1 | `156` |  | `}`
|  | `157` |  | ``
|  | `158` |  | `/// <summary>`
|  | `159` |  | `/// Stops this shell.`
|  | `160` |  | `/// </summary>`
|  | `161` |  | `/// <exception cref="SshException">Shell is not started.</exception>`
|  | `162` |  | `public void Stop()`
| 1 | `163` |  | `{`
| 1 | `164` |  | `if (!IsStarted)`
| 0 | `165` |  | `{`
| 0 | `166` |  | `throw new SshException("Shell is not started.");`
|  | `167` |  | `}`
|  | `168` |  | ``
| 1 | `169` |  | `_channel?.Dispose();`
| 1 | `170` |  | `}`
|  | `171` |  | ``
|  | `172` |  | `private void Session_ErrorOccured(object sender, ExceptionEventArgs e)`
| 0 | `173` |  | `{`
| 0 | `174` |  | `RaiseError(e);`
| 0 | `175` |  | `}`
|  | `176` |  | ``
|  | `177` |  | `private void RaiseError(ExceptionEventArgs e)`
| 0 | `178` |  | `{`
| 0 | `179` |  | `ErrorOccurred?.Invoke(this, e);`
| 0 | `180` |  | `}`
|  | `181` |  | ``
|  | `182` |  | `private void Session_Disconnected(object sender, EventArgs e)`
| 0 | `183` |  | `{`
| 0 | `184` |  | `Stop();`
| 0 | `185` |  | `}`
|  | `186` |  | ``
|  | `187` |  | `private void Channel_ExtendedDataReceived(object sender, ChannelExtendedDataEventArgs e)`
| 0 | `188` |  | `{`
| 0 | `189` |  | `_extendedOutputStream?.Write(e.Data, 0, e.Data.Length);`
| 0 | `190` |  | `}`
|  | `191` |  | ``
|  | `192` |  | `private void Channel_DataReceived(object sender, ChannelDataEventArgs e)`
| 1 | `193` |  | `{`
| 1 | `194` |  | `_outputStream?.Write(e.Data, 0, e.Data.Length);`
| 1 | `195` |  | `}`
|  | `196` |  | ``
|  | `197` |  | `private void Channel_Closed(object sender, ChannelEventArgs e)`
| 1 | `198` |  | `{`
| 1 | `199` |  | `if (Stopping is not null)`
| 0 | `200` |  | `{`
|  | `201` |  | `// Handle event on different thread`
| 0 | `202` |  | `ThreadAbstraction.ExecuteThread(() => Stopping(this, EventArgs.Empty));`
| 0 | `203` |  | `}`
|  | `204` |  | ``
| 1 | `205` |  | `_channel.Dispose();`
| 1 | `206` |  | `_ = _channelClosedWaitHandle.Set();`
|  | `207` |  | ``
| 1 | `208` |  | `_input.Dispose();`
| 1 | `209` |  | `_input = null;`
|  | `210` |  | ``
| 1 | `211` |  | `_ = _dataReaderTaskCompleted.WaitOne(_session.ConnectionInfo.Timeout);`
| 1 | `212` |  | `_dataReaderTaskCompleted.Dispose();`
| 1 | `213` |  | `_dataReaderTaskCompleted = null;`
|  | `214` |  | ``
| 1 | `215` |  | `_channel.DataReceived -= Channel_DataReceived;`
| 1 | `216` |  | `_channel.ExtendedDataReceived -= Channel_ExtendedDataReceived;`
| 1 | `217` |  | `_channel.Closed -= Channel_Closed;`
|  | `218` |  | ``
| 1 | `219` |  | `UnsubscribeFromSessionEvents(_session);`
|  | `220` |  | ``
| 1 | `221` |  | `if (Stopped != null)`
| 0 | `222` |  | `{`
|  | `223` |  | `// Handle event on different thread`
| 0 | `224` |  | `ThreadAbstraction.ExecuteThread(() => Stopped(this, EventArgs.Empty));`
| 0 | `225` |  | `}`
|  | `226` |  | ``
| 1 | `227` |  | `_channel = null;`
| 1 | `228` |  | `}`
|  | `229` |  | ``
|  | `230` |  | `/// <summary>`
|  | `231` |  | `/// Unsubscribes the current <see cref="Shell"/> from session events.`
|  | `232` |  | `/// </summary>`
|  | `233` |  | `/// <param name="session">The session.</param>`
|  | `234` |  | `/// <remarks>`
|  | `235` |  | `/// Does nothing when <paramref name="session"/> is <see langword="null"/>.`
|  | `236` |  | `/// </remarks>`
|  | `237` |  | `private void UnsubscribeFromSessionEvents(ISession session)`
| 1 | `238` |  | `{`
| 1 | `239` |  | `if (session is null)`
| 0 | `240` |  | `{`
| 0 | `241` |  | `return;`
|  | `242` |  | `}`
|  | `243` |  | ``
| 1 | `244` |  | `session.Disconnected -= Session_Disconnected;`
| 1 | `245` |  | `session.ErrorOccured -= Session_ErrorOccured;`
| 1 | `246` |  | `}`
|  | `247` |  | ``
|  | `248` |  | `private bool _disposed;`
|  | `249` |  | ``
|  | `250` |  | `/// <summary>`
|  | `251` |  | `/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.`
|  | `252` |  | `/// </summary>`
|  | `253` |  | `public void Dispose()`
| 0 | `254` |  | `{`
| 0 | `255` |  | `Dispose(disposing: true);`
| 0 | `256` |  | `GC.SuppressFinalize(this);`
| 0 | `257` |  | `}`
|  | `258` |  | ``
|  | `259` |  | `/// <summary>`
|  | `260` |  | `/// Releases unmanaged and - optionally - managed resources.`
|  | `261` |  | `/// </summary>`
|  | `262` |  | `/// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources; <see langwor`
|  | `263` |  | `protected virtual void Dispose(bool disposing)`
| 1 | `264` |  | `{`
| 1 | `265` |  | `if (_disposed)`
| 0 | `266` |  | `{`
| 0 | `267` |  | `return;`
|  | `268` |  | `}`
|  | `269` |  | ``
| 1 | `270` |  | `if (disposing)`
| 0 | `271` |  | `{`
| 0 | `272` |  | `UnsubscribeFromSessionEvents(_session);`
|  | `273` |  | ``
| 0 | `274` |  | `var channelClosedWaitHandle = _channelClosedWaitHandle;`
| 0 | `275` |  | `if (channelClosedWaitHandle is not null)`
| 0 | `276` |  | `{`
| 0 | `277` |  | `channelClosedWaitHandle.Dispose();`
| 0 | `278` |  | `_channelClosedWaitHandle = null;`
| 0 | `279` |  | `}`
|  | `280` |  | ``
| 0 | `281` |  | `var channel = _channel;`
| 0 | `282` |  | `if (channel is not null)`
| 0 | `283` |  | `{`
| 0 | `284` |  | `channel.Dispose();`
| 0 | `285` |  | `_channel = null;`
| 0 | `286` |  | `}`
|  | `287` |  | ``
| 0 | `288` |  | `var dataReaderTaskCompleted = _dataReaderTaskCompleted;`
| 0 | `289` |  | `if (dataReaderTaskCompleted is not null)`
| 0 | `290` |  | `{`
| 0 | `291` |  | `dataReaderTaskCompleted.Dispose();`
| 0 | `292` |  | `_dataReaderTaskCompleted = null;`
| 0 | `293` |  | `}`
|  | `294` |  | ``
| 0 | `295` |  | `_disposed = true;`
| 0 | `296` |  | `}`
| 1 | `297` |  | `}`
|  | `298` |  | ``
|  | `299` |  | `/// <summary>`
|  | `300` |  | `/// Finalizes an instance of the <see cref="Shell"/> class.`
|  | `301` |  | `/// </summary>`
|  | `302` |  | `~Shell()`
| 2 | `303` |  | `{`
| 1 | `304` |  | `Dispose(disposing: false);`
| 2 | `305` |  | `}`
|  | `306` |  | `}`
|  | `307` |  | `}`

# Methods/Properties

get_IsStarted()
.ctor(Renci.SshNet.ISession,System.IO.Stream,System.IO.Stream,System.IO.Stream,System.String,System.UInt32,System.UInt32,System.UInt32,System.UInt32,System.Collections.Generic.IDictionary`2<Renci.SshNet.Common.TerminalModes,System.UInt32>,System.Int32)
Start()
Stop()
Session_ErrorOccured(System.Object,Renci.SshNet.Common.ExceptionEventArgs)
RaiseError(Renci.SshNet.Common.ExceptionEventArgs)
Session_Disconnected(System.Object,System.EventArgs)
Channel_ExtendedDataReceived(System.Object,Renci.SshNet.Common.ChannelExtendedDataEventArgs)
Channel_DataReceived(System.Object,Renci.SshNet.Common.ChannelDataEventArgs)
Channel_Closed(System.Object,Renci.SshNet.Common.ChannelEventArgs)
UnsubscribeFromSessionEvents(Renci.SshNet.ISession)
Dispose()
Dispose(System.Boolean)
Finalize()