/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ #pragma warning disable 1634, 1691 using System.Collections.Generic; using System.Collections.ObjectModel; using System.Reflection; using System.Security; using System.Management.Automation.Host; using Microsoft.PowerShell.Commands.Internal.Format; namespace System.Management.Automation.Runspaces { /// /// Defines configuration information for runspace. /// /// /// /// /// Developers may want to derive from this class when writing their own /// host to host monad engine. This will allow host to control the behaviour /// of monad engine through customized runspace configuration. /// /// /// /// #if CORECLR internal #else public #endif abstract class RunspaceConfiguration { #region RunspaceConfiguration Factory #if V2 /// /// Create an instance of default RunspaceConfiguration implementation. /// /// RunspaceConfiguration instance created /// /// Exception thrown when implementation class for RunspaceConfiguration is not found. /// /// internal static RunspaceConfiguration Create() { Assembly assembly = Assembly.GetEntryAssembly(); // If monad engine is hosted by an native application, Assembly.GetEntryAssembly() // will actually return null. In this case, we should throw an exception back. if (assembly == null) { throw tracer.NewInvalidOperationException("MiniShellErrors", "InvalidEntryAssembly"); } return Create(assembly); } #endif /// /// Create an instance of RunspaceConfiguration type implemented from an assembly. /// /// Assembly name from which to find runspace configuration implementation /// Runspace configuration instance created /// /// Exception thrown when is null or empty. /// /// public static RunspaceConfiguration Create(string assemblyName) { if (String.IsNullOrEmpty(assemblyName)) { throw PSTraceSource.NewArgumentNullException("assemblyName"); } // If this assembly can't be loaded, Assembly.Load will throw an exception. // Just let the exception bubble up. Assembly assembly = null; foreach (Assembly asm in ClrFacade.GetAssemblies()) { if (string.Equals(asm.GetName().Name, assemblyName, StringComparison.OrdinalIgnoreCase)) { assembly = asm; break; } } if (assembly == null) { assembly = Assembly.Load(new AssemblyName(assemblyName)); } return Create(assembly); } /// /// Create one single shell runspace configuration object from console file /// /// /// /// public static RunspaceConfiguration Create(string consoleFilePath, out PSConsoleLoadException warnings) { return RunspaceConfigForSingleShell.Create(consoleFilePath, out warnings); } /// /// Create one single shell runspace configuration object for a msh version. /// /// public static RunspaceConfiguration Create() { return RunspaceConfigForSingleShell.CreateDefaultConfiguration(); } #if V2 /// /// This is a RunspaceConfiguration factory function which will create an instance /// based on a type specified in a particular assembly. /// /// This function will likely being exposed as a public interface in v2. /// /// /// /// private static RunspaceConfiguration Create(string assemblyName, string runspaceConfigTypeName) { // If this assembly can't be loaded, Assembly.Load will throw an exception. // Just let the exception bubble up. Assembly assembly = Assembly.Load(assemblyName); try { Type runspaceConfigType = assembly.GetType(runspaceConfigTypeName, true); return Create(runspaceConfigType); } catch (SecurityException) { throw new RunspaceConfigurationTypeException(assembly.FullName, runspaceConfigTypeName); } } #endif /// /// Create an RunspaceConfiguration-derived instance based an RunspaceConfiguration-derived type /// in specified assembly. The type name is defined by assembly attribute: /// RunspaceConfigurationTypeAttribute. /// /// /// Runspace configuration instance created /// /// Exception thrown when is null or empty. /// private static RunspaceConfiguration Create(Assembly assembly) { if (assembly == null) throw PSTraceSource.NewArgumentNullException("assembly"); object[] attributes = ClrFacade.GetCustomAttributes(assembly); if (attributes == null || attributes.Length == 0) { throw new RunspaceConfigurationAttributeException("RunspaceConfigurationAttributeNotExist", assembly.FullName); } if (attributes.Length > 1) { throw new RunspaceConfigurationAttributeException("RunspaceConfigurationAttributeDuplicate", assembly.FullName); } RunspaceConfigurationTypeAttribute runspaceConfigTypeAttribute = (RunspaceConfigurationTypeAttribute)attributes[0]; try { Type runspaceConfigType = assembly.GetType(runspaceConfigTypeAttribute.RunspaceConfigurationType, true, false); return Create(runspaceConfigType); } catch (SecurityException) { throw new RunspaceConfigurationTypeException(assembly.FullName, runspaceConfigTypeAttribute.RunspaceConfigurationType); } } /// /// Create RunspaceConfiguration-derived instance based on a derived type. /// /// A type that is derived from RunspaceConfiguration /// private static RunspaceConfiguration Create(Type runspaceConfigType) { MethodInfo method = runspaceConfigType.GetMethod("Create", BindingFlags.Public | BindingFlags.Static); if (method == null) return null; return (RunspaceConfiguration)method.Invoke(null, null); } #endregion RunspaceConfiguration Factory #region Shell Id /// /// Gets the shell id for current runspace configuration. /// public abstract string ShellId { get; } #endregion #region PSSnapin Api's /// /// Add a PSSnapin to runspace configuration. /// /// /// This member provides logic for adding PSSnapin. /// /// RunspaceConfiguration derived class should not override this member. /// /// /// name of the PSSnapin /// warning message /// public PSSnapInInfo AddPSSnapIn(string name, out PSSnapInException warning) { return DoAddPSSnapIn(name, out warning); } internal virtual PSSnapInInfo DoAddPSSnapIn(string name, out PSSnapInException warning) { throw PSTraceSource.NewNotSupportedException(); } /// /// Remove a PSSnapin from runspace configuration. /// /// /// This member provides logic for removing PSSnapin. /// /// RunspaceConfiguration derived class should not override this member. /// /// name of the PSSnapin /// warning message /// public PSSnapInInfo RemovePSSnapIn(string name, out PSSnapInException warning) { return DoRemovePSSnapIn(name, out warning); } internal virtual PSSnapInInfo DoRemovePSSnapIn(string name, out PSSnapInException warning) { throw PSTraceSource.NewNotSupportedException(); } #endregion #region Cmdlet, Provider, Relationship private RunspaceConfigurationEntryCollection _cmdlets; /// /// Gets the cmdlets defined in runspace configuration. /// /// /// RunspaceConfiguration derived class can override this member to provide its own cmdlet list. /// public virtual RunspaceConfigurationEntryCollection Cmdlets { get { return _cmdlets ?? (_cmdlets = new RunspaceConfigurationEntryCollection()); } } private RunspaceConfigurationEntryCollection _providers; /// /// Gets the providers defined in runspace configuration. /// /// /// RunspaceConfiguration derived class can override this member to provide its own provider list. /// public virtual RunspaceConfigurationEntryCollection Providers { get { return _providers ?? (_providers = new RunspaceConfigurationEntryCollection()); } } /* /// /// Gets the relationships defined in runspace configuration. /// public virtual Collection RelationshipData { get; } */ #endregion #region Types and Formats /// /// types.ps1xml information /// /// internal TypeTable TypeTable { get { return _typeTable ?? (_typeTable = new TypeTable()); } } private TypeTable _typeTable; private RunspaceConfigurationEntryCollection _types; /// /// Gets the type data files defined in runspace configuration. /// /// /// RunspaceConfiguration derived class can override this member to provide its own types to be /// loaded at engine start up. /// public virtual RunspaceConfigurationEntryCollection Types { get { return _types ?? (_types = new RunspaceConfigurationEntryCollection()); } } private RunspaceConfigurationEntryCollection _formats; /// /// Gets the format data files defined in runspace configuration. /// /// /// RunspaceConfiguration derived class can override this member to provide its own formats to be /// loaded at engine start up. /// public virtual RunspaceConfigurationEntryCollection Formats { get { return _formats ?? (_formats = new RunspaceConfigurationEntryCollection()); } } internal TypeInfoDataBaseManager FormatDBManager { get; } = new TypeInfoDataBaseManager(); #endregion #region Script Data private RunspaceConfigurationEntryCollection _scripts; /// /// Gets the scripts defined in runspace configuration. /// /// /// RunspaceConfiguration derived class can override this member to provide its own scripts /// to be include in monad engine. /// public virtual RunspaceConfigurationEntryCollection Scripts { get { return _scripts ?? (_scripts = new RunspaceConfigurationEntryCollection()); } } #endregion #region Profile configuration private RunspaceConfigurationEntryCollection _initializationScripts; /// /// Gets the initialization scripts defined in runspace configuration. /// /// /// RunspaceConfiguration derived class can override this member to provide its own initialization scripts /// to be run at the start up of monad engine. /// public virtual RunspaceConfigurationEntryCollection InitializationScripts { get { return _initializationScripts ?? (_initializationScripts = new RunspaceConfigurationEntryCollection()); } } #endregion #region Assemblies private RunspaceConfigurationEntryCollection _assemblies; /// /// Gets the assemblies defined in runspace configuration. /// /// /// RunspaceConfiguration derived class can override this member to provide its own assemblies /// to be loaded at the start up of monad engine. /// public virtual RunspaceConfigurationEntryCollection Assemblies { get { return _assemblies ?? (_assemblies = new RunspaceConfigurationEntryCollection()); } } #endregion #region Security Manager private AuthorizationManager _authorizationManager = null; /// /// Gets the authorization manager to be used for runspace. /// /// /// RunspaceConfiguration derived class can override this member to provide its own authorization /// manager for monad engine. /// public virtual AuthorizationManager AuthorizationManager { get { return _authorizationManager ?? (_authorizationManager = new Microsoft.PowerShell.PSAuthorizationManager(this.ShellId)); } } #endregion #region Configuration Update private PSHost _host = null; internal void Bind(ExecutionContext executionContext) { _host = executionContext.EngineHostInterface; Initialize(executionContext); this.Assemblies.OnUpdate += executionContext.UpdateAssemblyCache; s_runspaceInitTracer.WriteLine("initializing assembly list"); try { this.Assemblies.Update(true); } catch (RuntimeException e) { s_runspaceInitTracer.WriteLine("assembly list initialization failed"); MshLog.LogEngineHealthEvent( executionContext, MshLog.EVENT_ID_CONFIGURATION_FAILURE, e, Severity.Error); executionContext.ReportEngineStartupError(e.Message); throw; } if (executionContext.CommandDiscovery != null) { this.Cmdlets.OnUpdate += executionContext.CommandDiscovery.UpdateCmdletCache; // Force an update here so that cmdlet cache is updated in engine. s_runspaceInitTracer.WriteLine("initializing cmdlet list"); try { this.Cmdlets.Update(true); } catch (PSNotSupportedException e) { s_runspaceInitTracer.WriteLine("cmdlet list initialization failed"); MshLog.LogEngineHealthEvent( executionContext, MshLog.EVENT_ID_CONFIGURATION_FAILURE, e, Severity.Error); executionContext.ReportEngineStartupError(e.Message); throw; } } if (executionContext.EngineSessionState != null) { this.Providers.OnUpdate += executionContext.EngineSessionState.UpdateProviders; // Force an update here so that provider cache is updated in engine. s_runspaceInitTracer.WriteLine("initializing provider list"); try { this.Providers.Update(true); } catch (PSNotSupportedException e) { s_runspaceInitTracer.WriteLine("provider list initialization failed"); MshLog.LogEngineHealthEvent( executionContext, MshLog.EVENT_ID_CONFIGURATION_FAILURE, e, Severity.Error); executionContext.ReportEngineStartupError(e.Message); throw; } } } internal void Unbind(ExecutionContext executionContext) { if (executionContext == null) return; if (executionContext.CommandDiscovery != null) { this.Cmdlets.OnUpdate -= executionContext.CommandDiscovery.UpdateCmdletCache; } if (executionContext.EngineSessionState != null) { this.Providers.OnUpdate -= executionContext.EngineSessionState.UpdateProviders; } this.Assemblies.OnUpdate -= executionContext.UpdateAssemblyCache; } private bool _initialized = false; private Object _syncObject = new Object(); internal void Initialize(ExecutionContext executionContext) { #pragma warning disable 56517 lock (_syncObject) { if (!_initialized) { _initialized = true; this.Types.OnUpdate += this.UpdateTypes; this.Formats.OnUpdate += this.UpdateFormats; s_runspaceInitTracer.WriteLine("initializing types information"); try { this.UpdateTypes(); } catch (RuntimeException e) { s_runspaceInitTracer.WriteLine("type information initialization failed"); MshLog.LogEngineHealthEvent( executionContext, MshLog.EVENT_ID_CONFIGURATION_FAILURE, e, Severity.Warning); executionContext.ReportEngineStartupError(e.Message); } s_runspaceInitTracer.WriteLine("initializing format information"); try { this.UpdateFormats(true); } catch (RuntimeException e) { s_runspaceInitTracer.WriteLine("format information initialization failed"); MshLog.LogEngineHealthEvent( executionContext, MshLog.EVENT_ID_CONFIGURATION_FAILURE, e, Severity.Warning); executionContext.ReportEngineStartupError(e.Message); } } } #pragma warning restore 56517 } internal void UpdateTypes() { Collection independentErrors = new Collection(); Collection entryIndicesToRemove = new Collection(); Collection PSSnapinFiles = FormatAndTypeDataHelper.GetFormatAndTypesErrors( this, _host, this.Types, independentErrors, entryIndicesToRemove); if (entryIndicesToRemove.Count > 0) { RemoveNeedlessEntries(RunspaceConfigurationCategory.Types, entryIndicesToRemove); } this.TypeTable.Update(PSSnapinFiles, _authorizationManager, _host); FormatAndTypeDataHelper.ThrowExceptionOnError( "ErrorsUpdatingTypes", independentErrors, PSSnapinFiles, RunspaceConfigurationCategory.Types); } internal void UpdateFormats() { UpdateFormats(false); } private void UpdateFormats(bool preValidated) { Collection independentErrors = new Collection(); Collection entryIndicesToRemove = new Collection(); Collection PSSnapinFiles = FormatAndTypeDataHelper.GetFormatAndTypesErrors( this, _host, this.Formats, independentErrors, entryIndicesToRemove); if (entryIndicesToRemove.Count > 0) { RemoveNeedlessEntries(RunspaceConfigurationCategory.Formats, entryIndicesToRemove); } this.FormatDBManager.UpdateDataBase(PSSnapinFiles, this.AuthorizationManager, _host, preValidated); FormatAndTypeDataHelper.ThrowExceptionOnError( "ErrorsUpdatingFormats", independentErrors, PSSnapinFiles, RunspaceConfigurationCategory.Formats); } /// /// Remove duplicate entries and entries with files that cannot be loaded from the Types and Formats /// /// /// private void RemoveNeedlessEntries(RunspaceConfigurationCategory category, IList entryIndicesToRemove) { for (int i = entryIndicesToRemove.Count - 1; i >= 0; i--) { if (category == RunspaceConfigurationCategory.Types) { this.Types.RemoveItem(entryIndicesToRemove[i]); } else if (category == RunspaceConfigurationCategory.Formats) { this.Formats.RemoveItem(entryIndicesToRemove[i]); } } } #endregion [TraceSource("RunspaceInit", "Initialization code for Runspace")] private static PSTraceSource s_runspaceInitTracer = PSTraceSource.GetTracer("RunspaceInit", "Initialization code for Runspace", false); } /// /// Enum for describing different kind information that can be configured in runspace configuration. /// /// /// internal enum RunspaceConfigurationCategory { /// /// Cmdlets /// Cmdlets, /// /// Providers /// Providers, /// /// Assemblies /// Assemblies, /// /// Scripts /// Scripts, /// /// Initialization scripts /// InitializationScripts, /// /// Types /// Types, /// /// Formats /// Formats, } #if V2 /// /// Defines enum for runspace constraints /// [Flags] public enum RunspaceConstraints { /// /// No constraint. /// None, /// /// Allow console applications. /// AllowConsoleApplication, /// /// Allow user interaction. /// AllowUserInteraction, /// /// For GUI cases. /// AllowGUIDesktop } #endif }