Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(client-dotnet): implement C# NOW-proto client #14

Merged
merged 4 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 35 additions & 14 deletions NowProto.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,51 @@ VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nugets", "nugets", "{BD6154A5-8825-4023-A6AE-CE9FCC4B073B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Devolutions.NowProto", "nugets\Devolutions.NowProto\Devolutions.NowProto.csproj", "{2EB02FFB-DC42-4217-8520-93F06E17BFC4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devolutions.NowProto", "nugets\Devolutions.NowProto\Devolutions.NowProto.csproj", "{2EB02FFB-DC42-4217-8520-93F06E17BFC4}"
CBenoit marked this conversation as resolved.
Show resolved Hide resolved
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Devolutions.NowProto.Tests", "nugets\Devolutions.NowProto.Tests\Devolutions.NowProto.Tests.csproj", "{34912761-E8CB-4D88-A4B4-365F6DB88985}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devolutions.NowProto.Tests", "nugets\Devolutions.NowProto.Tests\Devolutions.NowProto.Tests.csproj", "{34912761-E8CB-4D88-A4B4-365F6DB88985}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devolutions.NowClient", "nugets\Devolutions.NowClient\Devolutions.NowClient.csproj", "{4692658E-C138-4A40-A10C-7E55D38D250B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Debug|x64.ActiveCfg = Debug|x64
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Debug|x64.Build.0 = Debug|x64
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Debug|x86.ActiveCfg = Debug|x86
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Debug|x86.Build.0 = Debug|x86
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Release|x64.ActiveCfg = Release|x64
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Release|x64.Build.0 = Release|x64
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Release|x86.ActiveCfg = Release|x86
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Release|x86.Build.0 = Release|x86
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Debug|x64.ActiveCfg = Debug|x64
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Debug|x64.Build.0 = Debug|x64
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Debug|x86.ActiveCfg = Debug|x86
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Debug|x86.Build.0 = Debug|x86
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Release|x64.ActiveCfg = Release|x64
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Release|x64.Build.0 = Release|x64
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Release|x86.ActiveCfg = Release|x86
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Release|x86.Build.0 = Release|x86
{4692658E-C138-4A40-A10C-7E55D38D250B}.Debug|x64.ActiveCfg = Debug|x64
{4692658E-C138-4A40-A10C-7E55D38D250B}.Debug|x64.Build.0 = Debug|x64
{4692658E-C138-4A40-A10C-7E55D38D250B}.Debug|x86.ActiveCfg = Debug|x86
{4692658E-C138-4A40-A10C-7E55D38D250B}.Debug|x86.Build.0 = Debug|x86
{4692658E-C138-4A40-A10C-7E55D38D250B}.Release|x64.ActiveCfg = Release|x64
{4692658E-C138-4A40-A10C-7E55D38D250B}.Release|x64.Build.0 = Release|x64
{4692658E-C138-4A40-A10C-7E55D38D250B}.Release|x86.ActiveCfg = Release|x86
{4692658E-C138-4A40-A10C-7E55D38D250B}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2EB02FFB-DC42-4217-8520-93F06E17BFC4}.Release|Any CPU.Build.0 = Release|Any CPU
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Debug|Any CPU.Build.0 = Debug|Any CPU
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Release|Any CPU.ActiveCfg = Release|Any CPU
{34912761-E8CB-4D88-A4B4-365F6DB88985}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{2EB02FFB-DC42-4217-8520-93F06E17BFC4} = {BD6154A5-8825-4023-A6AE-CE9FCC4B073B}
{34912761-E8CB-4D88-A4B4-365F6DB88985} = {BD6154A5-8825-4023-A6AE-CE9FCC4B073B}
{4692658E-C138-4A40-A10C-7E55D38D250B} = {BD6154A5-8825-4023-A6AE-CE9FCC4B073B}
EndGlobalSection
EndGlobal
60 changes: 60 additions & 0 deletions nugets/Devolutions.NowClient/AExecParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Threading.Channels;

using Devolutions.NowClient.Worker;

namespace Devolutions.NowClient
{
/// <summary>
/// Common remote execution parameters for all execution styles.
/// </summary>
public abstract class AExecParams
{
/// <summary>
/// Callback for stdout data collection.
/// </summary>
public StdoutHandler OnStdout
{
set
{
_stdoutHandler = value;
}
}

/// <summary>
/// Callback for stderr data collection.
/// </summary>
public StderrHandler OnStderr
{
set
{
_stderrHandler = value;
}
}

/// <summary>
/// Callback to be called when the execution session starts on the remote host.
/// </summary>
public StartedHandler OnStarted
{
set
{
_startedHandler = value;
}
}

internal ExecSession ToExecSession(uint sessionId, ChannelWriter<IClientCommand> commandWriter)
{
return new ExecSession(
sessionId,
commandWriter,
_stdoutHandler,
_stderrHandler,
_startedHandler
);
}

private StdoutHandler? _stdoutHandler;
private StderrHandler? _stderrHandler;
private StartedHandler? _startedHandler;
}
}
14 changes: 14 additions & 0 deletions nugets/Devolutions.NowClient/Devolutions.NowClient.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>x86;x64;ARM64</Platforms>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Devolutions.NowProto\Devolutions.NowProto.csproj" />
</ItemGroup>

</Project>
34 changes: 34 additions & 0 deletions nugets/Devolutions.NowClient/ExecBatchParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Devolutions.NowProto.Messages;

namespace Devolutions.NowClient
{
/// <summary>
/// Batch (Windows CMD) remote execution session parameters.
/// </summary>
/// <param name="command">Command/script to execute.</param>
public class ExecBatchParams(string command) : AExecParams
{
/// <summary>
/// Set the working directory for the command/script.
/// </summary>
public ExecBatchParams Directory(string directory)
{
_directory = directory;
return this;
}

internal NowMsgExecBatch ToNowMessage(uint sessionId)
{
var builder = new NowMsgExecBatch.Builder(sessionId, command);

if (_directory != null)
{
builder.Directory(_directory);
}

return builder.Build();
}

private string? _directory = null;
}
}
50 changes: 50 additions & 0 deletions nugets/Devolutions.NowClient/ExecProcessParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using Devolutions.NowProto.Messages;

namespace Devolutions.NowClient
{
/// <summary>
/// Plain process execution session parameters (e.g. CreateProcessW on Windows hosts).
/// </summary>
/// <param name="filename"></param>
public class ExecProcessParams(string filename) : AExecParams
{
/// <summary>
/// Set the command line parameters for the process.
/// </summary>
public ExecProcessParams Parameters(string parameters)
{
_parameters = parameters;
return this;
}

/// <summary>
/// Set the working directory for the process.
/// </summary>
public ExecProcessParams Directory(string directory)
{
_directory = directory;
return this;
}

internal NowMsgExecProcess ToNowMessage(uint sessionId)
{
var builder = new NowMsgExecProcess.Builder(sessionId, filename);

if (_parameters != null)
{
builder.Parameters(_parameters);
}

if (_directory != null)
{
builder.Directory(_directory);
}

return builder.Build();
}


private string? _parameters = null;
private string? _directory = null;
}
}
140 changes: 140 additions & 0 deletions nugets/Devolutions.NowClient/ExecPwshParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using Devolutions.NowProto.Messages;

namespace Devolutions.NowClient
{
/// <summary>
/// Powershell 7 and higher (pwsh) remote execution session parameters.
/// </summary>
/// <param name="command">PowerShell command/script to execute.</param>
public class ExecPwshParams(string command) : AExecParams
{
/// <summary>
/// Set the working directory for the command/script.
/// </summary>
public ExecPwshParams Directory(string directory)
{
_directory = directory;
return this;
}

/// <summary>
/// Set the configuration name for the PowerShell session. (-ConfigurationName)
/// </summary>
public ExecPwshParams ConfigurationName(string configurationName)
{
_configurationName = configurationName;
return this;
}

/// <summary>
/// Set the execution policy for the PowerShell session. (-ExecutionPolicy)
/// </summary>
public ExecPwshParams ExecutionPolicy(string executionPolicy)
{
_executionPolicy = executionPolicy;
return this;
}

/// <summary>
/// Set the apartment state for the PowerShell session. (-Sta/-Mta)
/// </summary>
public ExecPwshParams ApartmentState(NowMsgExecPwsh.ApartmentStateKind apartmentState)
{
_apartmentState = apartmentState;
return this;
}

/// <summary>
/// Disable the PowerShell logo display. (-NoLogo)
/// </summary>
public ExecPwshParams NoLogo()
{
_noLogo = true;
return this;
}

/// <summary>
/// Do not close the PowerShell session after the command/script execution. (-NoExit)
/// </summary>
public ExecPwshParams NoExit()
{
_noExit = true;
return this;
}

/// <summary>
/// Do not load the PowerShell profile. (-NoProfile)
/// </summary>
public ExecPwshParams NoProfile()
{
_noProfile = true;
return this;
}

/// <summary>
/// Run the PowerShell session in non-interactive mode. (-NonInteractive)
/// </summary>
public ExecPwshParams NonInteractive()
{
_nonInteractive = true;
return this;
}

internal NowMsgExecPwsh ToNowMessage(uint sessionId)
{
var builder = new NowMsgExecPwsh.Builder(sessionId, command);

if (_directory != null)
{
builder.Directory(_directory);
}

if (_configurationName != null)
{
builder.ConfigurationName(_configurationName);
}

if (_executionPolicy != null)
{
builder.ExecutionPolicy(_executionPolicy);
}

if (_apartmentState != null)
{
builder.ApartmentState(_apartmentState.Value);
}

if (_noLogo)
{
builder.SetNoLogo();
}

if (_noExit)
{
builder.SetNoExit();
}

if (_noProfile)
{
builder.SetNoProfile();
}

if (_nonInteractive)
{
builder.SetNonInteractive();
}

return builder.Build();
}


private string? _configurationName = null;
private string? _executionPolicy = null;
private string? _directory = null;
private NowMsgExecPwsh.ApartmentStateKind? _apartmentState = null;
private bool _noLogo = false;
private bool _noExit = false;
private bool _noProfile = false;
private bool _nonInteractive = false;
}
}
17 changes: 17 additions & 0 deletions nugets/Devolutions.NowClient/ExecRunParams.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Devolutions.NowProto.Messages;

namespace Devolutions.NowClient
{
/// <summary>
/// OS-specific simple "fire-and-forget" command execution parameters.
/// Note that session state/result is not available for this type of execution.
/// </summary>
/// <param name="command">Command to execute.</param>
public class ExecRunParams(string command) : AExecParams
{
internal NowMsgExecRun ToNowMessage(uint sessionId)
{
return new NowMsgExecRun(sessionId, command);
}
}
}
Loading
Loading