Merge pull request #1787 from PaulHigin/WorkingA

PowerShell remoting over SSH.
This commit is contained in:
Mike Richmond 2016-08-12 18:12:53 -07:00 committed by GitHub
commit ef5bf5ce08
18 changed files with 1202 additions and 35 deletions

View file

@ -155,6 +155,11 @@ namespace Microsoft.PowerShell
get { return _namedPipeServerMode; }
}
internal bool SSHServerMode
{
get { return _sshServerMode; }
}
internal bool ServerMode
{
get
@ -394,6 +399,10 @@ namespace Microsoft.PowerShell
{
_namedPipeServerMode = true;
}
else if (MatchSwitch(switchKey, "sshservermode", "sshs"))
{
_sshServerMode = true;
}
else if (MatchSwitch(switchKey, "configurationname", "config"))
{
++i;
@ -935,6 +944,7 @@ namespace Microsoft.PowerShell
private bool _socketServerMode;
private bool _serverMode;
private bool _namedPipeServerMode;
private bool _sshServerMode;
private string _configurationName;
private ConsoleHost _parent;
private ConsoleHostUserInterface _ui;

View file

@ -249,6 +249,12 @@ namespace Microsoft.PowerShell
s_cpp.ConfigurationName);
exitCode = 0;
}
else if (s_cpp.SSHServerMode)
{
ClrFacade.StartProfileOptimization("StartupProfileData-SSHServerMode");
System.Management.Automation.Remoting.Server.SSHProcessMediator.Run(s_cpp.InitialCommand);
exitCode = 0;
}
else if (s_cpp.SocketServerMode)
{
ClrFacade.StartProfileOptimization("StartupProfileData-SocketServerMode");

View file

@ -436,7 +436,12 @@ namespace System.Management.Automation.Runspaces
/// <summary>
/// Runspace is based on a VM socket transport.
/// </summary>
VMSocketTransport = 0x4
VMSocketTransport = 0x4,
/// <summary>
/// Runspace is based on SSH transport.
/// </summary>
SSHTransport = 0x8
}
#endregion

View file

@ -663,6 +663,7 @@ namespace System.Management.Automation.Runspaces
if ((!(connectionInfo is WSManConnectionInfo)) &&
(!(connectionInfo is NewProcessConnectionInfo)) &&
(!(connectionInfo is NamedPipeConnectionInfo)) &&
(!(connectionInfo is SSHConnectionInfo)) &&
(!(connectionInfo is VMConnectionInfo)) &&
(!(connectionInfo is ContainerConnectionInfo)))
{

View file

@ -974,6 +974,10 @@ namespace System.Management.Automation
{
returnCaps |= RunspaceCapability.VMSocketTransport;
}
else if (_connectionInfo is SSHConnectionInfo)
{
returnCaps |= RunspaceCapability.SSHTransport;
}
else
{
ContainerConnectionInfo containerConnectionInfo = _connectionInfo as ContainerConnectionInfo;

View file

@ -263,7 +263,7 @@ namespace System.Management.Automation.Runspaces
}
else
{
Name = AutoGenerateRunspaceName();
Name = AutoGenerateRunspaceName(Id);
remoteRunspace.PSSessionName = Name;
}
@ -298,6 +298,15 @@ namespace System.Management.Automation.Runspaces
return;
}
// SSH session
SSHConnectionInfo sshConnectionInfo = remoteRunspace.ConnectionInfo as SSHConnectionInfo;
if (sshConnectionInfo != null)
{
ComputerType = TargetMachineType.RemoteMachine;
ConfigurationName = "DefaultShell";
return;
}
// We only support WSMan/VM/Container sessions now.
Dbg.Assert(false, "Invalid Runspace");
}
@ -310,9 +319,35 @@ namespace System.Management.Automation.Runspaces
/// Generates and returns the runspace name
/// </summary>
/// <returns>auto generated name</returns>
private String AutoGenerateRunspaceName()
private string AutoGenerateRunspaceName(int id)
{
return "Session" + Id.ToString(System.Globalization.NumberFormatInfo.InvariantInfo);
string sessionIdStr = id.ToString(System.Globalization.NumberFormatInfo.InvariantInfo);
if (_remoteRunspace.ConnectionInfo is WSManConnectionInfo)
{
return "WinRM" + sessionIdStr;
}
else if (_remoteRunspace.ConnectionInfo is SSHConnectionInfo)
{
return "SSH" + sessionIdStr;
}
else if ((_remoteRunspace.ConnectionInfo is NamedPipeConnectionInfo) ||
(_remoteRunspace.ConnectionInfo is ContainerConnectionInfo))
{
return "NamedPipe" + sessionIdStr;
}
else if (_remoteRunspace.ConnectionInfo is NewProcessConnectionInfo)
{
return "Process" + sessionIdStr;
}
else if (_remoteRunspace.ConnectionInfo is VMConnectionInfo)
{
return "Socket" + sessionIdStr;
}
else
{
return "Session" + sessionIdStr;
}
}
/// <summary>
@ -360,7 +395,7 @@ namespace System.Management.Automation.Runspaces
/// <returns>Runspace name</returns>
internal static string ComposeRunspaceName(int id)
{
return "Session" + id.ToString(System.Globalization.NumberFormatInfo.InvariantInfo);
return "WinRM" + id.ToString(System.Globalization.NumberFormatInfo.InvariantInfo);
}
#endregion

View file

