MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Collections.ObjectModel;6using System.Management.Automation.Host;7using System.Management.Automation.Internal;8using System.Management.Automation.Remoting;9 10using Dbg = System.Management.Automation.Diagnostics;11 12namespace System.Management.Automation.Runspaces.Internal13{14 /// <summary>15 /// PowerShell client side proxy base which handles invocation16 /// of powershell on a remote machine.17 /// </summary>18 internal sealed class ClientRemotePowerShell : IDisposable19 {20 #region Tracer21 22 [TraceSource("CRPS", "ClientRemotePowerShell")]23 private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("CRPS", "ClientRemotePowerShellBase");24 25 #endregion Tracer26 27 #region Constructors28 29 /// <summary>30 /// Constructor which creates a client remote powershell.31 /// </summary>32 /// <param name="shell">Powershell instance.</param>33 /// <param name="runspacePool">The runspace pool associated with34 /// this shell</param>35 internal ClientRemotePowerShell(PowerShell shell, RemoteRunspacePoolInternal runspacePool)36 {37 this.shell = shell;38 clientRunspacePoolId = runspacePool.InstanceId;39 this.runspacePool = runspacePool;40 41 // retrieve the computer name from the runspacepool42 // information so that it can be used in adding43 // warning to host messages44 computerName = runspacePool.ConnectionInfo.ComputerName;45 }46 47 #endregion Constructors48 49 #region Internal Methods/Properties50 51 /// <summary>52 /// Instance Id associated with this53 /// client remote powershell.54 /// </summary>55 internal Guid InstanceId56 {57 get58 {59 return PowerShell.InstanceId;60 }61 }62 63 /// <summary>64 /// PowerShell associated with this ClientRemotePowerShell.65 /// </summary>66 internal PowerShell PowerShell67 {68 get69 {70 return shell;71 }72 }73 74 /// <summary>75 /// Set the state information of the client powershell.76 /// </summary>77 /// <param name="stateInfo">State information to set.</param>78 internal void SetStateInfo(PSInvocationStateInfo stateInfo)79 {80 shell.SetStateChanged(stateInfo);81 }82 83 /// <summary>84 /// Whether input is available when this object is created.85 /// </summary>86 internal bool NoInput87 {88 get89 {90 return noInput;91 }92 }93 94 /// <summary>95 /// Input stream associated with this object.96 /// </summary>97 internal ObjectStreamBase InputStream98 {99 get100 {101 return inputstream;102 }103 104 set105 {106 inputstream = value;107 108 if (inputstream != null && (inputstream.IsOpen || inputstream.Count > 0))109 {110 noInput = false;111 }112 else113 {114 noInput = true;115 }116 }117 }118 119 /// <summary>120 /// Output stream associated with this object.121 /// </summary>122 internal ObjectStreamBase OutputStream123 {124 get125 {126 return outputstream;127 }128 129 set130 {131 outputstream = value;132 }133 }134 135 /// <summary>136 /// Data structure handler object.137 /// </summary>138 internal ClientPowerShellDataStructureHandler DataStructureHandler139 {140 get141 {142 return dataStructureHandler;143 }144 }145 146 /// <summary>147 /// Invocation settings associated with this148 /// ClientRemotePowerShell.149 /// </summary>150 internal PSInvocationSettings Settings151 {152 get153 {154 return settings;155 }156 }157 158 /// <summary>159 /// Close the output, error and other collections160 /// associated with the shell, so that the161 /// enumerator does not block.162 /// </summary>163 internal void UnblockCollections()164 {165 shell.ClearRemotePowerShell();166 167 outputstream.Close();168 errorstream.Close();169 inputstream?.Close();170 }171 172 /// <summary>173 /// Stop the remote powershell asynchronously.174 /// </summary>175 /// <remarks>This method will be called from176 /// within the lock on PowerShell. Hence no need177 /// to lock</remarks>178 internal void StopAsync()179 {180 // If we are in robust connection retry mode then auto-disconnect this command181 // rather than try to stop it.182 PSConnectionRetryStatus retryStatus = _connectionRetryStatus;183 if ((retryStatus == PSConnectionRetryStatus.NetworkFailureDetected ||184 retryStatus == PSConnectionRetryStatus.ConnectionRetryAttempt) &&185 this.runspacePool.RunspacePoolStateInfo.State == RunspacePoolState.Opened)186 {187 // While in robust connection retry mode, this call forces robust connections188 // to abort retries and go directly to auto-disconnect.189 this.runspacePool.BeginDisconnect(null, null);190 return;191 }192 193 // PowerShell CoreStop would have handled cases194 // for NotStarted, Stopping and already Stopped195 // so at this point, there is no need to make any196 // check. The message simply needs to be sent197 // across to the server198 stopCalled = true;199 dataStructureHandler.SendStopPowerShellMessage();200 }201 202 /// <summary>203 /// </summary>204 internal void SendInput()205 {206 dataStructureHandler.SendInput(this.inputstream);207 }208 209 /// <summary>210 /// This event is raised, when a host call is for a remote pipeline211 /// which this remote powershell wraps.212 /// </summary>213 internal event EventHandler<RemoteDataEventArgs<RemoteHostCall>> HostCallReceived;214 215 /// <summary>216 /// Initialize the client remote powershell instance.217 /// </summary>218 /// <param name="inputstream">Input for execution.</param>219 /// <param name="errorstream">error stream to which220 /// data needs to be written to</param>221 /// <param name="informationalBuffers">informational buffers222 /// which will hold debug, verbose and warning messages</param>223 /// <param name="settings">settings based on which this powershell224 /// needs to be executed</param>225 /// <param name="outputstream">output stream to which data226 /// needs to be written to</param>227 internal void Initialize(228 ObjectStreamBase inputstream, ObjectStreamBase outputstream,229 ObjectStreamBase errorstream, PSInformationalBuffers informationalBuffers,230 PSInvocationSettings settings)231 {232 initialized = true;233 this.informationalBuffers = informationalBuffers;234 InputStream = inputstream;235 this.errorstream = errorstream;236 this.outputstream = outputstream;237 this.settings = settings;238 239 if (settings == null || settings.Host == null)240 {241 hostToUse = runspacePool.Host;242 }243 else244 {245 hostToUse = settings.Host;246 }247 248 dataStructureHandler = runspacePool.DataStructureHandler.CreatePowerShellDataStructureHandler(this);249 250 // register for events from the data structure handler251 dataStructureHandler.InvocationStateInfoReceived += HandleInvocationStateInfoReceived;252 dataStructureHandler.OutputReceived += HandleOutputReceived;253 dataStructureHandler.ErrorReceived += HandleErrorReceived;254 dataStructureHandler.InformationalMessageReceived += HandleInformationalMessageReceived;255 dataStructureHandler.HostCallReceived += HandleHostCallReceived;256 dataStructureHandler.ClosedNotificationFromRunspacePool += HandleCloseNotificationFromRunspacePool;257 dataStructureHandler.BrokenNotificationFromRunspacePool += HandleBrokenNotificationFromRunspacePool;258 dataStructureHandler.ConnectCompleted += HandleConnectCompleted;259 dataStructureHandler.ReconnectCompleted += HandleConnectCompleted;260 dataStructureHandler.RobustConnectionNotification += HandleRobustConnectionNotification;261 dataStructureHandler.CloseCompleted += HandleCloseCompleted;262 }263 264 /// <summary>265 /// Do any clean up operation per initialization here.266 /// </summary>267 internal void Clear()268 {269 initialized = false;270 }271 272 /// <summary>273 /// If this client remote powershell has been initialized.274 /// </summary>275 internal bool Initialized276 {277 get278 {279 return initialized;280 }281 }282 283 /// <summary>284 /// </summary>285 /// <param name="sender"></param>286 /// <param name="eventArgs"></param>287 internal static void ExitHandler(object sender, RemoteDataEventArgs<RemoteHostCall> eventArgs)288 {289 RemoteHostCall hostcall = eventArgs.Data;290 291 if (hostcall.IsSetShouldExitOrPopRunspace)292 {293 return;294 }295 296 // use the method from the RemotePowerShell to indeed execute this call297 ClientRemotePowerShell remotePowerShell = (ClientRemotePowerShell)sender;298 299 remotePowerShell.ExecuteHostCall(hostcall);300 }301 302 /// <summary>303 /// Attempts to reconnect or connect to a running command on a remote server,304 /// which will resume events and data collection from the server.305 /// If connectCmdInfo parameter is null then a reconnection is attempted and306 /// it is assumed that the current client state is unchanged since disconnection.307 /// If connectCmdInfo parameter is non-null then a connection is attempted to308 /// the specified remote running command.309 /// This is an asynchronous call and results will be reported in the ReconnectCompleted310 /// or the ConnectCompleted call back as appropriate.311 /// </summary>312 /// <param name="connectCmdInfo">ConnectCommandInfo specifying remote command.</param>313 internal void ConnectAsync(ConnectCommandInfo connectCmdInfo)314 {315 if (connectCmdInfo == null)316 {317 // Attempt to do a reconnect with the current PSRP client state.318 this.dataStructureHandler.ReconnectAsync();319 }320 else321 {322 // First add this command DS handler to the remote runspace pool list.323 Dbg.Assert(this.shell.RunspacePool != null, "Invalid runspace pool for this powershell object.");324 this.shell.RunspacePool.RemoteRunspacePoolInternal.AddRemotePowerShellDSHandler(325 this.InstanceId, this.dataStructureHandler);326 327 // Now do the asynchronous connect.328 this.dataStructureHandler.ConnectAsync();329 }330 }331 332 /// <summary>333 /// This event is fired when this PowerShell object receives a robust connection334 /// notification from the transport.335 /// </summary>336 internal event EventHandler<PSConnectionRetryStatusEventArgs> RCConnectionNotification;337 338 /// <summary>339 /// Current remote connection retry status.340 /// </summary>341 internal PSConnectionRetryStatus ConnectionRetryStatus342 {343 get { return _connectionRetryStatus; }344 }345 346 #endregion Internal Methods/Properties347 348 #region Private Methods349 350 /// <summary>351 /// An error record is received from the powershell at the352 /// server side. It is added to the error collection of the353 /// client powershell.354 /// </summary>355 /// <param name="sender">Sender of this event, unused.</param>356 /// <param name="eventArgs">Arguments describing this event.</param>357 private void HandleErrorReceived(object sender, RemoteDataEventArgs<ErrorRecord> eventArgs)358 {359 using (s_tracer.TraceEventHandlers())360 {361 shell.SetHadErrors(true);362 errorstream.Write(eventArgs.Data);363 }364 }365 366 /// <summary>367 /// An output object is received from the powershell at the368 /// server side. It is added to the output collection of the369 /// client powershell.370 /// </summary>371 /// <param name="sender">Sender of this event, unused.</param>372 /// <param name="eventArgs">Arguments describing this event.</param>373 private void HandleOutputReceived(object sender, RemoteDataEventArgs<object> eventArgs)374 {375 using (s_tracer.TraceEventHandlers())376 {377 object data = eventArgs.Data;378 379 try380 {381 outputstream.Write(data);382 }383 catch (PSInvalidCastException e)384 {385 shell.SetStateChanged(new PSInvocationStateInfo(PSInvocationState.Failed, e));386 }387 }388 }389 390 /// <summary>391 /// The invocation state of the server powershell has changed.392 /// The state of the client powershell is reflected accordingly.393 /// </summary>394 /// <param name="sender">Sender of this event, unused.</param>395 /// <param name="eventArgs">Arguments describing this event.</param>396 private void HandleInvocationStateInfoReceived(object sender,397 RemoteDataEventArgs<PSInvocationStateInfo> eventArgs)398 {399 using (s_tracer.TraceEventHandlers())400 {401 PSInvocationStateInfo stateInfo = eventArgs.Data;402 403 // we should not receive any transient state from404 // the server405 Dbg.Assert(!(stateInfo.State == PSInvocationState.Running ||406 stateInfo.State == PSInvocationState.Stopping),407 "Transient states should not be received from the server");408 409 if (stateInfo.State == PSInvocationState.Disconnected)410 {411 SetStateInfo(stateInfo);412 }413 else if (stateInfo.State == PSInvocationState.Stopped ||414 stateInfo.State == PSInvocationState.Failed ||415 stateInfo.State == PSInvocationState.Completed)416 {417 // Special case for failure error due to ErrorCode==-2144108453 (no ShellId found).418 // In this case terminate session since there is no longer a shell to communicate419 // with.420 bool terminateSession = false;421 if (stateInfo.State == PSInvocationState.Failed)422 {423 PSRemotingTransportException remotingTransportException = stateInfo.Reason as PSRemotingTransportException;424 terminateSession = (remotingTransportException != null) &&425 (remotingTransportException.ErrorCode == System.Management.Automation.Remoting.Client.WSManNativeApi.ERROR_WSMAN_TARGETSESSION_DOESNOTEXIST);426 }427 428 // if state is completed or failed or stopped,429 // then the collections need to be closed as430 // well, else the enumerator will block431 UnblockCollections();432 433 if (stopCalled || terminateSession)434 {435 // Reset stop called flag.436 stopCalled = false;437 438 // if a Stop method has been called, then powershell439 // would have already raised a Stopping event, after440 // which only a Stopped should be raised441 _stateInfoQueue.Enqueue(new PSInvocationStateInfo(PSInvocationState.Stopped,442 stateInfo.Reason));443 444 // If the stop call failed due to network problems then close the runspace445 // since it is now unusable.446 CheckAndCloseRunspaceAfterStop(stateInfo.Reason);447 }448 else449 {450 _stateInfoQueue.Enqueue(stateInfo);451 }452 // calling close async only after making sure all the internal members are prepared453 // to handle close complete.454 dataStructureHandler.CloseConnectionAsync(null);455 }456 }457 }458 459 /// <summary>460 /// Helper method to check any error condition after a stop call461 /// and close the remote runspace/pool if the stop call failed due462 /// to network outage problems.463 /// </summary>464 /// <param name="ex">Exception.</param>465 private void CheckAndCloseRunspaceAfterStop(Exception ex)466 {467 PSRemotingTransportException transportException = ex as PSRemotingTransportException;468 if (transportException != null &&469 (transportException.ErrorCode == System.Management.Automation.Remoting.Client.WSManNativeApi.ERROR_WSMAN_SENDDATA_CANNOT_CONNECT ||470 transportException.ErrorCode == System.Management.Automation.Remoting.Client.WSManNativeApi.ERROR_WSMAN_SENDDATA_CANNOT_COMPLETE ||471 transportException.ErrorCode == System.Management.Automation.Remoting.Client.WSManNativeApi.ERROR_WSMAN_TARGETSESSION_DOESNOTEXIST))472 {473 object rsObject = shell.GetRunspaceConnection();474 if (rsObject is Runspace)475 {476 Runspace runspace = (Runspace)rsObject;477 if (runspace.RunspaceStateInfo.State == RunspaceState.Opened)478 {479 try480 {481 runspace.Close();482 }483 catch (PSRemotingTransportException)484 { }485 }486 }487 else if (rsObject is RunspacePool)488 {489 RunspacePool runspacePool = (RunspacePool)rsObject;490 if (runspacePool.RunspacePoolStateInfo.State == RunspacePoolState.Opened)491 {492 try493 {494 runspacePool.Close();495 }496 catch (PSRemotingTransportException)497 { }498 }499 }500 }501 }502 503 /// <summary>504 /// Handler for handling any informational message received505 /// from the server side.506 /// </summary>507 /// <param name="sender">Sender of this event, unused.</param>508 /// <param name="eventArgs">Arguments describing this event.</param>509 private void HandleInformationalMessageReceived(object sender,510 RemoteDataEventArgs<InformationalMessage> eventArgs)511 {512 using (s_tracer.TraceEventHandlers())513 {514 InformationalMessage infoMessage = eventArgs.Data;515 516 switch (infoMessage.DataType)517 {518 case RemotingDataType.PowerShellDebug:519 {520 informationalBuffers.AddDebug((DebugRecord)infoMessage.Message);521 }522 523 break;524 525 case RemotingDataType.PowerShellVerbose:526 {527 informationalBuffers.AddVerbose((VerboseRecord)infoMessage.Message);528 }529 530 break;531 532 case RemotingDataType.PowerShellWarning:533 {534 informationalBuffers.AddWarning((WarningRecord)infoMessage.Message);535 }536 537 break;538 539 case RemotingDataType.PowerShellProgress:540 {541 ProgressRecord progress = (ProgressRecord)LanguagePrimitives.ConvertTo(infoMessage.Message,542 typeof(ProgressRecord), System.Globalization.CultureInfo.InvariantCulture);543 informationalBuffers.AddProgress(progress);544 }545 546 break;547 548 case RemotingDataType.PowerShellInformationStream:549 {550 informationalBuffers.AddInformation((InformationRecord)infoMessage.Message);551 }552 553 break;554 }555 }556 }557 558 /// <summary>559 /// </summary>560 /// <param name="sender"></param>561 /// <param name="eventArgs"></param>562 private void HandleHostCallReceived(object sender, RemoteDataEventArgs<RemoteHostCall> eventArgs)563 {564 using (s_tracer.TraceEventHandlers())565 {566 Collection<RemoteHostCall> prerequisiteCalls =567 eventArgs.Data.PerformSecurityChecksOnHostMessage(computerName);568 569 if (HostCallReceived != null)570 {571 // raise events for all prerequisite calls572 if (prerequisiteCalls.Count > 0)573 {574 foreach (RemoteHostCall hostcall in prerequisiteCalls)575 {576 RemoteDataEventArgs<RemoteHostCall> args =577 new RemoteDataEventArgs<RemoteHostCall>(hostcall);578 579 HostCallReceived.SafeInvoke(this, args);580 }581 }582 583 HostCallReceived.SafeInvoke(this, eventArgs);584 }585 else586 {587 // execute any prerequisite calls before588 // executing this host call589 if (prerequisiteCalls.Count > 0)590 {591 foreach (RemoteHostCall hostcall in prerequisiteCalls)592 {593 ExecuteHostCall(hostcall);594 }595 }596 597 ExecuteHostCall(eventArgs.Data);598 }599 }600 }601 602 /// <summary>603 /// Handler for ConnectCompleted and ReconnectCompleted events from the604 /// PSRP layer.605 /// </summary>606 /// <param name="sender">Sender of this event, unused.</param>607 /// <param name="e">Event arguments.</param>608 private void HandleConnectCompleted(object sender, RemoteDataEventArgs<Exception> e)609 {610 // After initial connect/reconnect set state to "Running". Later events611 // will update state to appropriate command execution state.612 SetStateInfo(new PSInvocationStateInfo(PSInvocationState.Running, null));613 }614 615 /// <summary>616 /// This is need for the state change events that resulted in closing the underlying617 /// datastructure handler. We cannot send the state back to the upper layers until618 /// close is completed from the datastructure/transport layer. We have to send619 /// the terminal state only when we know that underlying datastructure/transport620 /// is closed.621 /// </summary>622 /// <param name="sender"></param>623 /// <param name="args"></param>624 private void HandleCloseCompleted(object sender, EventArgs args)625 {626 // if state is completed or failed or stopped,627 // then the collections need to be closed as628 // well, else the enumerator will block629 UnblockCollections();630 631 // close the transport manager when CreateCloseAckPacket is received632 // otherwise may have race conditions in Server.OutOfProcessMediator633 dataStructureHandler.RaiseRemoveAssociationEvent();634 635 if (_stateInfoQueue.Count == 0)636 {637 // If shell state is not finished on client side and queue is empty638 // then set state to stopped unless the current state is Disconnected639 // in which case transition state to failed.640 if (!IsFinished(shell.InvocationStateInfo.State))641 {642 // If RemoteSessionStateEventArgs are provided then use them to set the643 // session close reason when setting finished state.644 RemoteSessionStateEventArgs sessionEventArgs = args as RemoteSessionStateEventArgs;645 Exception closeReason = sessionEventArgs?.SessionStateInfo.Reason;646 PSInvocationState finishedState = (shell.InvocationStateInfo.State == PSInvocationState.Disconnected) ?647 PSInvocationState.Failed : PSInvocationState.Stopped;648 649 SetStateInfo(new PSInvocationStateInfo(finishedState, closeReason));650 }651 }652 else653 {654 // Apply queued state changes.655 while (_stateInfoQueue.Count > 0)656 {657 PSInvocationStateInfo stateInfo = _stateInfoQueue.Dequeue();658 SetStateInfo(stateInfo);659 }660 }661 }662 663 private static bool IsFinished(PSInvocationState state)664 {665 return (state == PSInvocationState.Completed ||666 state == PSInvocationState.Failed ||667 state == PSInvocationState.Stopped);668 }669 670 /// <summary>671 /// Execute the specified host call.672 /// </summary>673 /// <param name="hostcall">Host call to execute.</param>674 private void ExecuteHostCall(RemoteHostCall hostcall)675 {676 if (hostcall.IsVoidMethod)677 {678 if (hostcall.IsSetShouldExitOrPopRunspace)679 {680 this.shell.ClearRemotePowerShell();681 }682 683 hostcall.ExecuteVoidMethod(hostToUse);684 }685 else686 {687 RemoteHostResponse remoteHostResponse = hostcall.ExecuteNonVoidMethod(hostToUse);688 dataStructureHandler.SendHostResponseToServer(remoteHostResponse);689 }690 }691 692 /// <summary>693 /// </summary>694 /// <param name="sender"></param>695 /// <param name="eventArgs"></param>696 private void HandleCloseNotificationFromRunspacePool(object sender,697 RemoteDataEventArgs<Exception> eventArgs)698 {699 // RunspacePool is closed...so going to set the state of PowerShell700 // to stopped here.701 702 // if state is completed or failed or stopped,703 // then the collections need to be closed as704 // well, else the enumerator will block705 UnblockCollections();706 707 // Since this is a terminal state..close the transport manager.708 dataStructureHandler.RaiseRemoveAssociationEvent();709 710 // RunspacePool is closed...so going to set the state of PowerShell711 // to stopped here.712 SetStateInfo(new PSInvocationStateInfo(PSInvocationState.Stopped,713 eventArgs.Data));714 715 // Not calling dataStructureHandler.CloseConnection() as this must716 // have already been called by RunspacePool.Close()717 }718 719 /// <summary>720 /// Handles notification from RunspacePool indicating721 /// that the pool is broken. This sets the state of722 /// all the powershell objects associated with the723 /// runspace pool to Failed.724 /// </summary>725 /// <param name="sender">Sender of this information, unused.</param>726 /// <param name="eventArgs">arguments describing this event727 /// contains information on the reason associated with the728 /// runspace pool entering a Broken state</param>729 private void HandleBrokenNotificationFromRunspacePool(object sender,730 RemoteDataEventArgs<Exception> eventArgs)731 {732 // RunspacePool is closed...so going to set the state of PowerShell733 // to stopped here.734 735 // if state is completed or failed or stopped,736 // then the collections need to be closed as737 // well, else the enumerator will block738 UnblockCollections();739 740 // Since this is a terminal state..close the transport manager.741 dataStructureHandler.RaiseRemoveAssociationEvent();742 if (stopCalled)743 {744 // Reset stop called flag.745 stopCalled = false;746 747 // if a Stop method has been called, then powershell748 // would have already raised a Stopping event, after749 // which only a Stopped should be raised750 SetStateInfo(new PSInvocationStateInfo(PSInvocationState.Stopped,751 eventArgs.Data));752 }753 else754 {755 SetStateInfo(new PSInvocationStateInfo(PSInvocationState.Failed,756 eventArgs.Data));757 }758 759 // Not calling dataStructureHandler.CloseConnection() as this must760 // have already been called by RunspacePool.Close()761 }762 763 /// <summary>764 /// Handles a robust connection layer notification from the transport765 /// manager.766 /// </summary>767 /// <param name="sender"></param>768 /// <param name="e"></param>769 private void HandleRobustConnectionNotification(770 object sender,771 ConnectionStatusEventArgs e)772 {773 // Create event arguments and warnings/errors for this robust connection notification.774 PSConnectionRetryStatusEventArgs connectionRetryStatusArgs = null;775 WarningRecord warningRecord = null;776 ErrorRecord errorRecord = null;777 int maxRetryConnectionTimeMSecs = this.runspacePool.MaxRetryConnectionTime;778 int maxRetryConnectionTimeMinutes = maxRetryConnectionTimeMSecs / 60000;779 switch (e.Notification)780 {781 case ConnectionStatus.NetworkFailureDetected:782 warningRecord = new WarningRecord(783 PSConnectionRetryStatusEventArgs.FQIDNetworkFailureDetected,784 StringUtil.Format(RemotingErrorIdStrings.RCNetworkFailureDetected,785 this.computerName, maxRetryConnectionTimeMinutes));786 787 connectionRetryStatusArgs =788 new PSConnectionRetryStatusEventArgs(PSConnectionRetryStatus.NetworkFailureDetected,789 this.computerName, maxRetryConnectionTimeMSecs, warningRecord);790 break;791 792 case ConnectionStatus.ConnectionRetryAttempt:793 warningRecord = new WarningRecord(794 PSConnectionRetryStatusEventArgs.FQIDConnectionRetryAttempt,795 StringUtil.Format(RemotingErrorIdStrings.RCConnectionRetryAttempt, this.computerName));796 797 connectionRetryStatusArgs =798 new PSConnectionRetryStatusEventArgs(PSConnectionRetryStatus.ConnectionRetryAttempt,799 this.computerName, maxRetryConnectionTimeMSecs, warningRecord);800 break;801 802 case ConnectionStatus.ConnectionRetrySucceeded:803 warningRecord = new WarningRecord(804 PSConnectionRetryStatusEventArgs.FQIDConnectionRetrySucceeded,805 StringUtil.Format(RemotingErrorIdStrings.RCReconnectSucceeded, this.computerName));806 807 connectionRetryStatusArgs =808 new PSConnectionRetryStatusEventArgs(PSConnectionRetryStatus.ConnectionRetrySucceeded,809 this.computerName, maxRetryConnectionTimeMinutes, warningRecord);810 break;811 812 case ConnectionStatus.AutoDisconnectStarting:813 {814 warningRecord = new WarningRecord(815 PSConnectionRetryStatusEventArgs.FQIDAutoDisconnectStarting,816 StringUtil.Format(RemotingErrorIdStrings.RCAutoDisconnectingWarning, this.computerName));817 818 connectionRetryStatusArgs =819 new PSConnectionRetryStatusEventArgs(PSConnectionRetryStatus.AutoDisconnectStarting,820 this.computerName, maxRetryConnectionTimeMinutes, warningRecord);821 }822 823 break;824 825 case ConnectionStatus.AutoDisconnectSucceeded:826 warningRecord = new WarningRecord(827 PSConnectionRetryStatusEventArgs.FQIDAutoDisconnectSucceeded,828 StringUtil.Format(RemotingErrorIdStrings.RCAutoDisconnected, this.computerName));829 830 connectionRetryStatusArgs =831 new PSConnectionRetryStatusEventArgs(PSConnectionRetryStatus.AutoDisconnectSucceeded,832 this.computerName, maxRetryConnectionTimeMinutes, warningRecord);833 break;834 835 case ConnectionStatus.InternalErrorAbort:836 {837 string msg = StringUtil.Format(RemotingErrorIdStrings.RCInternalError, this.computerName);838 RuntimeException reason = new RuntimeException(msg);839 errorRecord = new ErrorRecord(reason,840 PSConnectionRetryStatusEventArgs.FQIDNetworkOrDisconnectFailed,841 ErrorCategory.InvalidOperation, this);842 843 connectionRetryStatusArgs =844 new PSConnectionRetryStatusEventArgs(PSConnectionRetryStatus.InternalErrorAbort,845 this.computerName, maxRetryConnectionTimeMinutes, errorRecord);846 }847 848 break;849 }850 851 if (connectionRetryStatusArgs == null)852 {853 return;854 }855 856 // Update connection status.857 _connectionRetryStatus = connectionRetryStatusArgs.Notification;858 859 if (warningRecord != null)860 {861 RemotingWarningRecord remotingWarningRecord = new RemotingWarningRecord(862 warningRecord,863 new OriginInfo(this.computerName, this.InstanceId));864 865 // Add warning record to information channel.866 HandleInformationalMessageReceived(this,867 new RemoteDataEventArgs<InformationalMessage>(868 new InformationalMessage(remotingWarningRecord, RemotingDataType.PowerShellWarning)));869 870 // Write warning to host.871 RemoteHostCall writeWarning = new RemoteHostCall(872 -100,873 RemoteHostMethodId.WriteWarningLine,874 new object[] { warningRecord.Message });875 876 try877 {878 HandleHostCallReceived(this,879 new RemoteDataEventArgs<RemoteHostCall>(writeWarning));880 }881 catch (PSNotImplementedException)882 { }883 }884 885 if (errorRecord != null)886 {887 RemotingErrorRecord remotingErrorRecord = new RemotingErrorRecord(888 errorRecord,889 new OriginInfo(this.computerName, this.InstanceId));890 891 // Add error record to error channel, will also be written to host.892 HandleErrorReceived(this,893 new RemoteDataEventArgs<ErrorRecord>(remotingErrorRecord));894 }895 896 // Raise event.897 RCConnectionNotification.SafeInvoke(this, connectionRetryStatusArgs);898 }899 900 #endregion Private Methods901 902 #region Private Fields903 904 private ObjectStreamBase inputstream;905 private ObjectStreamBase errorstream;906 private PSInformationalBuffers informationalBuffers;907 private readonly PowerShell shell;908 private readonly Guid clientRunspacePoolId;909 private bool noInput;910 private PSInvocationSettings settings;911 private ObjectStreamBase outputstream;912 private readonly string computerName;913 private ClientPowerShellDataStructureHandler dataStructureHandler;914 private bool stopCalled = false;915 private PSHost hostToUse;916 private readonly RemoteRunspacePoolInternal runspacePool;917 918 private const string WRITE_DEBUG_LINE = "WriteDebugLine";919 private const string WRITE_VERBOSE_LINE = "WriteVerboseLine";920 private const string WRITE_WARNING_LINE = "WriteWarningLine";921 private const string WRITE_PROGRESS = "WriteProgress";922 923 private bool initialized = false;924 /// <summary>925 /// This queue is for the state change events that resulted in closing the underlying926 /// datastructure handler. We cannot send the state back to the upper layers until927 /// close is completed from the datastructure/transport layer.928 /// </summary>929 private readonly Queue<PSInvocationStateInfo> _stateInfoQueue = new Queue<PSInvocationStateInfo>();930 931 private PSConnectionRetryStatus _connectionRetryStatus = PSConnectionRetryStatus.None;932 933 #endregion Private Fields934 935 #region IDisposable936 937 /// <summary>938 /// Release all resources.939 /// </summary>940 public void Dispose()941 {942 // inputstream.Dispose();943 // outputstream.Dispose();944 // errorstream.Dispose();945 }946 947 #endregion IDisposable948 }949 950 #region PSConnectionRetryStatusEventArgs951 952 /// <summary>953 /// Robust Connection notifications.954 /// </summary>955 internal enum PSConnectionRetryStatus956 {957 None = 0,958 NetworkFailureDetected = 1,959 ConnectionRetryAttempt = 2,960 ConnectionRetrySucceeded = 3,961 AutoDisconnectStarting = 4,962 AutoDisconnectSucceeded = 5,963 InternalErrorAbort = 6964 }965 966 /// <summary>967 /// PSConnectionRetryStatusEventArgs.968 /// </summary>969 internal sealed class PSConnectionRetryStatusEventArgs : EventArgs970 {971 internal const string FQIDNetworkFailureDetected = "PowerShellNetworkFailureDetected";972 internal const string FQIDConnectionRetryAttempt = "PowerShellConnectionRetryAttempt";973 internal const string FQIDConnectionRetrySucceeded = "PowerShellConnectionRetrySucceeded";974 internal const string FQIDAutoDisconnectStarting = "PowerShellNetworkFailedStartDisconnect";975 internal const string FQIDAutoDisconnectSucceeded = "PowerShellAutoDisconnectSucceeded";976 internal const string FQIDNetworkOrDisconnectFailed = "PowerShellNetworkOrDisconnectFailed";977 978 internal PSConnectionRetryStatusEventArgs(979 PSConnectionRetryStatus notification,980 string computerName,981 int maxRetryConnectionTime,982 object infoRecord)983 {984 Notification = notification;985 ComputerName = computerName;986 MaxRetryConnectionTime = maxRetryConnectionTime;987 InformationRecord = infoRecord;988 }989 990 internal PSConnectionRetryStatus Notification { get; }991 992 internal string ComputerName { get; }993 994 internal int MaxRetryConnectionTime { get; }995 996 internal object InformationRecord { get; }997 }998 999 #endregion1000}1001 