MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Management.Automation.Host;5using System.Management.Automation.Internal;6using System.Management.Automation.Remoting.Client;7using System.Management.Automation.Runspaces.Internal;8 9using Dbg = System.Management.Automation.Diagnostics;10 11namespace System.Management.Automation.Remoting12{13 /// <summary>14 /// Executes methods on the client.15 /// </summary>16 internal sealed class ClientMethodExecutor17 {18 /// <summary>19 /// Transport manager.20 /// </summary>21 private readonly BaseClientTransportManager _transportManager;22 23 /// <summary>24 /// Client host.25 /// </summary>26 private readonly PSHost _clientHost;27 28 /// <summary>29 /// Client runspace pool id.30 /// </summary>31 private readonly Guid _clientRunspacePoolId;32 33 /// <summary>34 /// Client power shell id.35 /// </summary>36 private readonly Guid _clientPowerShellId;37 38 /// <summary>39 /// Remote host call.40 /// </summary>41 private readonly RemoteHostCall _remoteHostCall;42 43 /// <summary>44 /// Remote host call.45 /// </summary>46 internal RemoteHostCall RemoteHostCall47 {48 get49 {50 return _remoteHostCall;51 }52 }53 54 /// <summary>55 /// Constructor for ClientMethodExecutor.56 /// </summary>57 private ClientMethodExecutor(BaseClientTransportManager transportManager, PSHost clientHost, Guid clientRunspacePoolId, Guid clientPowerShellId, RemoteHostCall remoteHostCall)58 {59 Dbg.Assert(transportManager != null, "Expected transportManager != null");60 Dbg.Assert(remoteHostCall != null, "Expected remoteHostCall != null");61 _transportManager = transportManager;62 _remoteHostCall = remoteHostCall;63 _clientHost = clientHost;64 _clientRunspacePoolId = clientRunspacePoolId;65 _clientPowerShellId = clientPowerShellId;66 }67 68 /// <summary>69 /// Create a new ClientMethodExecutor object and then dispatch it.70 /// </summary>71 internal static void Dispatch(72 BaseClientTransportManager transportManager,73 PSHost clientHost,74 PSDataCollectionStream<ErrorRecord> errorStream,75 ObjectStream methodExecutorStream,76 bool isMethodExecutorStreamEnabled,77 RemoteRunspacePoolInternal runspacePool,78 Guid clientPowerShellId,79 RemoteHostCall remoteHostCall)80 {81 ClientMethodExecutor methodExecutor =82 new ClientMethodExecutor(transportManager, clientHost, runspacePool.InstanceId,83 clientPowerShellId, remoteHostCall);84 85 // If the powershell id is not specified, this message is for the runspace pool, execute86 // it immediately and return87 if (clientPowerShellId == Guid.Empty)88 {89 methodExecutor.Execute(errorStream);90 return;91 }92 93 // Check client host to see if SetShouldExit should be allowed94 bool hostAllowSetShouldExit = false;95 if (clientHost != null)96 {97 PSObject hostPrivateData = clientHost.PrivateData as PSObject;98 if (hostPrivateData != null)99 {100 PSNoteProperty allowSetShouldExit = hostPrivateData.Properties["AllowSetShouldExitFromRemote"] as PSNoteProperty;101 hostAllowSetShouldExit = allowSetShouldExit != null && allowSetShouldExit.Value is bool && (bool)allowSetShouldExit.Value;102 }103 }104 105 // Should we kill remote runspace? Check if "SetShouldExit" and if we are in the106 // cmdlet case. In the API case (when we are invoked from an API not a cmdlet) we107 // should not interpret "SetShouldExit" but should pass it on to the host. The108 // variable IsMethodExecutorStreamEnabled is only true in the cmdlet case. In the109 // API case it is false.110 111 if (remoteHostCall.IsSetShouldExit && isMethodExecutorStreamEnabled && !hostAllowSetShouldExit)112 {113 runspacePool.Close();114 return;115 }116 117 // Cmdlet case: queue up the executor in the pipeline stream.118 if (isMethodExecutorStreamEnabled)119 {120 Dbg.Assert(methodExecutorStream != null, "method executor stream can't be null when enabled");121 methodExecutorStream.Write(methodExecutor);122 }123 124 // API case: execute it immediately.125 else126 {127 methodExecutor.Execute(errorStream);128 }129 }130 131 /// <summary>132 /// Is runspace pushed.133 /// </summary>134 private static bool IsRunspacePushed(PSHost host)135 {136 if (host is not IHostSupportsInteractiveSession host2)137 {138 return false;139 }140 141 // IsRunspacePushed can throw (not implemented exception)142 try143 {144 return host2.IsRunspacePushed;145 }146 catch (PSNotImplementedException) { }147 148 return false;149 }150 151 /// <summary>152 /// Execute.153 /// </summary>154 internal void Execute(PSDataCollectionStream<ErrorRecord> errorStream)155 {156 Action<ErrorRecord> writeErrorAction = null;157 158 // If error-stream is null or we are in pushed-runspace - then write error directly to console.159 if (errorStream == null || IsRunspacePushed(_clientHost))160 {161 writeErrorAction = (ErrorRecord errorRecord) =>162 {163 try164 {165 _clientHost.UI?.WriteErrorLine(errorRecord.ToString());166 }167 catch (Exception)168 {169 // Catch-all OK, 3rd party callout.170 }171 };172 }173 174 // Otherwise write it to error-stream.175 else176 {177 writeErrorAction = (ErrorRecord errorRecord) => errorStream.Write(errorRecord);178 }179 180 this.Execute(writeErrorAction);181 }182 183 /// <summary>184 /// Execute.185 /// </summary>186 internal void Execute(Cmdlet cmdlet)187 {188 this.Execute(cmdlet.WriteError);189 }190 191 /// <summary>192 /// Execute.193 /// </summary>194 internal void Execute(Action<ErrorRecord> writeErrorAction)195 {196 if (_remoteHostCall.IsVoidMethod)197 {198 ExecuteVoid(writeErrorAction);199 }200 else201 {202 RemotingDataType remotingDataType =203 _clientPowerShellId == Guid.Empty ? RemotingDataType.RemoteRunspaceHostResponseData : RemotingDataType.RemotePowerShellHostResponseData;204 205 RemoteHostResponse remoteHostResponse = _remoteHostCall.ExecuteNonVoidMethod(_clientHost);206 RemoteDataObject<PSObject> dataToBeSent = RemoteDataObject<PSObject>.CreateFrom(207 RemotingDestination.Server, remotingDataType, _clientRunspacePoolId,208 _clientPowerShellId, remoteHostResponse.Encode());209 210 _transportManager.DataToBeSentCollection.Add<PSObject>(dataToBeSent, DataPriorityType.PromptResponse);211 }212 }213 214 /// <summary>215 /// Execute void.216 /// </summary>217 internal void ExecuteVoid(Action<ErrorRecord> writeErrorAction)218 {219 try220 {221 _remoteHostCall.ExecuteVoidMethod(_clientHost);222 }223 catch (Exception exception)224 {225 // Catch-all OK, 3rd party callout.226 227 // Extract inner exception.228 if (exception.InnerException != null)229 {230 exception = exception.InnerException;231 }232 233 // Create an error record and write it to the stream.234 ErrorRecord errorRecord = new ErrorRecord(235 exception,236 nameof(PSRemotingErrorId.RemoteHostCallFailed),237 ErrorCategory.InvalidArgument,238 _remoteHostCall.MethodName);239 writeErrorAction(errorRecord);240 }241 }242 }243}244 