@ -386,6 +386,8 @@ namespace Microsoft.PowerShell.Commands
[Parameter(ParameterSetName = InvokeCommandCommand.FilePathVMIdParameterSet)]
[Parameter(ParameterSetName = InvokeCommandCommand.FilePathVMNameParameterSet)]
[Parameter(ParameterSetName = InvokeCommandCommand.FilePathContainerIdParameterSet)]
[Parameter(ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)]
[Parameter(ParameterSetName = InvokeCommandCommand.FilePathSSHHostParameterSet)]
public SwitchParameter AsJob
{
get
@ -443,6 +445,8 @@ namespace Microsoft.PowerShell.Commands
[Parameter(ParameterSetName = InvokeCommandCommand.FilePathVMIdParameterSet)]
[Parameter(ParameterSetName = InvokeCommandCommand.FilePathVMNameParameterSet)]
[Parameter(ParameterSetName = InvokeCommandCommand.FilePathContainerIdParameterSet)]
[Parameter(ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)]
[Parameter(ParameterSetName = InvokeCommandCommand.FilePathSSHHostParameterSet)]
[Alias("HCN")]
public SwitchParameter HideComputerName
{
@ -505,6 +509,9 @@ namespace Microsoft.PowerShell.Commands
[Parameter(Position = 1,
Mandatory = true,
ParameterSetName = InvokeCommandCommand.ContainerIdParameterSet)]
[Parameter(Position = 1,
Mandatory = true,
ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)]
[ValidateNotNull]
[Alias("Command")]
public override ScriptBlock ScriptBlock
@ -548,6 +555,9 @@ namespace Microsoft.PowerShell.Commands
[Parameter(Position = 1,
Mandatory = true,
ParameterSetName = FilePathContainerIdParameterSet)]
[Parameter(Position = 1,
Mandatory = true,
ParameterSetName = FilePathSSHHostParameterSet)]
[ValidateNotNull]
[Alias("PSPath")]
public override string FilePath
@ -649,6 +659,49 @@ namespace Microsoft.PowerShell.Commands
set { base.RunAsAdministrator = value; }
}
#region SSH Parameters
/// <summary>
/// Host Name
/// </summary>
[ValidateNotNullOrEmpty()]
[Parameter(Position = 0, Mandatory = true, ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)]
[Parameter(Position = 0, Mandatory = true, ParameterSetName = InvokeCommandCommand.FilePathSSHHostParameterSet)]
public override string HostName
{
get { return base.HostName; }
set { base.HostName = value; }
}
/// <summary>
/// User Name
/// </summary>
[Parameter(Mandatory = true, ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)]
[Parameter(Mandatory = true, ParameterSetName = InvokeCommandCommand.FilePathSSHHostParameterSet)]
[ValidateNotNullOrEmpty()]
public override string UserName
{
get { return base.UserName; }
set { base.UserName = value; }
}
/// <summary>
/// Key Path
/// </summary>
[Parameter(ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)]
[Parameter(ParameterSetName = InvokeCommandCommand.FilePathSSHHostParameterSet)]
[ValidateNotNullOrEmpty()]
public override string KeyPath
{
get { return base.KeyPath; }
set { base.KeyPath = value; }
}
#endregion
#endregion Parameters
#region Overrides
@ -929,6 +982,18 @@ namespace Microsoft.PowerShell.Commands
}
break;
case InvokeCommandCommand.SSHHostParameterSet:
case InvokeCommandCommand.FilePathSSHHostParameterSet:
{
var job = new PSRemotingJob(new string[] { this.HostName }, Operations,
ScriptBlock.ToString(), ThrottleLimit, _name);
job.PSJobTypeName = RemoteJobType;
job.HideComputerName = _hideComputerName;
this.JobRepository.Add(job);
WriteObject(job);
}
break;
case InvokeCommandCommand.SessionParameterSet:
case InvokeCommandCommand.FilePathSessionParameterSet:
{

View file

@ -176,6 +176,11 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
protected const string VMNameParameterSet = "VMName";
/// <summary>
/// SSH host parameter set
/// </summary>
protected const string SSHHostParameterSet = "SSHHost";
/// <summary>
/// runspace parameter set
/// </summary>
@ -673,6 +678,43 @@ namespace Microsoft.PowerShell.Commands
}
private string _thumbPrint = null;
#region SSHHostParameters
/// <summary>
/// SSH Target Host Name
/// </summary>
[Parameter(ParameterSetName = PSRemotingBaseCmdlet.SSHHostParameterSet)]
[ValidateNotNullOrEmpty()]
public virtual string HostName
{
get;
set;
}
/// <summary>
/// SSH User Name
/// </summary>
[Parameter(ParameterSetName = PSRemotingBaseCmdlet.SSHHostParameterSet)]
[ValidateNotNullOrEmpty()]
public virtual string UserName
{
get;
set;
}
/// <summary>
/// SSH Key Path
/// </summary>
[Parameter(ParameterSetName = PSRemotingBaseCmdlet.SSHHostParameterSet)]
[ValidateNotNullOrEmpty()]
public virtual string KeyPath
{
get;
set;
}
#endregion
#endregion Properties
#region Internal Static Methods
@ -865,6 +907,11 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
protected const string FilePathContainerIdParameterSet = "FilePathContainerId";
/// <summary>
/// SSH Host file path parameter set.
/// </summary>
protected const string FilePathSSHHostParameterSet = "FilePathSSHHost";
#endregion
#region Parameters
@ -1113,6 +1160,21 @@ namespace Microsoft.PowerShell.Commands
}
}// CreateHelpersForSpecifiedComputerNames
/// <summary>
/// Creates helper objects for host names for PSRP over SSH
/// remoting.
/// </summary>
protected void CreateHelpersForSpecifiedHostNames()
{
var sshConnectionInfo = new SSHConnectionInfo(this.UserName, this.HostName, this.KeyPath);
var typeTable = TypeTable.LoadDefaultTypeFiles();
var remoteRunspace = RunspaceFactory.CreateRunspace(sshConnectionInfo, this.Host, typeTable) as RemoteRunspace;
var pipeline = CreatePipeline(remoteRunspace);
var operation = new ExecutionCmdletHelperComputerName(remoteRunspace, pipeline);
Operations.Add(operation);
}
/// <summary>
/// Creates helper objects with the specified command for
/// the specified remote runspaceinfo objects
@ -1720,6 +1782,11 @@ namespace Microsoft.PowerShell.Commands
}
break;
case PSExecutionCmdlet.SSHHostParameterSet:
case PSExecutionCmdlet.FilePathSSHHostParameterSet:
CreateHelpersForSpecifiedHostNames();
break;
case PSExecutionCmdlet.FilePathSessionParameterSet:
case PSExecutionCmdlet.SessionParameterSet:
{

View file

@ -297,6 +297,10 @@ namespace Microsoft.PowerShell.Commands
case ContainerIdParameterSet:
remoteRunspace = GetRunspaceForContainerSession();
break;
case SSHHostParameterSet:
remoteRunspace = GetRunspaceForSSHSession();
break;
}
// If runspace is null then the error record has already been written and we can exit.
@ -1239,6 +1243,20 @@ namespace Microsoft.PowerShell.Commands
return remoteRunspace;
}
/// <summary>
/// Create remote runspace for SSH session
/// </summary>
private RemoteRunspace GetRunspaceForSSHSession()
{
var sshConnectionInfo = new SSHConnectionInfo(this.UserName, this.HostName, this.KeyPath);
var typeTable = TypeTable.LoadDefaultTypeFiles();
var remoteRunspace = RunspaceFactory.CreateRunspace(sshConnectionInfo, this.Host, typeTable) as RemoteRunspace;
remoteRunspace.Open();
remoteRunspace.ShouldCloseOnPop = true;
return remoteRunspace;
}
#endregion
#region Internal Methods

View file

