/********************************************************************++
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
}