From 2429b431818f2c13edbb351bb682109e736ce9e3 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Tue, 5 Jun 2018 20:08:49 -0700 Subject: [PATCH] Skip compiling the non-supported cmdlets on Unix in System.Management.Automation.dll (#6939) This is to keep the cache in InitialSessionState in sync with the actual available cmdlet types in System.Management.Automation.dll --- .../System.Management.Automation.csproj | 10 + .../remoting/commands/ConnectPSSession.cs | 423 +--------- .../remoting/commands/CustomShellCommands.cs | 64 +- .../commands/NewPSSessionConfigurationFile.cs | 2 + .../commands/NewPSSessionOptionCommand.cs | 247 ------ .../remoting/commands/PSRemotingCmdlet.cs | 745 +++++++++++++++++- .../engine/remoting/commands/StartJob.cs | 4 +- 7 files changed, 757 insertions(+), 738 deletions(-) diff --git a/src/System.Management.Automation/System.Management.Automation.csproj b/src/System.Management.Automation/System.Management.Automation.csproj index 8d7ff2cfd..a93edb556 100644 --- a/src/System.Management.Automation/System.Management.Automation.csproj +++ b/src/System.Management.Automation/System.Management.Automation.csproj @@ -128,4 +128,14 @@ + + + + + + + + + + diff --git a/src/System.Management.Automation/engine/remoting/commands/ConnectPSSession.cs b/src/System.Management.Automation/engine/remoting/commands/ConnectPSSession.cs index 5af18f536..61133ab63 100644 --- a/src/System.Management.Automation/engine/remoting/commands/ConnectPSSession.cs +++ b/src/System.Management.Automation/engine/remoting/commands/ConnectPSSession.cs @@ -8,8 +8,8 @@ using System.Diagnostics.CodeAnalysis; using System.Management.Automation; using System.Management.Automation.Internal; using System.Management.Automation.Remoting; -using System.Management.Automation.Runspaces; using System.Management.Automation.Remoting.Client; +using System.Management.Automation.Runspaces; using System.Management.Automation.Host; using System.Threading; using Dbg = System.Management.Automation.Diagnostics; @@ -1087,425 +1087,4 @@ namespace Microsoft.PowerShell.Commands #endregion } - - #region QueryRunspaces - - internal class QueryRunspaces - { - #region Constructor - - internal QueryRunspaces() - { - _stopProcessing = false; - } - - #endregion - - #region Internal Methods - - /// - /// Queries all remote computers specified in collection of WSManConnectionInfo objects - /// and returns disconnected PSSession objects ready for connection to server. - /// Returned sessions can be matched to Guids or Names. - /// - /// Collection of WSManConnectionInfo objects. - /// Host for PSSession objects. - /// Out stream object. - /// Runspace repository. - /// Throttle limit. - /// Runspace state filter value. - /// Array of session Guids to match to. - /// Array of session Names to match to. - /// Configuration name to match to. - /// Collection of disconnected PSSession objects. - internal Collection GetDisconnectedSessions(Collection connectionInfos, PSHost host, - ObjectStream stream, RunspaceRepository runspaceRepository, - int throttleLimit, SessionFilterState filterState, - Guid[] matchIds, string[] matchNames, string configurationName) - { - Collection filteredPSSessions = new Collection(); - - // Create a query operation for each connection information object. - foreach (WSManConnectionInfo connectionInfo in connectionInfos) - { - Runspace[] runspaces = null; - - try - { - runspaces = Runspace.GetRunspaces(connectionInfo, host, BuiltInTypesTable); - } - catch (System.Management.Automation.RuntimeException e) - { - if (e.InnerException is InvalidOperationException) - { - // The Get-WSManInstance cmdlet used to query remote computers for runspaces will throw - // an Invalid Operation (inner) exception if the connectInfo object is invalid, including - // invalid computer names. - // We don't want to propagate the exception so just write error here. - if (stream.ObjectWriter != null && stream.ObjectWriter.IsOpen) - { - int errorCode; - string msg = StringUtil.Format(RemotingErrorIdStrings.QueryForRunspacesFailed, connectionInfo.ComputerName, ExtractMessage(e.InnerException, out errorCode)); - string FQEID = WSManTransportManagerUtils.GetFQEIDFromTransportError(errorCode, "RemotePSSessionQueryFailed"); - Exception reason = new RuntimeException(msg, e.InnerException); - ErrorRecord errorRecord = new ErrorRecord(reason, FQEID, ErrorCategory.InvalidOperation, connectionInfo); - stream.ObjectWriter.Write((Action)(cmdlet => cmdlet.WriteError(errorRecord))); - } - } - else - { - throw; - } - } - - if (_stopProcessing) - { - break; - } - - // Add all runspaces meeting filter criteria to collection. - if (runspaces != null) - { - // Convert configuration name into shell Uri for comparison. - string shellUri = null; - if (!string.IsNullOrEmpty(configurationName)) - { - shellUri = (configurationName.IndexOf( - System.Management.Automation.Remoting.Client.WSManNativeApi.ResourceURIPrefix, StringComparison.OrdinalIgnoreCase) != -1) ? - configurationName : System.Management.Automation.Remoting.Client.WSManNativeApi.ResourceURIPrefix + configurationName; - } - - foreach (Runspace runspace in runspaces) - { - // Filter returned runspaces by ConfigurationName if provided. - if (shellUri != null) - { - // Compare with returned shell Uri in connection info. - WSManConnectionInfo wsmanConnectionInfo = runspace.ConnectionInfo as WSManConnectionInfo; - if (wsmanConnectionInfo != null && - !shellUri.Equals(wsmanConnectionInfo.ShellUri, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - } - - // Check the repository for an existing viable PSSession for - // this runspace (based on instanceId). Use the existing - // local runspace instead of the one returned from the server - // query. - PSSession existingPSSession = null; - if (runspaceRepository != null) - { - existingPSSession = runspaceRepository.GetItem(runspace.InstanceId); - } - - if (existingPSSession != null && - UseExistingRunspace(existingPSSession.Runspace, runspace)) - { - if (TestRunspaceState(existingPSSession.Runspace, filterState)) - { - filteredPSSessions.Add(existingPSSession); - } - } - else if (TestRunspaceState(runspace, filterState)) - { - filteredPSSessions.Add(new PSSession(runspace as RemoteRunspace)); - } - } - } - } - - // Return only PSSessions that match provided Ids or Names. - if ((matchIds != null) && (filteredPSSessions.Count > 0)) - { - Collection matchIdsSessions = new Collection(); - foreach (Guid id in matchIds) - { - bool matchFound = false; - foreach (PSSession psSession in filteredPSSessions) - { - if (_stopProcessing) - { - break; - } - - if (psSession.Runspace.InstanceId.Equals(id)) - { - matchFound = true; - matchIdsSessions.Add(psSession); - break; - } - } - - if (!matchFound && stream.ObjectWriter != null && stream.ObjectWriter.IsOpen) - { - string msg = StringUtil.Format(RemotingErrorIdStrings.SessionIdMatchFailed, id); - Exception reason = new RuntimeException(msg); - ErrorRecord errorRecord = new ErrorRecord(reason, "PSSessionIdMatchFail", ErrorCategory.InvalidOperation, id); - stream.ObjectWriter.Write((Action)(cmdlet => cmdlet.WriteError(errorRecord))); - } - } - - // Return all found sessions. - return matchIdsSessions; - } - else if ((matchNames != null) && (filteredPSSessions.Count > 0)) - { - Collection matchNamesSessions = new Collection(); - foreach (string name in matchNames) - { - WildcardPattern namePattern = WildcardPattern.Get(name, WildcardOptions.IgnoreCase); - bool matchFound = false; - foreach (PSSession psSession in filteredPSSessions) - { - if (_stopProcessing) - { - break; - } - - if (namePattern.IsMatch(((RemoteRunspace)psSession.Runspace).RunspacePool.RemoteRunspacePoolInternal.Name)) - { - matchFound = true; - matchNamesSessions.Add(psSession); - } - } - - if (!matchFound && stream.ObjectWriter != null && stream.ObjectWriter.IsOpen) - { - string msg = StringUtil.Format(RemotingErrorIdStrings.SessionNameMatchFailed, name); - Exception reason = new RuntimeException(msg); - ErrorRecord errorRecord = new ErrorRecord(reason, "PSSessionNameMatchFail", ErrorCategory.InvalidOperation, name); - stream.ObjectWriter.Write((Action)(cmdlet => cmdlet.WriteError(errorRecord))); - } - } - - return matchNamesSessions; - } - else - { - // Return all collected sessions. - return filteredPSSessions; - } - } - - /// - /// Returns true if the existing runspace should be returned to the user - /// a. If the existing runspace is not broken - /// b. If the queried runspace is not connected to a different user. - /// - /// - /// - /// - private static bool UseExistingRunspace( - Runspace existingRunspace, - Runspace queriedrunspace) - { - Dbg.Assert(existingRunspace != null, "Invalid parameter."); - Dbg.Assert(queriedrunspace != null, "Invalid parameter."); - - if (existingRunspace.RunspaceStateInfo.State == RunspaceState.Broken) - { - return false; - } - - if (existingRunspace.RunspaceStateInfo.State == RunspaceState.Disconnected && - queriedrunspace.RunspaceAvailability == RunspaceAvailability.Busy) - { - return false; - } - - // Update existing runspace to have latest DisconnectedOn/ExpiresOn data. - existingRunspace.DisconnectedOn = queriedrunspace.DisconnectedOn; - existingRunspace.ExpiresOn = queriedrunspace.ExpiresOn; - - return true; - } - - /// - /// Returns Exception message. If message is WSMan Xml then - /// the WSMan message and error code is extracted and returned. - /// - /// Exception - /// Returned WSMan error code - /// WSMan message - internal static string ExtractMessage( - Exception e, - out int errorCode) - { - errorCode = 0; - - if (e == null || - e.Message == null) - { - return string.Empty; - } - - string rtnMsg = null; - try - { - System.Xml.XmlReaderSettings xmlReaderSettings = InternalDeserializer.XmlReaderSettingsForUntrustedXmlDocument.Clone(); - xmlReaderSettings.MaxCharactersInDocument = 4096; - xmlReaderSettings.MaxCharactersFromEntities = 1024; - xmlReaderSettings.DtdProcessing = System.Xml.DtdProcessing.Prohibit; - - using (System.Xml.XmlReader reader = System.Xml.XmlReader.Create( - new System.IO.StringReader(e.Message), xmlReaderSettings)) - { - while (reader.Read()) - { - if (reader.NodeType == System.Xml.XmlNodeType.Element) - { - if (reader.LocalName.Equals("Message", StringComparison.OrdinalIgnoreCase)) - { - rtnMsg = reader.ReadElementContentAsString(); - } - else if (reader.LocalName.Equals("WSManFault", StringComparison.OrdinalIgnoreCase)) - { - string errorCodeString = reader.GetAttribute("Code"); - if (errorCodeString != null) - { - try - { - // WinRM returns both signed and unsigned 32 bit string values. Convert to signed 32 bit integer. - Int64 eCode = Convert.ToInt64(errorCodeString, System.Globalization.NumberFormatInfo.InvariantInfo); - unchecked - { - errorCode = (int)eCode; - } - } - catch (FormatException) - { } - catch (OverflowException) - { } - } - } - } - } - } - } - catch (System.Xml.XmlException) - { } - - return rtnMsg ?? e.Message; - } - - /// - /// Discontinue all remote server query operations. - /// - internal void StopAllOperations() - { - _stopProcessing = true; - } - - /// - /// Compares the runspace filter state with the runspace state. - /// - /// Runspace object to test. - /// Filter state to compare. - /// Result of test. - public static bool TestRunspaceState(Runspace runspace, SessionFilterState filterState) - { - bool result; - - switch (filterState) - { - case SessionFilterState.All: - result = true; - break; - - case SessionFilterState.Opened: - result = (runspace.RunspaceStateInfo.State == RunspaceState.Opened); - break; - - case SessionFilterState.Closed: - result = (runspace.RunspaceStateInfo.State == RunspaceState.Closed); - break; - - case SessionFilterState.Disconnected: - result = (runspace.RunspaceStateInfo.State == RunspaceState.Disconnected); - break; - - case SessionFilterState.Broken: - result = (runspace.RunspaceStateInfo.State == RunspaceState.Broken); - break; - - default: - Dbg.Assert(false, "Invalid SessionFilterState value."); - result = false; - break; - } - - return result; - } - - /// - /// Returns the default type table for built-in PowerShell types. - /// - internal static TypeTable BuiltInTypesTable - { - get - { - if (s_TypeTable == null) - { - lock (s_SyncObject) - { - if (s_TypeTable == null) - { - s_TypeTable = TypeTable.LoadDefaultTypeFiles(); - } - } - } - - return s_TypeTable; - } - } - - #endregion - - #region Private Members - - private bool _stopProcessing; - - private static readonly object s_SyncObject = new object(); - private static TypeTable s_TypeTable; - - #endregion - } - - #endregion - - # region Public SessionFilterState Enum - - /// - /// Runspace states that can be used as filters for querying remote runspaces. - /// - public enum SessionFilterState - { - /// - /// Return runspaces in any state. - /// - All = 0, - - /// - /// Return runspaces in Opened state. - /// - Opened = 1, - - /// - /// Return runspaces in Disconnected state. - /// - Disconnected = 2, - - /// - /// Return runspaces in Closed state. - /// - Closed = 3, - - /// - /// Return runspaces in Broken state. - /// - Broken = 4 - } - - #endregion } diff --git a/src/System.Management.Automation/engine/remoting/commands/CustomShellCommands.cs b/src/System.Management.Automation/engine/remoting/commands/CustomShellCommands.cs index 2f1abf4e6..c185421e8 100644 --- a/src/System.Management.Automation/engine/remoting/commands/CustomShellCommands.cs +++ b/src/System.Management.Automation/engine/remoting/commands/CustomShellCommands.cs @@ -1498,46 +1498,6 @@ else return result; } - /// - /// Checks if the specified version of PowerShell is installed - /// - /// - internal static void CheckIfPowerShellVersionIsInstalled(Version version) - { - // Check if PowerShell 2.0 is installed - if (version != null && version.Major == 2) - { -#if CORECLR - // PowerShell 2.0 is not available for CoreCLR - throw new ArgumentException( - PSRemotingErrorInvariants.FormatResourceString( - RemotingErrorIdStrings.PowerShellNotInstalled, - version, "PSVersion")); -#else - // Because of app-compat issues, in Win8, we will have PS 2.0 installed by default but not .NET 2.0 - // In such a case, it is not enough if we check just PowerShell registry keys. We also need to check if .NET 2.0 is installed. - try - { - RegistryKey engineKey = PSSnapInReader.GetPSEngineKey(PSVersionInfo.RegistryVersion1Key); - // Also check for .NET 2.0 installation - if (!PsUtils.FrameworkRegistryInstallation.IsFrameworkInstalled(2, 0, 0)) - { - throw new ArgumentException( - PSRemotingErrorInvariants.FormatResourceString( - RemotingErrorIdStrings.NetFrameWorkV2NotInstalled)); - } - } - catch (PSArgumentException) - { - throw new ArgumentException( - PSRemotingErrorInvariants.FormatResourceString( - RemotingErrorIdStrings.PowerShellNotInstalled, - version, "PSVersion")); - } -#endif - } - } - /// /// Takes array of group name string objects and returns a semicolon delimited string. /// @@ -2018,24 +1978,6 @@ else return (Environment.OSVersion.Version >= new Version(6, 2)) ? remoteSDDL_Win8 : remoteSDDL; } - internal static void CheckPSVersion(Version version) - { - // PSVersion value can only be 2.0, 3.0, 4.0, 5.0, or 5.1 - if (version != null) - { - // PSVersion value can only be 2.0, 3.0, 4.0, 5.0, or 5.1 - if (!((version.Major >= 2) && (version.Major <= 4) && (version.Minor == 0)) && - !((version.Major == 5) && (version.Minor <= 1)) - ) - { - throw new ArgumentException( - PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.PSVersionParameterOutOfRange, - version, "PSVersion") - ); - } - } - } - #endregion #region Parameters @@ -2356,10 +2298,10 @@ else get { return psVersion; } set { - CheckPSVersion(value); + RemotingCommandUtils.CheckPSVersion(value); // Check if specified version of PowerShell is installed - PSSessionConfigurationCommandUtilities.CheckIfPowerShellVersionIsInstalled(value); + RemotingCommandUtils.CheckIfPowerShellVersionIsInstalled(value); psVersion = value; isPSVersionSpecified = true; @@ -5244,7 +5186,7 @@ Enable-PSRemoting -force $args[0] -queryForRegisterDefault $args[1] -captionForR SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Medium, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=144298")] public sealed class DisablePSRemotingCommand : PSCmdlet { - # region Private Data + #region Private Data // To Escape { -- {{ // To Escape } -- }} diff --git a/src/System.Management.Automation/engine/remoting/commands/NewPSSessionConfigurationFile.cs b/src/System.Management.Automation/engine/remoting/commands/NewPSSessionConfigurationFile.cs index fdda2b96e..5e5bce96e 100644 --- a/src/System.Management.Automation/engine/remoting/commands/NewPSSessionConfigurationFile.cs +++ b/src/System.Management.Automation/engine/remoting/commands/NewPSSessionConfigurationFile.cs @@ -15,6 +15,7 @@ using System.Globalization; namespace Microsoft.PowerShell.Commands { +#if !UNIX /// /// New-PSSessionConfigurationFile command implementation /// @@ -1064,6 +1065,7 @@ namespace Microsoft.PowerShell.Commands #endregion } +#endif /// /// New-PSRoleCapabilityFile command implementation diff --git a/src/System.Management.Automation/engine/remoting/commands/NewPSSessionOptionCommand.cs b/src/System.Management.Automation/engine/remoting/commands/NewPSSessionOptionCommand.cs index 3252832cf..4252ba038 100644 --- a/src/System.Management.Automation/engine/remoting/commands/NewPSSessionOptionCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/NewPSSessionOptionCommand.cs @@ -8,253 +8,6 @@ using System.Management.Automation; using System.Management.Automation.Remoting; using System.Management.Automation.Runspaces; -namespace System.Management.Automation.Remoting -{ - /// - /// IMPORTANT: proxy configuration is supported for HTTPS only; for HTTP, the direct - /// connection to the server is used - /// - [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] - public enum ProxyAccessType - { - /// - /// ProxyAccessType is not specified. That means Proxy information (ProxyAccessType, ProxyAuthenticationMechanism - /// and ProxyCredential)is not passed to WSMan at all. - /// - None = 0, - /// - /// use the Internet Explorer proxy configuration for the current user. - /// Internet Explorer proxy settings for the current active network connection. - /// This option requires the user profile to be loaded, so the option can - /// be directly used when called within a process that is running under - /// an interactive user account identity; if the client application is running - /// under a user context different than the interactive user, the client - /// application has to explicitly load the user profile prior to using this option. - /// - IEConfig = 1, - /// - /// proxy settings configured for WinHTTP, using the ProxyCfg.exe utility - /// - WinHttpConfig = 2, - /// - /// Force autodetection of proxy - /// - AutoDetect = 4, - /// - /// do not use a proxy server - resolves all host names locally - /// - NoProxyServer = 8 - } - /// - /// Options for a remote PSSession - /// - public sealed class PSSessionOption - { - /// - /// Creates a new instance of - /// - public PSSessionOption() - { - } - - /// - /// The MaximumConnectionRedirectionCount parameter enables the implicit redirection functionality. - /// -1 = no limit - /// 0 = no redirection - /// - public int MaximumConnectionRedirectionCount { get; set; } = WSManConnectionInfo.defaultMaximumConnectionRedirectionCount; - - /// - /// If false, underlying WSMan infrastructure will compress data sent on the network. - /// If true, data will not be compressed. Compression improves performance by - /// reducing the amount of data sent on the network. Compression my require extra - /// memory consumption and CPU usage. In cases where available memory / CPU is less, - /// set this property to "true". - /// By default the value of this property is "false". - /// - public bool NoCompression { get; set; } = false; - - /// - /// If true then Operating System won't load the user profile (i.e. registry keys under HKCU) on the remote server - /// which can result in a faster session creation time. This option won't have any effect if the remote machine has - /// already loaded the profile (i.e. in another session). - /// - public bool NoMachineProfile { get; set; } = false; - - /// - /// By default, ProxyAccessType is None, that means Proxy information (ProxyAccessType, - /// ProxyAuthenticationMechanism and ProxyCredential)is not passed to WSMan at all. - /// - public ProxyAccessType ProxyAccessType { get; set; } = ProxyAccessType.None; - - /// - /// The following is the definition of the input parameter "ProxyAuthentication". - /// This parameter takes a set of authentication methods the user can select - /// from. The available options should be as follows: - /// - Negotiate: Use the default authentication (as defined by the underlying - /// protocol) for establishing a remote connection. - /// - Basic: Use basic authentication for establishing a remote connection - /// - Digest: Use Digest authentication for establishing a remote connection - /// - /// Default is Negotiate. - /// - public AuthenticationMechanism ProxyAuthentication - { - get { return _proxyAuthentication; } - set - { - switch (value) - { - case AuthenticationMechanism.Basic: - case AuthenticationMechanism.Negotiate: - case AuthenticationMechanism.Digest: - _proxyAuthentication = value; - break; - default: - string message = PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.ProxyAmbiguousAuthentication, - value, - AuthenticationMechanism.Basic.ToString(), - AuthenticationMechanism.Negotiate.ToString(), - AuthenticationMechanism.Digest.ToString()); - throw new ArgumentException(message); - } - } - } - private AuthenticationMechanism _proxyAuthentication = AuthenticationMechanism.Negotiate; - - /// - /// The following is the definition of the input parameter "ProxyCredential". - /// - public PSCredential ProxyCredential { get; set; } - - /// - /// When connecting over HTTPS, the client does not validate that the server - /// certificate is signed by a trusted certificate authority (CA). Use only when - /// the remote computer is trusted by other means, for example, if the remote - /// computer is part of a network that is physically secure and isolated or the - /// remote computer is listed as a trusted host in WinRM configuration - /// - public bool SkipCACheck { get; set; } - - /// - /// Indicates that certificate common name (CN) of the server need not match the - /// hostname of the server. Used only in remote operations using https. This - /// option should only be used for trusted machines. - /// - public bool SkipCNCheck { get; set; } - - /// - /// Indicates that certificate common name (CN) of the server need not match the - /// hostname of the server. Used only in remote operations using https. This - /// option should only be used for trusted machines - /// - public bool SkipRevocationCheck { get; set; } - - /// - /// The duration for which PowerShell remoting waits before timing out - /// for any operation. The user would like to tweak this timeout - /// depending on whether he/she is connecting to a machine in the data - /// center or across a slow WAN. - /// - /// Default: 3*60*1000 == 3minutes - /// - public TimeSpan OperationTimeout { get; set; } = TimeSpan.FromMilliseconds(BaseTransportManager.ClientDefaultOperationTimeoutMs); - - /// - /// Specifies that no encryption will be used when doing remote operations over - /// http. Unencrypted traffic is not allowed by default and must be enabled in - /// the local configuration - /// - public bool NoEncryption { get; set; } - - /// - /// Indicates the request is encoded in UTF16 format rather than UTF8 format; - /// UTF8 is the default. - /// - [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "UTF")] - public bool UseUTF16 { get; set; } - - /// - /// Uses Service Principal Name (SPN) along with the Port number during authentication. - /// - [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SPN")] - public bool IncludePortInSPN { get; set; } - - /// - /// Determines how server in disconnected state deals with cached output - /// data when the cache becomes filled. - /// Default value is 'block mode' where command execution is blocked after - /// the server side data cache becomes filled. - /// - public OutputBufferingMode OutputBufferingMode { get; set; } = WSManConnectionInfo.DefaultOutputBufferingMode; - - /// - /// Number of times a connection will be re-attempted when a connection fails due to network - /// issues. - /// - public int MaxConnectionRetryCount { get; set; } = WSManConnectionInfo.DefaultMaxConnectionRetryCount; - - /// - /// Culture that the remote session should use - /// - public CultureInfo Culture { get; set; } - - /// - /// UI culture that the remote session should use - /// - public CultureInfo UICulture { get; set; } - - /// - /// Total data (in bytes) that can be received from a remote machine - /// targeted towards a command. If null, then the size is unlimited. - /// Default is unlimited data. - /// - public Nullable MaximumReceivedDataSizePerCommand { get; set; } - - /// - /// Maximum size (in bytes) of a deserialized object received from a remote machine. - /// If null, then the size is unlimited. Default is 200MB object size. - /// - public Nullable MaximumReceivedObjectSize { get; set; } = 200 << 20; - - /// - /// Application arguments the server can see in - /// - [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public PSPrimitiveDictionary ApplicationArguments { get; set; } - - /// - /// The duration for which PowerShell remoting waits before timing out on a connection to a remote machine. - /// Simply put, the timeout for a remote runspace creation. - /// The user would like to tweak this timeout depending on whether - /// he/she is connecting to a machine in the data center or across a slow WAN. - /// - /// Default: 3 * 60 * 1000 = 3 minutes - /// - public TimeSpan OpenTimeout { get; set; } = TimeSpan.FromMilliseconds(RunspaceConnectionInfo.DefaultOpenTimeout); - - /// - /// The duration for which PowerShell should wait before it times out on cancel operations - /// (close runspace or stop powershell). For instance, when the user hits ctrl-C, - /// New-PSSession cmdlet tries to call a stop on all remote runspaces which are in the Opening state. - /// The user wouldn't mind waiting for 15 seconds, but this should be time bound and of a shorter duration. - /// A high timeout here like 3 minutes will give the user a feeling that the PowerShell client is not responding. - /// - /// Default: 60 * 1000 = 1 minute - /// - public TimeSpan CancelTimeout { get; set; } = TimeSpan.FromMilliseconds(RunspaceConnectionInfo.defaultCancelTimeout); - - /// - /// The duration for which a Runspace on server needs to wait before it declares the client dead and closes itself down. - /// This is especially important as these values may have to be configured differently for enterprise administration - /// and exchange scenarios. - /// - /// Default: -1 -> Use current server value for IdleTimeout. - /// - public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMilliseconds(RunspaceConnectionInfo.DefaultIdleTimeout); - } -} - namespace Microsoft.PowerShell.Commands { /// diff --git a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs index 5af6cf39d..82dd99ea5 100644 --- a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs +++ b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs @@ -2,17 +2,20 @@ // Licensed under the MIT License. using System; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Remoting; -using System.Management.Automation.Runspaces; -using System.Management.Automation.Internal; -using Dbg = System.Management.Automation.Diagnostics; using System.Collections; using System.Collections.ObjectModel; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Host; +using System.Management.Automation.Internal; using System.Management.Automation.Language; +using System.Management.Automation.Runspaces; +using System.Management.Automation.Remoting; +using System.Management.Automation.Remoting.Client; +using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Commands { @@ -3784,5 +3787,735 @@ namespace Microsoft.PowerShell.Commands #endregion + #region QueryRunspaces + + internal class QueryRunspaces + { + #region Constructor + + internal QueryRunspaces() + { + _stopProcessing = false; + } + + #endregion + + #region Internal Methods + + /// + /// Queries all remote computers specified in collection of WSManConnectionInfo objects + /// and returns disconnected PSSession objects ready for connection to server. + /// Returned sessions can be matched to Guids or Names. + /// + /// Collection of WSManConnectionInfo objects. + /// Host for PSSession objects. + /// Out stream object. + /// Runspace repository. + /// Throttle limit. + /// Runspace state filter value. + /// Array of session Guids to match to. + /// Array of session Names to match to. + /// Configuration name to match to. + /// Collection of disconnected PSSession objects. + internal Collection GetDisconnectedSessions(Collection connectionInfos, PSHost host, + ObjectStream stream, RunspaceRepository runspaceRepository, + int throttleLimit, SessionFilterState filterState, + Guid[] matchIds, string[] matchNames, string configurationName) + { + Collection filteredPSSessions = new Collection(); + + // Create a query operation for each connection information object. + foreach (WSManConnectionInfo connectionInfo in connectionInfos) + { + Runspace[] runspaces = null; + + try + { + runspaces = Runspace.GetRunspaces(connectionInfo, host, BuiltInTypesTable); + } + catch (System.Management.Automation.RuntimeException e) + { + if (e.InnerException is InvalidOperationException) + { + // The Get-WSManInstance cmdlet used to query remote computers for runspaces will throw + // an Invalid Operation (inner) exception if the connectInfo object is invalid, including + // invalid computer names. + // We don't want to propagate the exception so just write error here. + if (stream.ObjectWriter != null && stream.ObjectWriter.IsOpen) + { + int errorCode; + string msg = StringUtil.Format(RemotingErrorIdStrings.QueryForRunspacesFailed, connectionInfo.ComputerName, ExtractMessage(e.InnerException, out errorCode)); + string FQEID = WSManTransportManagerUtils.GetFQEIDFromTransportError(errorCode, "RemotePSSessionQueryFailed"); + Exception reason = new RuntimeException(msg, e.InnerException); + ErrorRecord errorRecord = new ErrorRecord(reason, FQEID, ErrorCategory.InvalidOperation, connectionInfo); + stream.ObjectWriter.Write((Action)(cmdlet => cmdlet.WriteError(errorRecord))); + } + } + else + { + throw; + } + } + + if (_stopProcessing) + { + break; + } + + // Add all runspaces meeting filter criteria to collection. + if (runspaces != null) + { + // Convert configuration name into shell Uri for comparison. + string shellUri = null; + if (!string.IsNullOrEmpty(configurationName)) + { + shellUri = (configurationName.IndexOf( + System.Management.Automation.Remoting.Client.WSManNativeApi.ResourceURIPrefix, StringComparison.OrdinalIgnoreCase) != -1) ? + configurationName : System.Management.Automation.Remoting.Client.WSManNativeApi.ResourceURIPrefix + configurationName; + } + + foreach (Runspace runspace in runspaces) + { + // Filter returned runspaces by ConfigurationName if provided. + if (shellUri != null) + { + // Compare with returned shell Uri in connection info. + WSManConnectionInfo wsmanConnectionInfo = runspace.ConnectionInfo as WSManConnectionInfo; + if (wsmanConnectionInfo != null && + !shellUri.Equals(wsmanConnectionInfo.ShellUri, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + } + + // Check the repository for an existing viable PSSession for + // this runspace (based on instanceId). Use the existing + // local runspace instead of the one returned from the server + // query. + PSSession existingPSSession = null; + if (runspaceRepository != null) + { + existingPSSession = runspaceRepository.GetItem(runspace.InstanceId); + } + + if (existingPSSession != null && + UseExistingRunspace(existingPSSession.Runspace, runspace)) + { + if (TestRunspaceState(existingPSSession.Runspace, filterState)) + { + filteredPSSessions.Add(existingPSSession); + } + } + else if (TestRunspaceState(runspace, filterState)) + { + filteredPSSessions.Add(new PSSession(runspace as RemoteRunspace)); + } + } + } + } + + // Return only PSSessions that match provided Ids or Names. + if ((matchIds != null) && (filteredPSSessions.Count > 0)) + { + Collection matchIdsSessions = new Collection(); + foreach (Guid id in matchIds) + { + bool matchFound = false; + foreach (PSSession psSession in filteredPSSessions) + { + if (_stopProcessing) + { + break; + } + + if (psSession.Runspace.InstanceId.Equals(id)) + { + matchFound = true; + matchIdsSessions.Add(psSession); + break; + } + } + + if (!matchFound && stream.ObjectWriter != null && stream.ObjectWriter.IsOpen) + { + string msg = StringUtil.Format(RemotingErrorIdStrings.SessionIdMatchFailed, id); + Exception reason = new RuntimeException(msg); + ErrorRecord errorRecord = new ErrorRecord(reason, "PSSessionIdMatchFail", ErrorCategory.InvalidOperation, id); + stream.ObjectWriter.Write((Action)(cmdlet => cmdlet.WriteError(errorRecord))); + } + } + + // Return all found sessions. + return matchIdsSessions; + } + else if ((matchNames != null) && (filteredPSSessions.Count > 0)) + { + Collection matchNamesSessions = new Collection(); + foreach (string name in matchNames) + { + WildcardPattern namePattern = WildcardPattern.Get(name, WildcardOptions.IgnoreCase); + bool matchFound = false; + foreach (PSSession psSession in filteredPSSessions) + { + if (_stopProcessing) + { + break; + } + + if (namePattern.IsMatch(((RemoteRunspace)psSession.Runspace).RunspacePool.RemoteRunspacePoolInternal.Name)) + { + matchFound = true; + matchNamesSessions.Add(psSession); + } + } + + if (!matchFound && stream.ObjectWriter != null && stream.ObjectWriter.IsOpen) + { + string msg = StringUtil.Format(RemotingErrorIdStrings.SessionNameMatchFailed, name); + Exception reason = new RuntimeException(msg); + ErrorRecord errorRecord = new ErrorRecord(reason, "PSSessionNameMatchFail", ErrorCategory.InvalidOperation, name); + stream.ObjectWriter.Write((Action)(cmdlet => cmdlet.WriteError(errorRecord))); + } + } + + return matchNamesSessions; + } + else + { + // Return all collected sessions. + return filteredPSSessions; + } + } + + /// + /// Returns true if the existing runspace should be returned to the user + /// a. If the existing runspace is not broken + /// b. If the queried runspace is not connected to a different user. + /// + /// + /// + /// + private static bool UseExistingRunspace( + Runspace existingRunspace, + Runspace queriedrunspace) + { + Dbg.Assert(existingRunspace != null, "Invalid parameter."); + Dbg.Assert(queriedrunspace != null, "Invalid parameter."); + + if (existingRunspace.RunspaceStateInfo.State == RunspaceState.Broken) + { + return false; + } + + if (existingRunspace.RunspaceStateInfo.State == RunspaceState.Disconnected && + queriedrunspace.RunspaceAvailability == RunspaceAvailability.Busy) + { + return false; + } + + // Update existing runspace to have latest DisconnectedOn/ExpiresOn data. + existingRunspace.DisconnectedOn = queriedrunspace.DisconnectedOn; + existingRunspace.ExpiresOn = queriedrunspace.ExpiresOn; + + return true; + } + + /// + /// Returns Exception message. If message is WSMan Xml then + /// the WSMan message and error code is extracted and returned. + /// + /// Exception + /// Returned WSMan error code + /// WSMan message + internal static string ExtractMessage( + Exception e, + out int errorCode) + { + errorCode = 0; + + if (e == null || + e.Message == null) + { + return string.Empty; + } + + string rtnMsg = null; + try + { + System.Xml.XmlReaderSettings xmlReaderSettings = InternalDeserializer.XmlReaderSettingsForUntrustedXmlDocument.Clone(); + xmlReaderSettings.MaxCharactersInDocument = 4096; + xmlReaderSettings.MaxCharactersFromEntities = 1024; + xmlReaderSettings.DtdProcessing = System.Xml.DtdProcessing.Prohibit; + + using (System.Xml.XmlReader reader = System.Xml.XmlReader.Create( + new System.IO.StringReader(e.Message), xmlReaderSettings)) + { + while (reader.Read()) + { + if (reader.NodeType == System.Xml.XmlNodeType.Element) + { + if (reader.LocalName.Equals("Message", StringComparison.OrdinalIgnoreCase)) + { + rtnMsg = reader.ReadElementContentAsString(); + } + else if (reader.LocalName.Equals("WSManFault", StringComparison.OrdinalIgnoreCase)) + { + string errorCodeString = reader.GetAttribute("Code"); + if (errorCodeString != null) + { + try + { + // WinRM returns both signed and unsigned 32 bit string values. Convert to signed 32 bit integer. + Int64 eCode = Convert.ToInt64(errorCodeString, System.Globalization.NumberFormatInfo.InvariantInfo); + unchecked + { + errorCode = (int)eCode; + } + } + catch (FormatException) + { } + catch (OverflowException) + { } + } + } + } + } + } + } + catch (System.Xml.XmlException) + { } + + return rtnMsg ?? e.Message; + } + + /// + /// Discontinue all remote server query operations. + /// + internal void StopAllOperations() + { + _stopProcessing = true; + } + + /// + /// Compares the runspace filter state with the runspace state. + /// + /// Runspace object to test. + /// Filter state to compare. + /// Result of test. + public static bool TestRunspaceState(Runspace runspace, SessionFilterState filterState) + { + bool result; + + switch (filterState) + { + case SessionFilterState.All: + result = true; + break; + + case SessionFilterState.Opened: + result = (runspace.RunspaceStateInfo.State == RunspaceState.Opened); + break; + + case SessionFilterState.Closed: + result = (runspace.RunspaceStateInfo.State == RunspaceState.Closed); + break; + + case SessionFilterState.Disconnected: + result = (runspace.RunspaceStateInfo.State == RunspaceState.Disconnected); + break; + + case SessionFilterState.Broken: + result = (runspace.RunspaceStateInfo.State == RunspaceState.Broken); + break; + + default: + Dbg.Assert(false, "Invalid SessionFilterState value."); + result = false; + break; + } + + return result; + } + + /// + /// Returns the default type table for built-in PowerShell types. + /// + internal static TypeTable BuiltInTypesTable + { + get + { + if (s_TypeTable == null) + { + lock (s_SyncObject) + { + if (s_TypeTable == null) + { + s_TypeTable = TypeTable.LoadDefaultTypeFiles(); + } + } + } + + return s_TypeTable; + } + } + + #endregion + + #region Private Members + + private bool _stopProcessing; + + private static readonly object s_SyncObject = new object(); + private static TypeTable s_TypeTable; + + #endregion + } + + #endregion + + #region SessionFilterState Enum + + /// + /// Runspace states that can be used as filters for querying remote runspaces. + /// + public enum SessionFilterState + { + /// + /// Return runspaces in any state. + /// + All = 0, + + /// + /// Return runspaces in Opened state. + /// + Opened = 1, + + /// + /// Return runspaces in Disconnected state. + /// + Disconnected = 2, + + /// + /// Return runspaces in Closed state. + /// + Closed = 3, + + /// + /// Return runspaces in Broken state. + /// + Broken = 4 + } + + #endregion + + #region RemotingCommandUtils + + internal static class RemotingCommandUtils + { + internal static void CheckPSVersion(Version version) + { + // PSVersion value can only be 2.0, 3.0, 4.0, 5.0, or 5.1 + if (version != null) + { + // PSVersion value can only be 2.0, 3.0, 4.0, 5.0, or 5.1 + if (!(version.Major >= 2 && version.Major <= 4 && version.Minor == 0) && + !(version.Major == 5 && version.Minor <= 1)) + { + throw new ArgumentException( + StringUtil.Format(RemotingErrorIdStrings.PSVersionParameterOutOfRange, version, "PSVersion")); + } + } + } + + /// + /// Checks if the specified version of PowerShell is installed + /// + /// + internal static void CheckIfPowerShellVersionIsInstalled(Version version) + { + // Check if PowerShell 2.0 is installed + if (version != null && version.Major == 2) + { +#if CORECLR + // PowerShell 2.0 is not available for CoreCLR + throw new ArgumentException( + PSRemotingErrorInvariants.FormatResourceString( + RemotingErrorIdStrings.PowerShellNotInstalled, + version, "PSVersion")); +#else + // Because of app-compat issues, in Win8, we will have PS 2.0 installed by default but not .NET 2.0 + // In such a case, it is not enough if we check just PowerShell registry keys. We also need to check if .NET 2.0 is installed. + try + { + RegistryKey engineKey = PSSnapInReader.GetPSEngineKey(PSVersionInfo.RegistryVersion1Key); + // Also check for .NET 2.0 installation + if (!PsUtils.FrameworkRegistryInstallation.IsFrameworkInstalled(2, 0, 0)) + { + throw new ArgumentException( + PSRemotingErrorInvariants.FormatResourceString( + RemotingErrorIdStrings.NetFrameWorkV2NotInstalled)); + } + } + catch (PSArgumentException) + { + throw new ArgumentException( + PSRemotingErrorInvariants.FormatResourceString( + RemotingErrorIdStrings.PowerShellNotInstalled, + version, "PSVersion")); + } +#endif + } + } + } + + #endregion + #endregion Helper Classes } + +namespace System.Management.Automation.Remoting +{ + /// + /// IMPORTANT: proxy configuration is supported for HTTPS only; for HTTP, the direct + /// connection to the server is used + /// + [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] + public enum ProxyAccessType + { + /// + /// ProxyAccessType is not specified. That means Proxy information (ProxyAccessType, ProxyAuthenticationMechanism + /// and ProxyCredential)is not passed to WSMan at all. + /// + None = 0, + /// + /// use the Internet Explorer proxy configuration for the current user. + /// Internet Explorer proxy settings for the current active network connection. + /// This option requires the user profile to be loaded, so the option can + /// be directly used when called within a process that is running under + /// an interactive user account identity; if the client application is running + /// under a user context different than the interactive user, the client + /// application has to explicitly load the user profile prior to using this option. + /// + IEConfig = 1, + /// + /// proxy settings configured for WinHTTP, using the ProxyCfg.exe utility + /// + WinHttpConfig = 2, + /// + /// Force autodetection of proxy + /// + AutoDetect = 4, + /// + /// do not use a proxy server - resolves all host names locally + /// + NoProxyServer = 8 + } + /// + /// Options for a remote PSSession + /// + public sealed class PSSessionOption + { + /// + /// Creates a new instance of + /// + public PSSessionOption() + { + } + + /// + /// The MaximumConnectionRedirectionCount parameter enables the implicit redirection functionality. + /// -1 = no limit + /// 0 = no redirection + /// + public int MaximumConnectionRedirectionCount { get; set; } = WSManConnectionInfo.defaultMaximumConnectionRedirectionCount; + + /// + /// If false, underlying WSMan infrastructure will compress data sent on the network. + /// If true, data will not be compressed. Compression improves performance by + /// reducing the amount of data sent on the network. Compression my require extra + /// memory consumption and CPU usage. In cases where available memory / CPU is less, + /// set this property to "true". + /// By default the value of this property is "false". + /// + public bool NoCompression { get; set; } = false; + + /// + /// If true then Operating System won't load the user profile (i.e. registry keys under HKCU) on the remote server + /// which can result in a faster session creation time. This option won't have any effect if the remote machine has + /// already loaded the profile (i.e. in another session). + /// + public bool NoMachineProfile { get; set; } = false; + + /// + /// By default, ProxyAccessType is None, that means Proxy information (ProxyAccessType, + /// ProxyAuthenticationMechanism and ProxyCredential)is not passed to WSMan at all. + /// + public ProxyAccessType ProxyAccessType { get; set; } = ProxyAccessType.None; + + /// + /// The following is the definition of the input parameter "ProxyAuthentication". + /// This parameter takes a set of authentication methods the user can select + /// from. The available options should be as follows: + /// - Negotiate: Use the default authentication (as defined by the underlying + /// protocol) for establishing a remote connection. + /// - Basic: Use basic authentication for establishing a remote connection + /// - Digest: Use Digest authentication for establishing a remote connection + /// + /// Default is Negotiate. + /// + public AuthenticationMechanism ProxyAuthentication + { + get { return _proxyAuthentication; } + set + { + switch (value) + { + case AuthenticationMechanism.Basic: + case AuthenticationMechanism.Negotiate: + case AuthenticationMechanism.Digest: + _proxyAuthentication = value; + break; + default: + string message = PSRemotingErrorInvariants.FormatResourceString(RemotingErrorIdStrings.ProxyAmbiguousAuthentication, + value, + AuthenticationMechanism.Basic.ToString(), + AuthenticationMechanism.Negotiate.ToString(), + AuthenticationMechanism.Digest.ToString()); + throw new ArgumentException(message); + } + } + } + private AuthenticationMechanism _proxyAuthentication = AuthenticationMechanism.Negotiate; + + /// + /// The following is the definition of the input parameter "ProxyCredential". + /// + public PSCredential ProxyCredential { get; set; } + + /// + /// When connecting over HTTPS, the client does not validate that the server + /// certificate is signed by a trusted certificate authority (CA). Use only when + /// the remote computer is trusted by other means, for example, if the remote + /// computer is part of a network that is physically secure and isolated or the + /// remote computer is listed as a trusted host in WinRM configuration + /// + public bool SkipCACheck { get; set; } + + /// + /// Indicates that certificate common name (CN) of the server need not match the + /// hostname of the server. Used only in remote operations using https. This + /// option should only be used for trusted machines. + /// + public bool SkipCNCheck { get; set; } + + /// + /// Indicates that certificate common name (CN) of the server need not match the + /// hostname of the server. Used only in remote operations using https. This + /// option should only be used for trusted machines + /// + public bool SkipRevocationCheck { get; set; } + + /// + /// The duration for which PowerShell remoting waits before timing out + /// for any operation. The user would like to tweak this timeout + /// depending on whether he/she is connecting to a machine in the data + /// center or across a slow WAN. + /// + /// Default: 3*60*1000 == 3minutes + /// + public TimeSpan OperationTimeout { get; set; } = TimeSpan.FromMilliseconds(BaseTransportManager.ClientDefaultOperationTimeoutMs); + + /// + /// Specifies that no encryption will be used when doing remote operations over + /// http. Unencrypted traffic is not allowed by default and must be enabled in + /// the local configuration + /// + public bool NoEncryption { get; set; } + + /// + /// Indicates the request is encoded in UTF16 format rather than UTF8 format; + /// UTF8 is the default. + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "UTF")] + public bool UseUTF16 { get; set; } + + /// + /// Uses Service Principal Name (SPN) along with the Port number during authentication. + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SPN")] + public bool IncludePortInSPN { get; set; } + + /// + /// Determines how server in disconnected state deals with cached output + /// data when the cache becomes filled. + /// Default value is 'block mode' where command execution is blocked after + /// the server side data cache becomes filled. + /// + public OutputBufferingMode OutputBufferingMode { get; set; } = WSManConnectionInfo.DefaultOutputBufferingMode; + + /// + /// Number of times a connection will be re-attempted when a connection fails due to network + /// issues. + /// + public int MaxConnectionRetryCount { get; set; } = WSManConnectionInfo.DefaultMaxConnectionRetryCount; + + /// + /// Culture that the remote session should use + /// + public CultureInfo Culture { get; set; } + + /// + /// UI culture that the remote session should use + /// + public CultureInfo UICulture { get; set; } + + /// + /// Total data (in bytes) that can be received from a remote machine + /// targeted towards a command. If null, then the size is unlimited. + /// Default is unlimited data. + /// + public Nullable MaximumReceivedDataSizePerCommand { get; set; } + + /// + /// Maximum size (in bytes) of a deserialized object received from a remote machine. + /// If null, then the size is unlimited. Default is 200MB object size. + /// + public Nullable MaximumReceivedObjectSize { get; set; } = 200 << 20; + + /// + /// Application arguments the server can see in + /// + [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public PSPrimitiveDictionary ApplicationArguments { get; set; } + + /// + /// The duration for which PowerShell remoting waits before timing out on a connection to a remote machine. + /// Simply put, the timeout for a remote runspace creation. + /// The user would like to tweak this timeout depending on whether + /// he/she is connecting to a machine in the data center or across a slow WAN. + /// + /// Default: 3 * 60 * 1000 = 3 minutes + /// + public TimeSpan OpenTimeout { get; set; } = TimeSpan.FromMilliseconds(RunspaceConnectionInfo.DefaultOpenTimeout); + + /// + /// The duration for which PowerShell should wait before it times out on cancel operations + /// (close runspace or stop powershell). For instance, when the user hits ctrl-C, + /// New-PSSession cmdlet tries to call a stop on all remote runspaces which are in the Opening state. + /// The user wouldn't mind waiting for 15 seconds, but this should be time bound and of a shorter duration. + /// A high timeout here like 3 minutes will give the user a feeling that the PowerShell client is not responding. + /// + /// Default: 60 * 1000 = 1 minute + /// + public TimeSpan CancelTimeout { get; set; } = TimeSpan.FromMilliseconds(RunspaceConnectionInfo.defaultCancelTimeout); + + /// + /// The duration for which a Runspace on server needs to wait before it declares the client dead and closes itself down. + /// This is especially important as these values may have to be configured differently for enterprise administration + /// and exchange scenarios. + /// + /// Default: -1 -> Use current server value for IdleTimeout. + /// + public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMilliseconds(RunspaceConnectionInfo.DefaultIdleTimeout); + } +} diff --git a/src/System.Management.Automation/engine/remoting/commands/StartJob.cs b/src/System.Management.Automation/engine/remoting/commands/StartJob.cs index 9ab6c3302..ab2bc5485 100644 --- a/src/System.Management.Automation/engine/remoting/commands/StartJob.cs +++ b/src/System.Management.Automation/engine/remoting/commands/StartJob.cs @@ -466,10 +466,10 @@ namespace Microsoft.PowerShell.Commands get { return _psVersion; } set { - PSSessionConfigurationCommandBase.CheckPSVersion(value); + RemotingCommandUtils.CheckPSVersion(value); // Check if specified version of PowerShell is installed - PSSessionConfigurationCommandUtilities.CheckIfPowerShellVersionIsInstalled(value); + RemotingCommandUtils.CheckIfPowerShellVersionIsInstalled(value); _psVersion = value; }