@ -238,6 +238,12 @@ namespace Microsoft.PowerShell.Commands
}
break;
case NewPSSessionCommand.SSHHostParameterSet:
{
remoteRunspaces = CreateRunspacesForSSHHostParameterSet();
}
break;
default:
{
Dbg.Assert(false, "Missing paramenter set in switch statement");
@ -1049,6 +1055,23 @@ namespace Microsoft.PowerShell.Commands
return remoteRunspaces;
}// CreateRunspacesWhenContainerParameterSpecified
/// <summary>
/// CreateRunspacesForSSHHostParameterSet
/// </summary>
/// <returns></returns>
private List<RemoteRunspace> CreateRunspacesForSSHHostParameterSet()
{
var remoteRunspaces = new List<RemoteRunspace>();
var sshConnectionInfo = new SSHConnectionInfo(
this.UserName,
this.HostName,
this.KeyPath);
var typeTable = TypeTable.LoadDefaultTypeFiles();
remoteRunspaces.Add(RunspaceFactory.CreateRunspace(sshConnectionInfo, this.Host, typeTable) as RemoteRunspace);
return remoteRunspaces;
}
/// <summary>
/// Helper method to either get a user supplied runspace/session name
/// or to generate one along with a unique Id.

View file

@ -222,10 +222,10 @@ namespace System.Management.Automation.Remoting
uint nDefaultTimeOut,
SECURITY_ATTRIBUTES securityAttributes);
internal static SECURITY_ATTRIBUTES GetSecurityAttributes(GCHandle securityDescriptorPinnedHandle)
internal static SECURITY_ATTRIBUTES GetSecurityAttributes(GCHandle securityDescriptorPinnedHandle, bool inheritHandle = false)
{
SECURITY_ATTRIBUTES securityAttributes = new NamedPipeNative.SECURITY_ATTRIBUTES();
securityAttributes.InheritHandle = false;
securityAttributes.InheritHandle = inheritHandle;
securityAttributes.NLength = (int)Marshal.SizeOf(securityAttributes);
securityAttributes.LPSecurityDescriptor = securityDescriptorPinnedHandle.AddrOfPinnedObject();
return securityAttributes;
@ -590,7 +590,7 @@ namespace System.Management.Automation.Remoting
#region Private Methods
private static CommonSecurityDescriptor GetServerPipeSecurity()
internal static CommonSecurityDescriptor GetServerPipeSecurity()
{
// Built-in Admin SID
SecurityIdentifier adminSID = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null);

View file

@ -4,9 +4,12 @@ Copyright (c) Microsoft Corporation. All rights reserved.
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Pipes;
using System.ComponentModel; // Win32Exception
using System.Management.Automation.Tracing;
using System.Management.Automation.Remoting;
using System.Management.Automation.Internal;
@ -14,6 +17,8 @@ using System.Management.Automation.Remoting.Client;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Security.AccessControl;
using Microsoft.Win32.SafeHandles;
using Dbg = System.Management.Automation.Diagnostics;
using WSManAuthenticationMechanism = System.Management.Automation.Remoting.Client.WSManNativeApi.WSManAuthenticationMechanism;
@ -1555,7 +1560,6 @@ namespace System.Management.Automation.Runspaces
/// </summary>
public override string ComputerName
{
// TODO: should this be different
get { return "localhost"; }
set { throw new NotImplementedException(); }
}
@ -1821,6 +1825,487 @@ namespace System.Management.Automation.Runspaces
#endregion
}
/// <summary>
/// Class used to create a connection through an SSH.exe client to a remote host machine.
/// Connection information includes SSH target (user name and host machine) along with
/// client key used for key based user authorization.
/// </summary>
public sealed class SSHConnectionInfo : RunspaceConnectionInfo
{
#region Properties
/// <summary>
/// User Name
/// </summary>
public string UserName
{
get;
private set;
}
/// <summary>
/// Key Path
/// </summary>
private string KeyPath
{
get;
set;
}
#endregion
#region Constructors
/// <summary>
/// Constructor
/// </summary>
private SSHConnectionInfo()
{ }
/// <summary>
/// Constructor
/// </summary>
/// <param name="userName">User Name</param>
/// <param name="computerName">Computer Name</param>
/// <param name="keyPath">Key Path</param>
public SSHConnectionInfo(
string userName,
string computerName,
string keyPath)
{
if (userName == null) { throw new PSArgumentNullException("userName"); }
if (computerName == null) { throw new PSArgumentNullException("computerName"); }
this.UserName = userName;
this.ComputerName = computerName;
this.KeyPath = keyPath;
}
#endregion
#region Overrides
/// <summary>
/// Computer is always localhost.
/// </summary>
public override string ComputerName
{
get;
set;
}
/// <summary>
/// Credential
/// </summary>
public override PSCredential Credential
{
get { return null; }
set { throw new NotImplementedException(); }
}
/// <summary>
/// Authentication
/// </summary>
public override AuthenticationMechanism AuthenticationMechanism
{
get { return AuthenticationMechanism.Default; }
set { throw new NotImplementedException(); }
}
/// <summary>
/// CertificateThumbprint
/// </summary>
public override string CertificateThumbprint
{
get { return string.Empty; }
set { throw new NotImplementedException(); }
}
/// <summary>
/// Shallow copy of current instance.
/// </summary>
/// <returns>NamedPipeConnectionInfo</returns>
internal override RunspaceConnectionInfo InternalCopy()
{
SSHConnectionInfo newCopy = new SSHConnectionInfo();
newCopy.ComputerName = this.ComputerName;
newCopy.UserName = this.UserName;
newCopy.KeyPath = this.KeyPath;
return newCopy;
}
/// <summary>
/// CreateClientSessionTransportManager
/// </summary>
/// <param name="instanceId"></param>
/// <param name="sessionName"></param>
/// <param name="cryptoHelper"></param>
/// <returns></returns>
internal override BaseClientSessionTransportManager CreateClientSessionTransportManager(Guid instanceId, string sessionName, PSRemotingCryptoHelper cryptoHelper)
{
return new SSHClientSessionTransportManager(
this,
instanceId,
cryptoHelper);
}
#endregion
#region Internal Methods
/// <summary>
/// StartSSHProcess
/// </summary>
/// <returns></returns>
internal System.Diagnostics.Process StartSSHProcess(
out StreamWriter stdInWriterVar,
out StreamReader stdOutReaderVar,
out StreamReader stdErrReaderVar)
{
string filePath = string.Empty;
#if !UNIX
var context = Runspaces.LocalPipeline.GetExecutionContextFromTLS();
if (context != null)
{
var cmdInfo = context.CommandDiscovery.LookupCommandInfo("ssh.exe", CommandOrigin.Internal) as ApplicationInfo;
if (cmdInfo != null)
{
filePath = cmdInfo.Path;
}
}
#else
filePath = @"ssh";
#endif
// Extract an optional domain name if provided.
string domainName = null;
string userName = this.UserName;
#if !UNIX
var parts = this.UserName.Split(Utils.Separators.Backslash);
if (parts.Length == 2)
{
domainName = parts[0];
userName = parts[1];
}
#endif
// Create client ssh process that hosts powershell.exe as a subsystem and is configured
// to be in server mode for PSRP over SSHD:
// powershell -Version 5.1 -sshs -NoLogo -NoProfile
// See sshd_configuration file, subsystems section and it will have this entry:
// Subsystem powershell C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Version 5.1 -sshs -NoLogo -NoProfile
string arguments;
if (!string.IsNullOrEmpty(this.KeyPath))
{
arguments = (string.IsNullOrEmpty(domainName)) ?
string.Format(CultureInfo.InvariantCulture, @"-i ""{0}"" {1}@{2} -s powershell", this.KeyPath, userName, this.ComputerName) :
string.Format(CultureInfo.InvariantCulture, @"-i ""{0}"" -l {1}@{2} {3} -s powershell", this.KeyPath, userName, domainName, this.ComputerName);
}
else
{
arguments = (string.IsNullOrEmpty(domainName)) ?
string.Format(CultureInfo.InvariantCulture, @"{0}@{1} -s powershell", userName, this.ComputerName) :
string.Format(CultureInfo.InvariantCulture, @"-l {0}@{1} {2} -s powershell", userName, domainName, this.ComputerName);
}
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(
filePath,
arguments);
startInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(filePath);
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
return StartSSHProcessImpl(startInfo, out stdInWriterVar, out stdOutReaderVar, out stdErrReaderVar);
}
#endregion
#region SSH Process Creation
#if UNIX
/// <summary>
/// Create a process through managed APIs and return StdIn, StdOut, StdError reader/writers
/// This works for non-Windows platforms and is simpler.
/// </summary>
private static System.Diagnostics.Process StartSSHProcessImpl(
System.Diagnostics.ProcessStartInfo startInfo,
out StreamWriter stdInWriterVar,
out StreamReader stdOutReaderVar,
out StreamReader stdErrReaderVar)
{
startInfo.RedirectStandardInput = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
System.Diagnostics.Process process = new Process();
process.StartInfo = startInfo;
process.Start();
stdInWriterVar = process.StandardInput;
stdOutReaderVar = process.StandardOutput;
stdErrReaderVar = process.StandardError;
return process;
}
#else
/// <summary>
/// Create a process through native Win32 APIs and return StdIn, StdOut, StdError reader/writers
/// This needs to be done via Win32 APIs because managed code creates anonymous synchronous pipes
/// for redirected StdIn/Out and SSH (and PSRP) require asynchrous (overlapped) pipes, which must
/// be through named pipes. Managed code for named pipes is unreliable and so this is done via
/// P-Invoking native APIs.
/// </summary>
private static System.Diagnostics.Process StartSSHProcessImpl(
System.Diagnostics.ProcessStartInfo startInfo,
out StreamWriter stdInWriterVar,
out StreamReader stdOutReaderVar,
out StreamReader stdErrReaderVar)
{
Exception ex = null;
System.Diagnostics.Process sshProcess = null;
//
// These std pipe handles are bound to managed Reader/Writer objects and returned to the transport
// manager object, which uses them for PSRP communication. The lifetime of these handles are then
// tied to the reader/writer objects which the transport is responsible for disposing (see
// SSHClientSessionTransportManger and the CloseConnection() method.
//
SafePipeHandle stdInPipeServer = null;
SafePipeHandle stdOutPipeServer = null;
SafePipeHandle stdErrPipeServer = null;
try
{
sshProcess = CreateProcessWithRedirectedStd(
startInfo,
out stdInPipeServer,
out stdOutPipeServer,
out stdErrPipeServer);
}
catch (InvalidOperationException e) { ex = e; }
catch (ArgumentException e) { ex = e; }
catch (FileNotFoundException e) { ex = e; }
catch (System.ComponentModel.Win32Exception e) { ex = e; }
if ((ex != null) ||
(sshProcess == null) ||
(sshProcess.HasExited == true))
{
throw new InvalidOperationException(RemotingErrorIdStrings.CannotStartSSHClient, ex);
}
// Create the std in writer/readers needed for communication with ssh.exe.
stdInWriterVar = null;
stdOutReaderVar = null;
stdErrReaderVar = null;
try
{
stdInWriterVar = new StreamWriter(new NamedPipeServerStream(PipeDirection.Out, true, true, stdInPipeServer));
stdOutReaderVar = new StreamReader(new NamedPipeServerStream(PipeDirection.In, true, true, stdOutPipeServer));
stdErrReaderVar = new StreamReader(new NamedPipeServerStream(PipeDirection.In, true, true, stdErrPipeServer));
}
catch (Exception e)
{
CommandProcessorBase.CheckForSevereException(e);
if (stdInWriterVar != null) { stdInWriterVar.Dispose(); } else { stdInPipeServer.Dispose(); }
if (stdOutReaderVar != null) { stdInWriterVar.Dispose(); } else { stdOutPipeServer.Dispose(); }
if (stdErrReaderVar != null) { stdInWriterVar.Dispose(); } else { stdErrPipeServer.Dispose(); }
throw;
}
return sshProcess;
}
/// <summary>
/// CreateProcessWithRedirectedStd
/// </summary>
private static Process CreateProcessWithRedirectedStd(
ProcessStartInfo startInfo,
out SafePipeHandle stdInPipeServer,
out SafePipeHandle stdOutPipeServer,
out SafePipeHandle stdErrPipeServer)
{
//
// Create named (async) pipes for reading/writing to std.
//
stdInPipeServer = null;
stdOutPipeServer = null;
stdErrPipeServer = null;
SafePipeHandle stdInPipeClient = null;
SafePipeHandle stdOutPipeClient = null;
SafePipeHandle stdErrPipeClient = null;
string randomName = System.IO.Path.GetFileNameWithoutExtension(System.IO.Path.GetRandomFileName());
try
{
// Get default pipe security (Admin and current user access)
var securityDesc = RemoteSessionNamedPipeServer.GetServerPipeSecurity();
var stdInPipeName = @"\\.\pipe\StdIn" + randomName;
stdInPipeServer = CreateNamedPipe(stdInPipeName, securityDesc);
stdInPipeClient = GetNamedPipeHandle(stdInPipeName);
var stdOutPipeName = @"\\.\pipe\StdOut" + randomName;
stdOutPipeServer = CreateNamedPipe(stdOutPipeName, securityDesc);
stdOutPipeClient = GetNamedPipeHandle(stdOutPipeName);
var stdErrPipeName = @"\\.\pipe\StdErr" + randomName;
stdErrPipeServer = CreateNamedPipe(stdErrPipeName, securityDesc);
stdErrPipeClient = GetNamedPipeHandle(stdErrPipeName);
}
catch (Exception e)
{
CommandProcessorBase.CheckForSevereException(e);
if (stdInPipeServer != null) { stdInPipeServer.Dispose(); }
if (stdInPipeClient != null) { stdInPipeClient.Dispose(); }
if (stdOutPipeServer != null) { stdOutPipeServer.Dispose(); }
if (stdOutPipeClient != null) { stdOutPipeClient.Dispose(); }
if (stdErrPipeServer != null) { stdErrPipeServer.Dispose(); }
if (stdErrPipeClient != null) { stdErrPipeClient.Dispose(); }
throw;
}
// Create process
PlatformInvokes.STARTUPINFO lpStartupInfo = new PlatformInvokes.STARTUPINFO();
PlatformInvokes.PROCESS_INFORMATION lpProcessInformation = new PlatformInvokes.PROCESS_INFORMATION();
int creationFlags = 0;
try
{
var cmdLine = String.Format(CultureInfo.InvariantCulture, @"""{0}"" {1}", startInfo.FileName, startInfo.Arguments);
lpStartupInfo.hStdInput = new SafeFileHandle(stdInPipeClient.DangerousGetHandle(), false);
lpStartupInfo.hStdOutput = new SafeFileHandle(stdOutPipeClient.DangerousGetHandle(), false);
lpStartupInfo.hStdError = new SafeFileHandle(stdErrPipeClient.DangerousGetHandle(), false);
lpStartupInfo.dwFlags = 0x100;
// No new window: Inherit the parent process's console window
creationFlags = 0x00000000;
// Create the new process suspended so we have a chance to get a corresponding Process object in case it terminates quickly.
creationFlags |= 0x00000004;
PlatformInvokes.SECURITY_ATTRIBUTES lpProcessAttributes = new PlatformInvokes.SECURITY_ATTRIBUTES();
PlatformInvokes.SECURITY_ATTRIBUTES lpThreadAttributes = new PlatformInvokes.SECURITY_ATTRIBUTES();
bool success = PlatformInvokes.CreateProcess(
null,
cmdLine,
lpProcessAttributes,
lpThreadAttributes,
true,
creationFlags,
IntPtr.Zero,
startInfo.WorkingDirectory,
lpStartupInfo,
lpProcessInformation);
if (!success)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
// At this point, we should have a suspended process. Get the .Net Process object, resume the process, and return.
Process result = Process.GetProcessById(lpProcessInformation.dwProcessId);
PlatformInvokes.ResumeThread(lpProcessInformation.hThread);
return result;
}
catch (Exception e)
{
CommandProcessorBase.CheckForSevereException(e);
if (stdInPipeServer != null) { stdInPipeServer.Dispose(); }
if (stdInPipeClient != null) { stdInPipeClient.Dispose(); }
if (stdOutPipeServer != null) { stdOutPipeServer.Dispose(); }
if (stdOutPipeClient != null) { stdOutPipeClient.Dispose(); }
if (stdErrPipeServer != null) { stdErrPipeServer.Dispose(); }
if (stdErrPipeClient != null) { stdErrPipeClient.Dispose(); }
throw;
}
finally
{
lpProcessInformation.Dispose();
}
}
private static SafePipeHandle GetNamedPipeHandle(string pipeName)
{
// Create pipe flags for asynchronous pipes.
uint pipeFlags = NamedPipeNative.FILE_FLAG_OVERLAPPED;
// We want an inheritable handle.
PlatformInvokes.SECURITY_ATTRIBUTES securityAttributes = new PlatformInvokes.SECURITY_ATTRIBUTES();
// Get handle to pipe.
var fileHandle = PlatformInvokes.CreateFileW(
pipeName,
NamedPipeNative.GENERIC_READ | NamedPipeNative.GENERIC_WRITE,
0,
securityAttributes,
NamedPipeNative.OPEN_EXISTING,
pipeFlags,
IntPtr.Zero);
int lastError = Marshal.GetLastWin32Error();
if (fileHandle == PlatformInvokes.INVALID_HANDLE_VALUE)
{
throw new System.ComponentModel.Win32Exception(lastError);
}
return new SafePipeHandle(fileHandle, true);
}
private static SafePipeHandle CreateNamedPipe(
string pipeName,
CommonSecurityDescriptor securityDesc)
{
// Create optional security attributes based on provided PipeSecurity.
NamedPipeNative.SECURITY_ATTRIBUTES securityAttributes = null;
GCHandle? securityDescHandle = null;
if (securityDesc != null)
{
byte[] securityDescBuffer = new byte[securityDesc.BinaryLength];
securityDesc.GetBinaryForm(securityDescBuffer, 0);
securityDescHandle = GCHandle.Alloc(securityDescBuffer, GCHandleType.Pinned);
securityAttributes = NamedPipeNative.GetSecurityAttributes(securityDescHandle.Value, true); ;
}
// Create async named pipe.
SafePipeHandle pipeHandle = NamedPipeNative.CreateNamedPipe(
pipeName,
NamedPipeNative.PIPE_ACCESS_DUPLEX | NamedPipeNative.FILE_FLAG_FIRST_PIPE_INSTANCE | NamedPipeNative.FILE_FLAG_OVERLAPPED,
NamedPipeNative.PIPE_TYPE_MESSAGE | NamedPipeNative.PIPE_READMODE_MESSAGE,
1,
32768,
32768,
0,
securityAttributes);
int lastError = Marshal.GetLastWin32Error();
if (securityDescHandle != null)
{
securityDescHandle.Value.Free();
}
if (pipeHandle.IsInvalid)
{
throw new Win32Exception(lastError);
}
return pipeHandle;
}
#endif
#endregion
}
/// <summary>
/// The class that contains connection information for a remote session between a local host
/// and VM. The local host can be a VM in nested scenario.

View file

@ -424,6 +424,7 @@ namespace System.Management.Automation.Remoting
lock (_syncObject)
{
_writer.WriteLine(data);
_writer.Flush();
}
}
@ -1403,6 +1404,174 @@ namespace System.Management.Automation.Remoting.Client
#endregion
}
internal sealed class SSHClientSessionTransportManager : OutOfProcessClientSessionTransportManagerBase
{
#region Data
private SSHConnectionInfo _connectionInfo;
private Process _sshProcess;
private StreamWriter _stdInWriter;
private StreamReader _stdOutReader;
private StreamReader _stdErrReader;
private const string _threadName = "SSHTransport Reader Thread";
#endregion
#region Constructors
internal SSHClientSessionTransportManager(
SSHConnectionInfo connectionInfo,
Guid runspaceId,
PSRemotingCryptoHelper cryptoHelper)
: base(runspaceId, cryptoHelper)
{
if (connectionInfo == null) { throw new PSArgumentException("connectionInfo"); }
_connectionInfo = connectionInfo;
}
#endregion
#region Overrides
internal override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (isDisposing)
{
CloseConnection();
}
}
protected override void CleanupConnection()
{
CloseConnection();
}
/// <summary>
/// Create an SSH connection to the target host and set up
/// transport reader/writer.
/// </summary>
internal override void CreateAsync()
{
// Create the ssh client process with connection to host target.
_sshProcess = _connectionInfo.StartSSHProcess(
out _stdInWriter,
out _stdOutReader,
out _stdErrReader);
_sshProcess.Exited += (sender, args) =>
{
CloseConnection();
};
// Create writer for named pipe.
stdInWriter = new OutOfProcessTextWriter(_stdInWriter);
// Create reader thread and send first PSRP message.
StartReaderThread(_stdOutReader);
}
#endregion
#region Private Methods
private void CloseConnection()
{
var stdInWriter = _stdInWriter;
if (stdInWriter != null) { stdInWriter.Dispose(); }
var stdOutReader = _stdOutReader;
if (stdOutReader != null) { stdOutReader.Dispose(); }
var stdErrReader = _stdErrReader;
if (stdErrReader != null) { stdErrReader.Dispose(); }
var sshProcess = _sshProcess;
if ((sshProcess != null) && !sshProcess.HasExited)
{
_sshProcess = null;
try
{
sshProcess.Kill();
}
catch (InvalidOperationException) { }
catch (NotSupportedException) { }
catch (System.ComponentModel.Win32Exception) { }
}
}
private void StartReaderThread(
StreamReader reader)
{
Thread readerThread = new Thread(ProcessReaderThread);
readerThread.Name = _threadName;
readerThread.IsBackground = true;
readerThread.Start(reader);
}
private void ProcessReaderThread(object state)
{
try
{
StreamReader reader = state as StreamReader;
Dbg.Assert(reader != null, "Reader cannot be null.");
// Send one fragment.
SendOneItem();
// Start reader loop.
while (true)
{
string data = reader.ReadLine();
if (data == null)
{
// End of stream indicates the target process was lost.
// Raise transport exception to invalidate the client remote runspace.
PSRemotingTransportException psrte = new PSRemotingTransportException(
PSRemotingErrorId.IPCServerProcessReportedError,
RemotingErrorIdStrings.IPCServerProcessReportedError,
RemotingErrorIdStrings.NamedPipeTransportProcessEnded);
RaiseErrorHandler(new TransportErrorOccuredEventArgs(psrte, TransportMethodEnum.ReceiveShellOutputEx));
break;
}
if (data.StartsWith(System.Management.Automation.Remoting.Server.NamedPipeErrorTextWriter.ErrorPrepend, StringComparison.OrdinalIgnoreCase))
{
// Error message from the server.
string errorData = data.Substring(System.Management.Automation.Remoting.Server.NamedPipeErrorTextWriter.ErrorPrepend.Length);
HandleErrorDataReceived(errorData);
}
else
{
// Normal output data.
HandleOutputDataReceived(data);
}
}
}
catch (ObjectDisposedException)
{
// Normal reader thread end.
}
catch (Exception e)
{
CommandProcessorBase.CheckForSevereException(e);
if (e is ArgumentOutOfRangeException)
{
Dbg.Assert(false, "Need to adjust transport fragmentor to accomodate read buffer size.");
}
string errorMsg = (e.Message != null) ? e.Message : string.Empty;
_tracer.WriteMessage("SSHClientSessionTransportManager", "StartReaderThread", Guid.Empty,
"Transport manager reader thread ended with error: {0}", errorMsg);
}
}
#endregion
}
internal abstract class NamedPipeClientSessionTransportManagerBase : OutOfProcessClientSessionTransportManagerBase
{
#region Data
@ -1979,8 +2148,8 @@ namespace System.Management.Automation.Remoting.Server
#region Constructors
internal OutOfProcessServerSessionTransportManager(OutOfProcessTextWriter outWriter, OutOfProcessTextWriter errWriter)
: base(BaseTransportManager.DefaultFragmentSize, new PSRemotingCryptoHelperServer())
internal OutOfProcessServerSessionTransportManager(OutOfProcessTextWriter outWriter, OutOfProcessTextWriter errWriter, PSRemotingCryptoHelperServer cryptoHelper)
: base(BaseTransportManager.DefaultFragmentSize, cryptoHelper)
{
Dbg.Assert(null != outWriter, "outWriter cannot be null.");
Dbg.Assert(null != errWriter, "errWriter cannot be null.");

View file

@ -7,6 +7,7 @@ using System.IO;
using System.Threading;
using System.Security.Principal;
using System.Management.Automation.Internal;
using Microsoft.Win32.SafeHandles;
using Dbg = System.Management.Automation.Diagnostics;
namespace System.Management.Automation.Remoting.Server
@ -296,14 +297,21 @@ namespace System.Management.Automation.Remoting.Server
#region Methods
protected OutOfProcessServerSessionTransportManager CreateSessionTransportManager(string configurationName)
protected OutOfProcessServerSessionTransportManager CreateSessionTransportManager(string configurationName, PSRemotingCryptoHelperServer cryptoHelper)
{
PSSenderInfo senderInfo;
#if !UNIX
WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent();
PSPrincipal userPrincipal = new PSPrincipal(new PSIdentity("", true, currentIdentity.Name, null),
currentIdentity);
PSSenderInfo senderInfo = new PSSenderInfo(userPrincipal, "http://localhost");
senderInfo = new PSSenderInfo(userPrincipal, "http://localhost");
#else
PSPrincipal userPrincipal = new PSPrincipal(new PSIdentity("", true, "", null),
null);
senderInfo = new PSSenderInfo(userPrincipal, "http://localhost");
#endif
OutOfProcessServerSessionTransportManager tm = new OutOfProcessServerSessionTransportManager(originalStdOut, originalStdErr);
OutOfProcessServerSessionTransportManager tm = new OutOfProcessServerSessionTransportManager(originalStdOut, originalStdErr, cryptoHelper);
ServerRemoteSession srvrRemoteSession = ServerRemoteSession.CreateServerRemoteSession(senderInfo,
_initialCommand, tm, configurationName);
@ -311,11 +319,11 @@ namespace System.Management.Automation.Remoting.Server
return tm;
}
protected void Start(string initialCommand, string configurationName = null)
protected void Start(string initialCommand, PSRemotingCryptoHelperServer cryptoHelper, string configurationName = null)
{
_initialCommand = initialCommand;
sessionTM = CreateSessionTransportManager(configurationName);
sessionTM = CreateSessionTransportManager(configurationName, cryptoHelper);
try
{
@ -326,7 +334,7 @@ namespace System.Management.Automation.Remoting.Server
{
if (sessionTM == null)
{
sessionTM = CreateSessionTransportManager(configurationName);
sessionTM = CreateSessionTransportManager(configurationName, cryptoHelper);
}
}
if (string.IsNullOrEmpty(data))
@ -474,7 +482,76 @@ namespace System.Management.Automation.Remoting.Server
// Setup unhandled exception to log events
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(AppDomainUnhandledException);
#endif
s_singletonInstance.Start(initialCommand);
s_singletonInstance.Start(initialCommand, new PSRemotingCryptoHelperServer());
}
#endregion
}
internal sealed class SSHProcessMediator : OutOfProcessMediatorBase
{
#region Private Data
private static SSHProcessMediator s_singletonInstance;
#endregion
#region Constructors
private SSHProcessMediator() : base(true)
{
#if !UNIX
var inputHandle = PlatformInvokes.GetStdHandle((uint)PlatformInvokes.StandardHandleId.Input);
originalStdIn = new StreamReader(
new FileStream(new SafeFileHandle(inputHandle, false), FileAccess.Read));
var outputHandle = PlatformInvokes.GetStdHandle((uint)PlatformInvokes.StandardHandleId.Output);
originalStdOut = new OutOfProcessTextWriter(
new StreamWriter(
new FileStream(new SafeFileHandle(outputHandle, false), FileAccess.Write)));
var errorHandle = PlatformInvokes.GetStdHandle((uint)PlatformInvokes.StandardHandleId.Error);
originalStdErr = new OutOfProcessTextWriter(
new StreamWriter(
new FileStream(new SafeFileHandle(errorHandle, false), FileAccess.Write)));
#else
originalStdIn = new StreamReader(Console.OpenStandardInput(), true);
originalStdOut = new OutOfProcessTextWriter(
new StreamWriter(Console.OpenStandardOutput()));
originalStdErr = new OutOfProcessTextWriter(
new StreamWriter(Console.OpenStandardError()));
#endif
}
#endregion
#region Static Methods
/// <summary>
///
/// </summary>
/// <param name="initialCommand"></param>
internal static void Run(string initialCommand)
{
lock (SyncObject)
{
if (s_singletonInstance != null)
{
Dbg.Assert(false, "Run should not be called multiple times");
return;
}
s_singletonInstance = new SSHProcessMediator();
}
PSRemotingCryptoHelperServer cryptoHelper;
#if !UNIX
cryptoHelper = new PSRemotingCryptoHelperServer();
#else
cryptoHelper = null;
#endif
s_singletonInstance.Start(initialCommand, cryptoHelper);
}
#endregion
@ -552,7 +629,7 @@ namespace System.Management.Automation.Remoting.Server
// AppDomain is not available in CoreCLR
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(AppDomainUnhandledException);
#endif
s_singletonInstance.Start(initialCommand, namedPipeServer.ConfigurationName);
s_singletonInstance.Start(initialCommand, new PSRemotingCryptoHelperServer(), namedPipeServer.ConfigurationName);
}
#endregion
@ -643,7 +720,7 @@ namespace System.Management.Automation.Remoting.Server
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(AppDomainUnhandledException);
#endif
s_instance.Start(initialCommand, configurationName);
s_instance.Start(initialCommand, new PSRemotingCryptoHelperServer(), configurationName);
}
#endregion

View file

@ -132,11 +132,12 @@ namespace System.Management.Automation.Remoting
_senderInfo = senderInfo;
_configProviderId = configurationProviderId;
_initParameters = initializationParameters;
if (Platform.IsWindows)
{
_cryptoHelper = (PSRemotingCryptoHelperServer)transportManager.CryptoHelper;
_cryptoHelper.Session = this;
}
#if !UNIX
_cryptoHelper = (PSRemotingCryptoHelperServer)transportManager.CryptoHelper;
_cryptoHelper.Session = this;
#else
_cryptoHelper = null;
#endif
Context = new ServerRemoteSessionContext();
SessionDataStructureHandler = new ServerRemoteSessionDSHandlerlImpl(this, transportManager);
@ -162,9 +163,9 @@ namespace System.Management.Automation.Remoting
transportManager.ReceivedDataCollection.MaximumReceivedDataSize = null;
}
#endregion Constructors
#endregion Constructors
#region Creation Factory
#region Creation Factory
/// <summary>
/// Creates a server remote session for the supplied <paramref name="configuratioinProviderId"/>
@ -240,9 +241,9 @@ namespace System.Management.Automation.Remoting
return result;
}
#endregion
#endregion
#region Overrides
#region Overrides
/// <summary>
/// This indicates the remote session object is Client, Server or Listener.
@ -457,9 +458,9 @@ namespace System.Management.Automation.Remoting
SessionDataStructureHandler.StateMachine.RaiseEvent(args);
}
#endregion Overrides
#endregion Overrides
#region Properties
#region Properties
/// <summary>
/// This property returns the ServerRemoteSessionContext object created inside
@ -473,9 +474,9 @@ namespace System.Management.Automation.Remoting
/// </summary>
internal ServerRemoteSessionDataStructureHandler SessionDataStructureHandler { get; }
#endregion
#endregion
#region Private/Internal Methods
#region Private/Internal Methods
/// <summary>
/// Let the session clear its resources.
@ -866,7 +867,11 @@ namespace System.Management.Automation.Remoting
_runspacePoolDriver.InstanceId);
}
#if !UNIX
bool isAdministrator = _senderInfo.UserInfo.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator);
#else
bool isAdministrator = false;
#endif
ServerRunspacePoolDriver tmpDriver = new ServerRunspacePoolDriver(
clientRunspacePoolId,
@ -1158,6 +1163,6 @@ namespace System.Management.Automation.Remoting
cmdTransportManager.ReceivedDataCollection.MaximumReceivedObjectSize = _maxRecvdObjectSize;
}
#endregion
#endregion
}
}

View file

@ -1609,4 +1609,7 @@ All WinRM sessions connected to Windows PowerShell session configurations, such
<value> Other Possible Cause:
-The domain or computer name was not included with the specified credential, for example: DOMAIN\UserName or COMPUTER\UserName.</value>
</data>
<data name="CannotStartSSHClient" xml:space="preserve">
<value>An error occurred when starting the SSH.exe client needed for the remoting connection.</value>
</data>
</root>

View file

@ -258,6 +258,13 @@ namespace System.Management.Automation.Security
(System.Security.Principal.WindowsIdentity.GetCurrent().ImpersonationLevel == System.Security.Principal.TokenImpersonationLevel.Impersonation) ?
SaferPolicy.Allowed : SaferPolicy.Disallowed;
}
catch (ArgumentException)
{
// This is for IO.Path.GetTempPath() call when temp paths are not accessible.
result =
(System.Security.Principal.WindowsIdentity.GetCurrent().ImpersonationLevel == System.Security.Principal.TokenImpersonationLevel.Impersonation) ?
SaferPolicy.Allowed : SaferPolicy.Disallowed;
}
finally
{
if (IO.File.Exists(testPathScript)) { IO.File.Delete(testPathScript); }

View file

@ -4,6 +4,7 @@ Copyright (c) Microsoft Corporation. All rights reserved.
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Win32.SafeHandles;
#if CORECLR
// Use stubs for SafeHandleZeroOrMinusOneIsInvalid, SecurityPermissionAttribute and ReliabilityContractAttribute
@ -11,7 +12,6 @@ using Microsoft.PowerShell.CoreClr.Stubs;
#else
using System.Security.Permissions;
using System.Runtime.ConstrainedExecution;
using Microsoft.Win32.SafeHandles;
#endif
namespace System.Management.Automation
@ -552,5 +552,192 @@ namespace System.Management.Automation
internal const uint SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000;
internal const int ERROR_SUCCESS = 0x0;
#region CreateProcess for SSH Remoting
#if !UNIX
// Fields
internal static readonly IntPtr INVALID_HANDLE_VALUE = IntPtr.Zero;
internal static UInt32 GENERIC_READ = 0x80000000;
internal static UInt32 GENERIC_WRITE = 0x40000000;
internal static UInt32 FILE_ATTRIBUTE_NORMAL = 0x80000000;
internal static UInt32 CREATE_ALWAYS = 2;
internal static UInt32 FILE_SHARE_WRITE = 0x00000002;
internal static UInt32 FILE_SHARE_READ = 0x00000001;
internal static UInt32 OF_READWRITE = 0x00000002;
internal static UInt32 OPEN_EXISTING = 3;
[StructLayout(LayoutKind.Sequential)]
internal class PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
public PROCESS_INFORMATION()
{
this.hProcess = IntPtr.Zero;
this.hThread = IntPtr.Zero;
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Dispose(true);
}
/// <summary>
/// Dispose
/// </summary>
/// <param name="disposing"></param>
private void Dispose(bool disposing)
{
if (disposing)
{
if (this.hProcess != IntPtr.Zero)
{
CloseHandle(this.hProcess);
this.hProcess = IntPtr.Zero;
}
if (this.hThread != IntPtr.Zero)
{
CloseHandle(this.hThread);
this.hThread = IntPtr.Zero;
}
}
}
}
[StructLayout(LayoutKind.Sequential)]
internal class STARTUPINFO
{
public int cb;
public IntPtr lpReserved;
public IntPtr lpDesktop;
public IntPtr lpTitle;
public int dwX;
public int dwY;
public int dwXSize;
public int dwYSize;
public int dwXCountChars;
public int dwYCountChars;
public int dwFillAttribute;
public int dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public SafeFileHandle hStdInput;
public SafeFileHandle hStdOutput;
public SafeFileHandle hStdError;
public STARTUPINFO()
{
this.lpReserved = IntPtr.Zero;
this.lpDesktop = IntPtr.Zero;
this.lpTitle = IntPtr.Zero;
this.lpReserved2 = IntPtr.Zero;
this.hStdInput = new SafeFileHandle(IntPtr.Zero, false);
this.hStdOutput = new SafeFileHandle(IntPtr.Zero, false);
this.hStdError = new SafeFileHandle(IntPtr.Zero, false);
this.cb = Marshal.SizeOf(this);
}
public void Dispose(bool disposing)
{
if (disposing)
{
if ((this.hStdInput != null) && !this.hStdInput.IsInvalid)
{
this.hStdInput.Dispose();
this.hStdInput = null;
}
if ((this.hStdOutput != null) && !this.hStdOutput.IsInvalid)
{
this.hStdOutput.Dispose();
this.hStdOutput = null;
}
if ((this.hStdError != null) && !this.hStdError.IsInvalid)
{
this.hStdError.Dispose();
this.hStdError = null;
}
}
}
public void Dispose()
{
Dispose(true);
}
}
[StructLayout(LayoutKind.Sequential)]
internal class SECURITY_ATTRIBUTES
{
public int nLength;
public SafeLocalMemHandle lpSecurityDescriptor;
public bool bInheritHandle;
public SECURITY_ATTRIBUTES()
{
this.nLength = 12;
this.bInheritHandle = true;
this.lpSecurityDescriptor = new SafeLocalMemHandle(IntPtr.Zero, true);
}
}
// Methods
//
[DllImport(PinvokeDllNames.CreateProcessDllName, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool CreateProcess(
[MarshalAs(UnmanagedType.LPWStr)] string lpApplicationName,
[MarshalAs(UnmanagedType.LPWStr)] string lpCommandLine,
SECURITY_ATTRIBUTES lpProcessAttributes,
SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
int dwCreationFlags,
IntPtr lpEnvironment,
[MarshalAs(UnmanagedType.LPWStr)] string lpCurrentDirectory,
STARTUPINFO lpStartupInfo,
PROCESS_INFORMATION lpProcessInformation);
[DllImport(PinvokeDllNames.ResumeThreadDllName, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern uint ResumeThread(IntPtr threadHandle);
[DllImport(PinvokeDllNames.CreateFileDllName, CharSet = CharSet.Unicode, SetLastError = true)]
public static extern System.IntPtr CreateFileW(
[In, MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
UInt32 dwDesiredAccess,
UInt32 dwShareMode,
SECURITY_ATTRIBUTES lpSecurityAttributes,
UInt32 dwCreationDisposition,
UInt32 dwFlagsAndAttributes,
System.IntPtr hTemplateFile);
#endif
#endregion
#region GetStdHandle
#if !UNIX
internal enum StandardHandleId : uint
{
Error = unchecked((uint)-12),
Output = unchecked((uint)-11),
Input = unchecked((uint)-10),
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr GetStdHandle(uint handleId);
#endif
#endregion
}
}