Skip to content

Commit 407e6af

Browse files
committed
Initial version of low allocation and low overhead SshCommandLite class
1 parent da9d3fc commit 407e6af

File tree

6 files changed

+774
-0
lines changed

6 files changed

+774
-0
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#nullable enable
2+
using System;
3+
4+
namespace Renci.SshNet.Common
5+
{
6+
/// <summary>
7+
/// Class for command exit related events.
8+
/// </summary>
9+
public class CommandExitedEventArgs : EventArgs
10+
{
11+
/// <summary>
12+
/// Initializes a new instance of the <see cref="CommandExitedEventArgs"/> class.
13+
/// </summary>
14+
/// <param name="exitStatus">The exit status.</param>
15+
/// <param name="exitSignal">The exit signal.</param>
16+
public CommandExitedEventArgs(int? exitStatus, string? exitSignal)
17+
{
18+
ExitStatus = exitStatus;
19+
ExitSignal = exitSignal;
20+
}
21+
22+
/// <summary>
23+
/// Gets the number representing the exit status of the command, if applicable,
24+
/// otherwise <see langword="null"/>.
25+
/// </summary>
26+
/// <remarks>
27+
/// The value is not <see langword="null"/> when an exit status code has been returned
28+
/// from the server. If the command terminated due to a signal, <see cref="ExitSignal"/>
29+
/// may be not <see langword="null"/> instead.
30+
/// </remarks>
31+
/// <seealso cref="ExitSignal"/>
32+
public int? ExitStatus { get; }
33+
34+
35+
/// <summary>
36+
/// Gets the name of the signal due to which the command
37+
/// terminated violently, if applicable, otherwise <see langword="null"/>.
38+
/// </summary>
39+
/// <remarks>
40+
/// The value (if it exists) is supplied by the server and is usually one of the
41+
/// following, as described in https://datatracker.ietf.org/doc/html/rfc4254#section-6.10:
42+
/// ABRT, ALRM, FPE, HUP, ILL, INT, KILL, PIPE, QUIT, SEGV, TER, USR1, USR2.
43+
/// </remarks>
44+
public string? ExitSignal { get; }
45+
}
46+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#nullable enable
2+
using System;
3+
using System.Text;
4+
5+
namespace Renci.SshNet.Common
6+
{
7+
/// <summary>
8+
/// Base class for command output related events.
9+
/// </summary>
10+
public class CommandOutputEventArgs : EventArgs
11+
{
12+
/// <summary>
13+
/// Initializes a new instance of the <see cref="CommandOutputEventArgs"/> class.
14+
/// </summary>
15+
/// <param name="rawData">The raw data received.</param>
16+
/// <param name="encoding">The encoding used for the transmission.</param>
17+
public CommandOutputEventArgs(ArraySegment<byte> rawData, Encoding encoding)
18+
{
19+
RawData = rawData;
20+
Encoding = encoding;
21+
}
22+
23+
/// <summary>
24+
/// Gets the received data as <see langword="string"/>.
25+
/// </summary>
26+
public string Text
27+
{
28+
get
29+
{
30+
return Encoding.GetString(RawData.Array, RawData.Offset, RawData.Count);
31+
}
32+
}
33+
34+
/// <summary>
35+
/// Gets the raw data received from the server. This is the data that was used to create the <see cref="Text"/> property.
36+
/// </summary>
37+
public ArraySegment<byte> RawData { get; }
38+
39+
/// <summary>
40+
/// Gets the output encoding used.
41+
/// </summary>
42+
public Encoding Encoding { get; }
43+
}
44+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
#nullable enable
3+
using System.Text;
4+
5+
namespace Renci.SshNet.Common
6+
{
7+
/// <summary>
8+
/// Class for extended text output related events.
9+
/// </summary>
10+
public class ExtendedCommandEventArgs : CommandOutputEventArgs
11+
{
12+
/// <summary>
13+
/// Initializes a new instance of the <see cref="ExtendedCommandEventArgs"/> class.
14+
/// </summary>
15+
/// <param name="rawData">The raw data received.</param>
16+
/// <param name="encoding">The encoding used for the transmission.</param>
17+
/// <param name="dataTypeCode">The data type code.</param>
18+
public ExtendedCommandEventArgs(ArraySegment<byte> rawData, Encoding encoding, uint dataTypeCode)
19+
: base(rawData, encoding)
20+
{
21+
DataTypeCode = dataTypeCode;
22+
}
23+
24+
/// <summary>
25+
/// Gets the data type code.
26+
/// </summary>
27+
public uint DataTypeCode { get; }
28+
29+
/// <summary>
30+
/// Gets a value indicating whether the current data represents an stderr output.
31+
/// </summary>
32+
public bool IsError
33+
{
34+
get
35+
{
36+
return DataTypeCode == 1;
37+
}
38+
}
39+
}
40+
}

src/Renci.SshNet/ISshClient.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,38 @@ public interface ISshClient : IBaseClient
6666
/// <exception cref="ArgumentNullException"><paramref name="commandText"/> is <see langword="null"/>.</exception>
6767
public SshCommand RunCommand(string commandText);
6868

69+
/// <summary>
70+
/// Creates the command to be executed.
71+
/// </summary>
72+
/// <param name="commandText">The command text.</param>
73+
/// <returns><see cref="SshCommandLite"/> object.</returns>
74+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
75+
public SshCommandLite CreateCommandLite(string commandText);
76+
77+
/// <summary>
78+
/// Creates the command to be executed with specified encoding.
79+
/// </summary>
80+
/// <param name="commandText">The command text.</param>
81+
/// <param name="encoding">The encoding to use for results.</param>
82+
/// <returns><see cref="SshCommandLite"/> object which uses specified encoding.</returns>
83+
/// <remarks>This method will change current default encoding.</remarks>
84+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
85+
/// <exception cref="ArgumentNullException"><paramref name="commandText"/> or <paramref name="encoding"/> is <see langword="null"/>.</exception>
86+
public SshCommandLite CreateCommandLite(string commandText, Encoding encoding);
87+
88+
/// <summary>
89+
/// Creates and executes the command.
90+
/// </summary>
91+
/// <param name="commandText">The command text.</param>
92+
/// <returns>Returns an instance of <see cref="SshCommandLite"/> with execution results.</returns>
93+
/// <remarks>This method internally uses asynchronous calls.</remarks>
94+
/// <exception cref="ArgumentException">CommandText property is empty.</exception>
95+
/// <exception cref="SshException">Invalid Operation - An existing channel was used to execute this command.</exception>
96+
/// <exception cref="InvalidOperationException">Asynchronous operation is already in progress.</exception>
97+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
98+
/// <exception cref="ArgumentNullException"><paramref name="commandText"/> is <see langword="null"/>.</exception>
99+
public SshCommandLite RunCommandLite(string commandText);
100+
69101
/// <summary>
70102
/// Creates the shell.
71103
/// </summary>

src/Renci.SshNet/SshClient.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,29 @@ public SshCommand RunCommand(string commandText)
209209
return cmd;
210210
}
211211

212+
/// <inheritdoc />
213+
public SshCommandLite CreateCommandLite(string commandText)
214+
{
215+
return CreateCommandLite(commandText, ConnectionInfo.Encoding);
216+
}
217+
218+
/// <inheritdoc />
219+
public SshCommandLite CreateCommandLite(string commandText, Encoding encoding)
220+
{
221+
EnsureSessionIsOpen();
222+
223+
ConnectionInfo.Encoding = encoding;
224+
return new SshCommandLite(Session!, commandText, encoding);
225+
}
226+
227+
/// <inheritdoc />
228+
public SshCommandLite RunCommandLite(string commandText)
229+
{
230+
var cmd = CreateCommandLite(commandText);
231+
_ = cmd.Execute();
232+
return cmd;
233+
}
234+
212235
/// <inheritdoc />
213236
public Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint>? terminalModes, int bufferSize)
214237
{

0 commit comments

Comments
 (0)