MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Generic;6using System.Collections.ObjectModel;7using System.Diagnostics;8using System.Diagnostics.CodeAnalysis; // for fxcop9using System.IO;10using System.Linq;11using System.Management.Automation.Host;12using System.Management.Automation.Internal;13using System.Management.Automation.Remoting;14using System.Runtime.Serialization;15using System.Threading;16 17using Microsoft.PowerShell.Commands;18 19using Dbg = System.Management.Automation.Diagnostics;20#if LEGACYTELEMETRY21using Microsoft.PowerShell.Telemetry.Internal;22#endif23 24#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings25 26namespace System.Management.Automation.Runspaces27{28 /// <summary>29 /// Runspace class for local runspace.30 /// </summary>31 internal sealed partial class LocalRunspace : RunspaceBase32 {33 #region constructors34 35 /// <summary>36 /// Construct an instance of an Runspace using a custom implementation37 /// of PSHost.38 /// </summary>39 /// <param name="host">40 /// The explicit PSHost implementation41 /// </param>42 /// <param name="initialSessionState">43 /// configuration information for this minshell.44 /// </param>45 /// <param name="suppressClone">46 /// If true, don't make a copy of the initial session state object47 /// </param>48 [SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode")]49 internal LocalRunspace(PSHost host, InitialSessionState initialSessionState, bool suppressClone)50 : base(host, initialSessionState, suppressClone)51 {52 }53 54 /// <summary>55 /// Construct an instance of an Runspace using a custom implementation56 /// of PSHost.57 /// </summary>58 /// <param name="host">59 /// The explicit PSHost implementation60 /// </param>61 /// <param name="initialSessionState">62 /// configuration information for this minshell.63 /// </param>64 [SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode")]65 internal LocalRunspace(PSHost host, InitialSessionState initialSessionState)66 : base(host, initialSessionState)67 {68 }69 #endregion constructors70 71 /// <summary>72 /// Private data to be used by applications built on top of PowerShell.73 ///74 /// Local runspace pool is created with application private data set to an empty <see cref="PSPrimitiveDictionary"/>.75 ///76 /// Runspaces that are part of a <see cref="RunspacePool"/> inherit application private data from the pool.77 /// </summary>78 public override PSPrimitiveDictionary GetApplicationPrivateData()79 {80 // if we didn't get applicationPrivateData from a runspace pool,81 // then we create a new one82 83 if (_applicationPrivateData == null)84 {85 lock (this.SyncRoot)86 {87 _applicationPrivateData ??= new PSPrimitiveDictionary();88 }89 }90 91 return _applicationPrivateData;92 }93 94 /// <summary>95 /// A method that runspace pools can use to propagate application private data into runspaces.96 /// </summary>97 /// <param name="applicationPrivateData"></param>98 internal override void SetApplicationPrivateData(PSPrimitiveDictionary applicationPrivateData)99 {100 _applicationPrivateData = applicationPrivateData;101 }102 103 private PSPrimitiveDictionary _applicationPrivateData;104 105 /// <summary>106 /// Gets the event manager.107 /// </summary>108 public override PSEventManager Events109 {110 get111 {112 System.Management.Automation.ExecutionContext context = this.GetExecutionContext;113 114 if (context == null)115 {116 return null;117 }118 119 return context.Events;120 }121 }122 123 /// <summary>124 /// This property determines whether a new thread is create for each invocation.125 /// </summary>126 /// <remarks>127 /// Any updates to the value of this property must be done before the Runspace is opened128 /// </remarks>129 /// <exception cref="InvalidRunspaceStateException">130 /// An attempt to change this property was made after opening the Runspace131 /// </exception>132 /// <exception cref="InvalidOperationException">133 /// The thread options cannot be changed to the requested value134 /// </exception>135 public override PSThreadOptions ThreadOptions136 {137 get138 {139 return _createThreadOptions;140 }141 142 set143 {144 lock (this.SyncRoot)145 {146 if (value == _createThreadOptions)147 {148 return;149 }150 151 if (this.RunspaceStateInfo.State != RunspaceState.BeforeOpen)152 {153 if (!IsValidThreadOptionsConfiguration(value))154 {155 throw new InvalidOperationException(StringUtil.Format(RunspaceStrings.InvalidThreadOptionsChange));156 }157 }158 159 _createThreadOptions = value;160 }161 }162 }163 164 private bool IsValidThreadOptionsConfiguration(PSThreadOptions options)165 {166 // If the runspace is already opened, we only allow changing options when:167 // - The new value is ReuseThread, and168 // - The apartment state is not STA169 return options == PSThreadOptions.ReuseThread && this.ApartmentState != ApartmentState.STA;170 }171 172 private PSThreadOptions _createThreadOptions = PSThreadOptions.Default;173 174 /// <summary>175 /// Resets the runspace state to allow for fast reuse. Not all of the runspace176 /// elements are reset. The goal is to minimize the chance of the user taking177 /// accidental dependencies on prior runspace state.178 /// </summary>179 public override void ResetRunspaceState()180 {181 PSInvalidOperationException invalidOperation = null;182 183 if (this.InitialSessionState == null)184 {185 invalidOperation = PSTraceSource.NewInvalidOperationException();186 }187 else if (this.RunspaceState != Runspaces.RunspaceState.Opened)188 {189 invalidOperation = PSTraceSource.NewInvalidOperationException(190 RunspaceStrings.RunspaceNotInOpenedState, this.RunspaceState);191 }192 else if (this.RunspaceAvailability != Runspaces.RunspaceAvailability.Available)193 {194 invalidOperation = PSTraceSource.NewInvalidOperationException(195 RunspaceStrings.ConcurrentInvokeNotAllowed);196 }197 198 if (invalidOperation != null)199 {200 invalidOperation.Source = "ResetRunspaceState";201 throw invalidOperation;202 }203 204 this.InitialSessionState.ResetRunspaceState(this.ExecutionContext);205 206 // Finally, reset history for this runspace. This needs to be done207 // last to so that changes to the default MaximumHistorySize will be picked up.208 _history = new History(this.ExecutionContext);209 }210 211 #region protected_methods212 213 /// <summary>214 /// Create a pipeline from a command string.215 /// </summary>216 /// <param name="command">A valid command string. Can be null.</param>217 /// <param name="addToHistory">If true command is added to history.</param>218 /// <param name="isNested">True for nested pipeline.</param>219 /// <returns>220 /// A pipeline pre-filled with Commands specified in commandString.221 /// </returns>222 protected override Pipeline CoreCreatePipeline(string command, bool addToHistory, bool isNested)223 {224 // NTRAID#Windows Out Of Band Releases-915851-2005/09/13225 if (_disposed)226 {227 throw PSTraceSource.NewObjectDisposedException("runspace");228 }229 230 return (Pipeline)new LocalPipeline(this, command, addToHistory, isNested);231 }232 233 #endregion protected_methods234 235 #region protected_properties236 237 /// <summary>238 /// Gets the execution context.239 /// </summary>240 internal override ExecutionContext GetExecutionContext241 {242 get243 {244 if (_engine == null)245 return null;246 else247 return _engine.Context;248 }249 }250 251 /// <summary>252 /// Returns true if the internal host is in a nested prompt.253 /// </summary>254 internal override bool InNestedPrompt255 {256 get257 {258 System.Management.Automation.ExecutionContext context = this.GetExecutionContext;259 260 if (context == null)261 {262 return false;263 }264 265 return context.InternalHost.HostInNestedPrompt() || InInternalNestedPrompt;266 }267 }268 269 /// <summary>270 /// Allows internal nested commands to be run as "HostInNestedPrompt" so that CreatePipelineProcessor() does271 /// not set CommandOrigin to Internal as it normally does by default. This then allows cmdlets like Invoke-History272 /// to replay history command lines in the current runspace with the same language mode context as the host.273 /// </summary>274 internal bool InInternalNestedPrompt275 {276 get;277 set;278 }279 280 #endregion protected_properties281 282 #region internal_properties283 284 /// <summary>285 /// Gets history manager for this runspace.286 /// </summary>287 /// <value></value>288 internal History History289 {290 get291 {292 return _history;293 }294 }295 296 /// <summary>297 /// Gets transcription data for this runspace.298 /// </summary>299 /// <value></value>300 internal TranscriptionData TranscriptionData301 {302 get303 {304 return _transcriptionData;305 }306 }307 308 private TranscriptionData _transcriptionData = null;309 310 private JobRepository _jobRepository;311 /// <summary>312 /// List of jobs in this runspace.313 /// </summary>314 internal JobRepository JobRepository315 {316 get317 {318 return _jobRepository;319 }320 }321 322 private JobManager _jobManager;323 324 /// <summary>325 /// Manager for JobSourceAdapters registered in this runspace.326 /// </summary>327 public override JobManager JobManager328 {329 get330 {331 return _jobManager;332 }333 }334 335 private RunspaceRepository _runspaceRepository;336 337 /// <summary>338 /// List of remote runspaces in this runspace.339 /// </summary>340 internal RunspaceRepository RunspaceRepository341 {342 get343 {344 return _runspaceRepository;345 }346 }347 348 #endregion internal_properties349 350 #region Debugger351 352 /// <summary>353 /// Debugger.354 /// </summary>355 public override Debugger Debugger356 {357 get358 {359 return InternalDebugger ?? base.Debugger;360 }361 }362 363 private static readonly string s_debugPreferenceCachePath = Path.Combine(Platform.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "WindowsPowerShell", "DebugPreference.clixml");364 private static readonly object s_debugPreferenceLockObject = new object();365 366 /// <summary>367 /// DebugPreference serves as a property bag to keep368 /// track of all process specific debug preferences.369 /// </summary>370 public class DebugPreference371 {372 public string[] AppDomainNames;373 }374 375 /// <summary>376 /// CreateDebugPerfStruct is a helper method to populate DebugPreference.377 /// </summary>378 /// <param name="AppDomainNames">App Domain Names.</param>379 /// <returns>DebugPreference.</returns>380 private static DebugPreference CreateDebugPreference(string[] AppDomainNames)381 {382 DebugPreference DebugPreference = new DebugPreference();383 DebugPreference.AppDomainNames = AppDomainNames;384 return DebugPreference;385 }386 387 /// <summary>388 /// SetDebugPreference is a helper method used to enable and disable debug preference.389 /// </summary>390 /// <param name="processName">Process Name.</param>391 /// <param name="appDomainName">App Domain Name.</param>392 /// <param name="enable">Indicates if the debug preference has to be enabled or disabled.</param>393 internal static void SetDebugPreference(string processName, List<string> appDomainName, bool enable)394 {395 lock (s_debugPreferenceLockObject)396 {397 bool iscacheUpdated = false;398 Hashtable debugPreferenceCache = null;399 400 string[] appDomainNames = null;401 if (appDomainName != null)402 {403 appDomainNames = appDomainName.ToArray();404 }405 406 if (!File.Exists(LocalRunspace.s_debugPreferenceCachePath))407 {408 if (enable)409 {410 DebugPreference DebugPreference = CreateDebugPreference(appDomainNames);411 debugPreferenceCache = new Hashtable();412 debugPreferenceCache.Add(processName, DebugPreference);413 iscacheUpdated = true;414 }415 }416 else417 {418 debugPreferenceCache = GetDebugPreferenceCache(null);419 if (debugPreferenceCache != null)420 {421 if (enable)422 {423 // Debug preference is set to enable.424 // If the cache does not contain the process name, then we just update the cache.425 if (!debugPreferenceCache.ContainsKey(processName))426 {427 DebugPreference DebugPreference = CreateDebugPreference(appDomainNames);428 debugPreferenceCache.Add(processName, DebugPreference);429 iscacheUpdated = true;430 }431 else432 {433 // In this case, the cache contains the process name, hence we check the list of434 // app domains for which the debug preference is set to enable.435 DebugPreference processDebugPreference = GetProcessSpecificDebugPreference(debugPreferenceCache[processName]);436 437 // processDebugPreference would point to null if debug preference is enabled for all app domains.438 // If processDebugPreference is not null then it means that user has selected specific439 // appdomins for which the debug preference has to be enabled.440 if (processDebugPreference != null)441 {442 List<string> cachedAppDomainNames = null;443 if (processDebugPreference.AppDomainNames != null && processDebugPreference.AppDomainNames.Length > 0)444 {445 cachedAppDomainNames = new List<string>(processDebugPreference.AppDomainNames);446 447 foreach (string currentAppDomainName in appDomainName)448 {449 if (!cachedAppDomainNames.Contains(currentAppDomainName, StringComparer.OrdinalIgnoreCase))450 {451 cachedAppDomainNames.Add(currentAppDomainName);452 iscacheUpdated = true;453 }454 }455 }456 457 if (iscacheUpdated)458 {459 DebugPreference DebugPreference = CreateDebugPreference(cachedAppDomainNames.ToArray());460 debugPreferenceCache[processName] = DebugPreference;461 }462 }463 }464 }465 else466 {467 // Debug preference is set to disable.468 if (debugPreferenceCache.ContainsKey(processName))469 {470 if (appDomainName == null)471 {472 debugPreferenceCache.Remove(processName);473 iscacheUpdated = true;474 }475 else476 {477 DebugPreference processDebugPreference = GetProcessSpecificDebugPreference(debugPreferenceCache[processName]);478 479 // processDebugPreference would point to null if debug preference is enabled for all app domains.480 // If processDebugPreference is not null then it means that user has selected specific481 // appdomins for which the debug preference has to be enabled.482 if (processDebugPreference != null)483 {484 List<string> cachedAppDomainNames = null;485 if (processDebugPreference.AppDomainNames != null && processDebugPreference.AppDomainNames.Length > 0)486 {487 cachedAppDomainNames = new List<string>(processDebugPreference.AppDomainNames);488 489 foreach (string currentAppDomainName in appDomainName)490 {491 if (cachedAppDomainNames.Contains(currentAppDomainName, StringComparer.OrdinalIgnoreCase))492 {493 // remove requested appdomains debug preference details.494 cachedAppDomainNames.Remove(currentAppDomainName);495 iscacheUpdated = true;496 }497 }498 }499 500 if (iscacheUpdated)501 {502 DebugPreference DebugPreference = CreateDebugPreference(cachedAppDomainNames.ToArray());503 debugPreferenceCache[processName] = DebugPreference;504 }505 }506 }507 }508 }509 }510 else511 {512 // For whatever reason, cache is corrupted. Hence override the cache content.513 if (enable)514 {515 debugPreferenceCache = new Hashtable();516 DebugPreference DebugPreference = CreateDebugPreference(appDomainNames);517 debugPreferenceCache.Add(processName, DebugPreference);518 iscacheUpdated = true;519 }520 }521 }522 523 if (iscacheUpdated)524 {525 using (PowerShell ps = PowerShell.Create())526 {527 ps.AddCommand("Export-Clixml").AddParameter("Path", LocalRunspace.s_debugPreferenceCachePath).AddParameter("InputObject", debugPreferenceCache);528 ps.Invoke();529 }530 }531 }532 }533 534 /// <summary>535 /// GetDebugPreferenceCache is a helper method used to fetch536 /// the debug preference cache contents as a Hashtable.537 /// </summary>538 /// <param name="runspace">Runspace.</param>539 /// <returns>If the Debug preference is persisted then a hashtable containing540 /// the debug preference is returned or else Null is returned.</returns>541 private static Hashtable GetDebugPreferenceCache(Runspace runspace)542 {543 Hashtable debugPreferenceCache = null;544 using (PowerShell ps = PowerShell.Create())545 {546 if (runspace != null)547 {548 ps.Runspace = runspace;549 }550 551 ps.AddCommand("Import-Clixml").AddParameter("Path", LocalRunspace.s_debugPreferenceCachePath);552 Collection<PSObject> psObjects = ps.Invoke();553 554 if (psObjects != null && psObjects.Count == 1)555 {556 debugPreferenceCache = psObjects[0].BaseObject as Hashtable;557 }558 }559 560 return debugPreferenceCache;561 }562 563 /// <summary>564 /// GetProcessSpecificDebugPreference is a helper method used to fetch persisted process specific debug preference.565 /// </summary>566 /// <param name="debugPreference"></param>567 /// <returns></returns>568 private static DebugPreference GetProcessSpecificDebugPreference(object debugPreference)569 {570 DebugPreference processDebugPreference = null;571 if (debugPreference != null)572 {573 PSObject debugPreferencePsObject = debugPreference as PSObject;574 if (debugPreferencePsObject != null)575 {576 processDebugPreference = LanguagePrimitives.ConvertTo<DebugPreference>(debugPreferencePsObject);577 }578 }579 580 return processDebugPreference;581 }582 583 #endregion584 585 /// <summary>586 /// Open the runspace.587 /// </summary>588 /// <param name="syncCall">589 /// parameter which control if Open is done synchronously or asynchronously590 /// </param>591 protected override void OpenHelper(bool syncCall)592 {593 if (syncCall)594 {595 // Open runspace synchronously596 DoOpenHelper();597 }598 else599 {600 // Open runspace in another thread601 Thread asyncThread = new Thread(new ThreadStart(this.OpenThreadProc));602 603 asyncThread.Start();604 }605 }606 607 /// <summary>608 /// Start method for asynchronous open.609 /// </summary>610 private void OpenThreadProc()611 {612#pragma warning disable 56500613 try614 {615 DoOpenHelper();616 }617 catch (Exception)618 {619 // This exception is reported by raising RunspaceState620 // change event.621 }622#pragma warning restore 56500623 }624 625 /// <summary>626 /// Helper function used for opening a runspace.627 /// </summary>628 private void DoOpenHelper()629 {630 Dbg.Assert(InitialSessionState != null, "InitialSessionState should not be null");631 // NTRAID#Windows Out Of Band Releases-915851-2005/09/13632 if (_disposed)633 {634 throw PSTraceSource.NewObjectDisposedException("runspace");635 }636 637 bool startLifeCycleEventWritten = false;638 s_runspaceInitTracer.WriteLine("begin open runspace");639 try640 {641 _transcriptionData = new TranscriptionData();642 643 // All ISS-based configuration of the engine itself is done by AutomationEngine,644 // which calls InitialSessionState.Bind(). Anything that doesn't645 // require an active and open runspace should be done in ISS.Bind()646 _engine = new AutomationEngine(Host, InitialSessionState);647 _engine.Context.CurrentRunspace = this;648 649 // Log engine for start of engine life650 MshLog.LogEngineLifecycleEvent(_engine.Context, EngineState.Available);651 startLifeCycleEventWritten = true;652 653 _history = new History(_engine.Context);654 _jobRepository = new JobRepository();655 _jobManager = new JobManager();656 _runspaceRepository = new RunspaceRepository();657 658 s_runspaceInitTracer.WriteLine("initializing built-in aliases and variable information");659 InitializeDefaults();660 }661 catch (Exception exception)662 {663 s_runspaceInitTracer.WriteLine("Runspace open failed");664 665 // Log engine health event666 LogEngineHealthEvent(exception);667 668 // Log engine for end of engine life669 if (startLifeCycleEventWritten)670 {671 Dbg.Assert(_engine.Context != null, "if startLifeCycleEventWritten is true, ExecutionContext must be present");672 MshLog.LogEngineLifecycleEvent(_engine.Context, EngineState.Stopped);673 }674 675 // Open failed. Set the RunspaceState to Broken.676 SetRunspaceState(RunspaceState.Broken, exception);677 678 // Raise the event679 RaiseRunspaceStateEvents();680 681 // Rethrow the exception. For asynchronous execution,682 // OpenThreadProc will catch it. For synchronous execution683 // caller of open will catch it.684 throw;685 }686 687 SetRunspaceState(RunspaceState.Opened);688 RunspaceOpening.Set();689 690 // Raise the event691 RaiseRunspaceStateEvents();692 s_runspaceInitTracer.WriteLine("runspace opened successfully");693 694 // Now do initial state configuration that requires an active runspace695 Exception initError = InitialSessionState.BindRunspace(this, s_runspaceInitTracer);696 if (initError != null)697 {698 // Log engine health event699 LogEngineHealthEvent(initError);700 701 // Log engine for end of engine life702 Debug.Assert(_engine.Context != null,703 "if startLifeCycleEventWritten is true, ExecutionContext must be present");704 MshLog.LogEngineLifecycleEvent(_engine.Context, EngineState.Stopped);705 706 // Open failed. Set the RunspaceState to Broken.707 SetRunspaceState(RunspaceState.Broken, initError);708 709 // Raise the event710 RaiseRunspaceStateEvents();711 712 // Throw the exception. For asynchronous execution,713 // OpenThreadProc will catch it. For synchronous execution714 // caller of open will catch it.715 throw initError;716 }717 718#if LEGACYTELEMETRY719 TelemetryAPI.ReportLocalSessionCreated(InitialSessionState, TranscriptionData);720#endif721 }722 723 /// <summary>724 /// Logs engine health event.725 /// </summary>726 internal void LogEngineHealthEvent(Exception exception)727 {728 LogEngineHealthEvent(729 exception,730 Severity.Error,731 MshLog.EVENT_ID_CONFIGURATION_FAILURE,732 null);733 }734 735 /// <summary>736 /// Logs engine health event.737 /// </summary>738 internal void LogEngineHealthEvent(Exception exception,739 Severity severity,740 int id,741 Dictionary<string, string> additionalInfo)742 {743 Dbg.Assert(exception != null, "Caller should validate the parameter");744 745 LogContext logContext = new LogContext();746 logContext.EngineVersion = Version.ToString();747 logContext.HostId = Host.InstanceId.ToString();748 logContext.HostName = Host.Name;749 logContext.HostVersion = Host.Version.ToString();750 logContext.RunspaceId = InstanceId.ToString();751 logContext.Severity = severity.ToString();752 logContext.ShellId = Utils.DefaultPowerShellShellID;753 MshLog.LogEngineHealthEvent(754 logContext,755 id,756 exception,757 additionalInfo);758 }759 760 /// <summary>761 /// Returns the thread that must be used to execute pipelines when CreateThreadOptions is ReuseThread.762 /// </summary>763 /// <remarks>764 /// The pipeline calls this function after ensuring there is a single thread in the pipeline, so no locking is necessary765 /// </remarks>766 internal PipelineThread GetPipelineThread()767 {768 _pipelineThread ??= new PipelineThread(this.ApartmentState);769 770 return _pipelineThread;771 }772 773 private PipelineThread _pipelineThread = null;774 775 protected override void CloseHelper(bool syncCall)776 {777 if (syncCall)778 {779 // Do close synchronously780 DoCloseHelper();781 }782 else783 {784 // Do close asynchronously785 Thread asyncThread = new Thread(new ThreadStart(this.CloseThreadProc));786 787 asyncThread.Start();788 }789 }790 791 /// <summary>792 /// Start method for asynchronous close.793 /// </summary>794 private void CloseThreadProc()795 {796#pragma warning disable 56500797 try798 {799 DoCloseHelper();800 }801 catch (Exception)802 {803 }804#pragma warning restore 56500805 }806 807 /// <summary>808 /// Close the runspace.809 /// </summary>810 /// <remarks>811 /// Attempts to create/execute pipelines after a call to812 /// close will fail.813 /// </remarks>814 private void DoCloseHelper()815 {816 var isPrimaryRunspace = (Runspace.PrimaryRunspace == this);817 var haveOpenRunspaces = false;818 foreach (Runspace runspace in RunspaceList)819 {820 if (runspace.RunspaceStateInfo.State == RunspaceState.Opened)821 {822 haveOpenRunspaces = true;823 break;824 }825 }826 827 // When closing the primary runspace, ensure all other local runspaces are closed.828 var closeAllOpenRunspaces = isPrimaryRunspace && haveOpenRunspaces;829 830 // Stop all transcriptions and un-initialize AMSI if we're the last runspace to exit or we are exiting the primary runspace.831 if (!haveOpenRunspaces)832 {833 ExecutionContext executionContext = this.GetExecutionContext;834 if (executionContext != null)835 {836 PSHostUserInterface hostUI = executionContext.EngineHostInterface.UI;837 hostUI?.StopAllTranscribing();838 }839 840 AmsiUtils.Uninitialize();841 }842 843 // Generate the shutdown event844 Events?.GenerateEvent(PSEngineEvent.Exiting, null, Array.Empty<object>(), null, true, false);845 846 // Stop all running pipelines847 // Note:Do not perform the Cancel in lock. Reason is848 // Pipeline executes in separate thread, say threadP.849 // When pipeline is canceled/failed/completed in850 // Pipeline.ExecuteThreadProc it removes the pipeline851 // from the list of running pipelines. threadP will need852 // lock to remove the pipelines from the list of running pipelines853 // And we will deadlock.854 // Note:It is possible that one or more pipelines in the list855 // of active pipelines have completed before we call cancel.856 // That is fine since Pipeline.Cancel handles that( It ignores857 // the cancel request if pipeline execution has already858 // completed/failed/canceled.859 StopPipelines();860 861 // Disconnect all disconnectable jobs in the job repository.862 StopOrDisconnectAllJobs();863 864 // Close or disconnect all the remote runspaces available in the865 // runspace repository.866 CloseOrDisconnectAllRemoteRunspaces(() =>867 {868 List<RemoteRunspace> runspaces = new List<RemoteRunspace>();869 foreach (PSSession psSession in this.RunspaceRepository.Runspaces)870 {871 runspaces.Add(psSession.Runspace as RemoteRunspace);872 }873 874 return runspaces;875 });876 877 // Notify Engine components that runspace is closing.878 _engine.Context.RunspaceClosingNotification();879 880 // Log engine lifecycle event.881 MshLog.LogEngineLifecycleEvent(_engine.Context, EngineState.Stopped);882 883 // All pipelines have been canceled. Close the runspace.884 _engine = null;885 886 SetRunspaceState(RunspaceState.Closed);887 888 // Raise Event889 RaiseRunspaceStateEvents();890 891 if (closeAllOpenRunspaces)892 {893 foreach (Runspace runspace in RunspaceList)894 {895 if (runspace.RunspaceStateInfo.State == RunspaceState.Opened)896 {897 runspace.Dispose();898 }899 }900 }901 902 // Report telemetry if we have no more open runspaces.903#if LEGACYTELEMETRY904 bool allRunspacesClosed = true;905 bool hostProvidesExitTelemetry = false;906 foreach (var r in Runspace.RunspaceList)907 {908 if (r.RunspaceStateInfo.State != RunspaceState.Closed)909 {910 allRunspacesClosed = false;911 break;912 }913 914 var localRunspace = r as LocalRunspace;915 if (localRunspace != null && localRunspace.Host is IHostProvidesTelemetryData)916 {917 hostProvidesExitTelemetry = true;918 break;919 }920 }921 922 if (allRunspacesClosed && !hostProvidesExitTelemetry)923 {924 TelemetryAPI.ReportExitTelemetry(null);925 }926#endif927 }928 929 /// <summary>930 /// Closes or disconnects all the remote runspaces passed in by the getRunspace931 /// function. If a remote runspace supports disconnect then it will be disconnected932 /// rather than closed.933 /// </summary>934 private static void CloseOrDisconnectAllRemoteRunspaces(Func<List<RemoteRunspace>> getRunspaces)935 {936 List<RemoteRunspace> runspaces = getRunspaces();937 if (runspaces.Count == 0)938 {939 return;940 }941 942 // whether the close of all remoterunspaces completed943 using (ManualResetEvent remoteRunspaceCloseCompleted = new ManualResetEvent(false))944 {945 ThrottleManager throttleManager = new ThrottleManager();946 throttleManager.ThrottleComplete += (object sender, EventArgs e) => remoteRunspaceCloseCompleted.Set();947 948 foreach (RemoteRunspace remoteRunspace in runspaces)949 {950 IThrottleOperation operation = new CloseOrDisconnectRunspaceOperationHelper(remoteRunspace);951 throttleManager.AddOperation(operation);952 }953 954 throttleManager.EndSubmitOperations();955 956 remoteRunspaceCloseCompleted.WaitOne();957 }958 }959 960 /// <summary>961 /// Disconnects all disconnectable jobs listed in the JobRepository.962 /// </summary>963 private void StopOrDisconnectAllJobs()964 {965 if (JobRepository.Jobs.Count == 0)966 {967 return;968 }969 970 List<RemoteRunspace> disconnectRunspaces = new List<RemoteRunspace>();971 972 using (ManualResetEvent jobsStopCompleted = new ManualResetEvent(false))973 {974 ThrottleManager throttleManager = new ThrottleManager();975 throttleManager.ThrottleComplete += (object sender, EventArgs e) => jobsStopCompleted.Set();976 977 foreach (Job job in this.JobRepository.Jobs)978 {979 // Only stop or disconnect PowerShell jobs.980 if (job is not PSRemotingJob)981 {982 continue;983 }984 985 if (!job.CanDisconnect)986 {987 // If the job cannot be disconnected then add it to988 // the stop list.989 throttleManager.AddOperation(new StopJobOperationHelper(job));990 }991 else if (job.JobStateInfo.State == JobState.Running)992 {993 // Otherwise add disconnectable runspaces to list so that994 // they can be disconnected.995 IEnumerable<RemoteRunspace> jobRunspaces = job.GetRunspaces();996 if (jobRunspaces != null)997 {998 disconnectRunspaces.AddRange(jobRunspaces);999 }1000 }1001 }1002 1003 // Stop jobs.1004 throttleManager.EndSubmitOperations();1005 jobsStopCompleted.WaitOne();1006 }1007 1008 // Disconnect all disconnectable job runspaces found.1009 CloseOrDisconnectAllRemoteRunspaces(() => disconnectRunspaces);1010 }1011 1012 internal void ReleaseDebugger()1013 {1014 Debugger debugger = Debugger;1015 if (debugger != null)1016 {1017 try1018 {1019 if (debugger.UnhandledBreakpointMode == UnhandledBreakpointProcessingMode.Wait)1020 {1021 // Sets the mode and also releases a held debug stop.1022 debugger.UnhandledBreakpointMode = UnhandledBreakpointProcessingMode.Ignore;1023 }1024 }1025 catch (PSNotImplementedException) { }1026 }1027 }1028 1029 #region SessionStateProxy1030 1031 protected override void DoSetVariable(string name, object value)1032 {1033 // NTRAID#Windows Out Of Band Releases-915851-2005/09/131034 if (_disposed)1035 {1036 throw PSTraceSource.NewObjectDisposedException("runspace");1037 }1038 1039 _engine.Context.EngineSessionState.SetVariableValue(name, value, CommandOrigin.Internal);1040 }1041 1042 protected override object DoGetVariable(string name)1043 {1044 // NTRAID#Windows Out Of Band Releases-915851-2005/09/131045 if (_disposed)1046 {1047 throw PSTraceSource.NewObjectDisposedException("runspace");1048 }1049 1050 return _engine.Context.EngineSessionState.GetVariableValue(name);1051 }1052 1053 protected override List<string> DoApplications1054 {1055 get1056 {1057 if (_disposed)1058 {1059 throw PSTraceSource.NewObjectDisposedException("runspace");1060 }1061 1062 return _engine.Context.EngineSessionState.Applications;1063 }1064 }1065 1066 protected override List<string> DoScripts1067 {1068 get1069 {1070 if (_disposed)1071 {1072 throw PSTraceSource.NewObjectDisposedException("runspace");1073 }1074 1075 return _engine.Context.EngineSessionState.Scripts;1076 }1077 }1078 1079 protected override DriveManagementIntrinsics DoDrive1080 {1081 get1082 {1083 if (_disposed)1084 {1085 throw PSTraceSource.NewObjectDisposedException("runspace");1086 }1087 1088 return _engine.Context.SessionState.Drive;1089 }1090 }1091 1092 protected override PSLanguageMode DoLanguageMode1093 {1094 get1095 {1096 if (_disposed)1097 {1098 throw PSTraceSource.NewObjectDisposedException("runspace");1099 }1100 1101 return _engine.Context.SessionState.LanguageMode;1102 }1103 1104 set1105 {1106 if (_disposed)1107 {1108 throw PSTraceSource.NewObjectDisposedException("runspace");1109 }1110 1111 _engine.Context.SessionState.LanguageMode = value;1112 }1113 }1114 1115 protected override PSModuleInfo DoModule1116 {1117 get1118 {1119 if (_disposed)1120 {1121 throw PSTraceSource.NewObjectDisposedException("runspace");1122 }1123 1124 return _engine.Context.EngineSessionState.Module;1125 }1126 }1127 1128 protected override PathIntrinsics DoPath1129 {1130 get1131 {1132 if (_disposed)1133 {1134 throw PSTraceSource.NewObjectDisposedException("runspace");1135 }1136 1137 return _engine.Context.SessionState.Path;1138 }1139 }1140 1141 protected override CmdletProviderManagementIntrinsics DoProvider1142 {1143 get1144 {1145 if (_disposed)1146 {1147 throw PSTraceSource.NewObjectDisposedException("runspace");1148 }1149 1150 return _engine.Context.SessionState.Provider;1151 }1152 }1153 1154 protected override PSVariableIntrinsics DoPSVariable1155 {1156 get1157 {1158 if (_disposed)1159 {1160 throw PSTraceSource.NewObjectDisposedException("runspace");1161 }1162 1163 return _engine.Context.SessionState.PSVariable;1164 }1165 }1166 1167 protected override CommandInvocationIntrinsics DoInvokeCommand1168 {1169 get1170 {1171 if (_disposed)1172 {1173 throw PSTraceSource.NewObjectDisposedException("runspace");1174 }1175 1176 return _engine.Context.EngineIntrinsics.InvokeCommand;1177 }1178 }1179 1180 protected override ProviderIntrinsics DoInvokeProvider1181 {1182 get1183 {1184 if (_disposed)1185 {1186 throw PSTraceSource.NewObjectDisposedException("runspace");1187 }1188 1189 return _engine.Context.EngineIntrinsics.InvokeProvider;1190 }1191 }1192 1193 #endregion SessionStateProxy1194 1195 #region IDisposable Members1196 1197 /// <summary>1198 /// Set to true when object is disposed.1199 /// </summary>1200 private bool _disposed;