2016-03-30 14:20:52 -07:00
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System.Management.Automation.Language ;
using System.Security ;
using System.Collections ;
using System.Collections.Generic ;
using System.Collections.ObjectModel ;
using System.Diagnostics ;
using System.Management.Automation.Runspaces ;
2016-07-29 13:02:49 -07:00
using Dbg = System . Management . Automation ;
2016-03-30 14:20:52 -07:00
using System.Diagnostics.CodeAnalysis ;
namespace System.Management.Automation
{
/// <summary>
/// Holds the state of a Monad Shell session
/// </summary>
[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "This is a bridge class between internal classes and a public interface. It requires this much coupling.")]
internal sealed partial class SessionStateInternal
{
#region tracer
/// <summary>
/// An instance of the PSTraceSource class used for trace output
/// using "SessionState" as the category.
/// </summary>
[Dbg.TraceSourceAttribute(
2016-07-29 13:02:49 -07:00
"SessionState",
2016-03-30 14:20:52 -07:00
"SessionState Class")]
2016-07-29 13:02:49 -07:00
private static Dbg . PSTraceSource s_tracer =
Dbg . PSTraceSource . GetTracer ( "SessionState" ,
2016-03-30 14:20:52 -07:00
"SessionState Class" );
#endregion tracer
#region Constructor
/// <summary>
/// Constructor for session state object
/// </summary>
///
/// <param name="context">
/// The context for the runspace to which this session state object belongs.
/// </param>
///
/// <exception cref="ArgumentNullException">
/// if <paramref name="context"/> is null.
/// </exception>
///
internal SessionStateInternal ( ExecutionContext context ) : this ( null , false , context )
{
}
internal SessionStateInternal ( SessionStateInternal parent , bool linkToGlobal , ExecutionContext context )
{
if ( context == null )
{
throw PSTraceSource . NewArgumentNullException ( "context" );
}
2016-07-29 13:02:49 -07:00
_context = context ;
2016-03-30 14:20:52 -07:00
// Create the working directory stack. This
// is used for the pushd and popd commands
2016-07-29 13:02:49 -07:00
_workingLocationStack = new Dictionary < String , Stack < PathInfo >>( StringComparer . OrdinalIgnoreCase );
2016-03-30 14:20:52 -07:00
_globalScope = new SessionStateScope ( null );
_moduleScope = _globalScope ;
2016-07-29 13:02:49 -07:00
_currentScope = _globalScope ;
2016-03-30 14:20:52 -07:00
InitializeSessionStateInternalSpecialVariables ( false );
// Create the push the global scope on as
// the starting script scope. That way, if you dot-source a script
// that uses variables qualified by script: it works.
_globalScope . ScriptScope = _globalScope ;
if ( parent != null )
{
_globalScope . Parent = parent . GlobalScope ;
// Copy the drives and providers from the parent...
CopyProviders ( parent );
// During loading of core modules, providers are not populated.
// We set the drive information later
if ( Providers != null && Providers . Count > 0 )
{
CurrentDrive = parent . CurrentDrive ;
}
// Link it to the global scope...
if ( linkToGlobal )
{
_globalScope = parent . GlobalScope ;
}
}
else
{
2016-07-29 13:02:49 -07:00
_currentScope . LocalsTuple = MutableTuple . MakeTuple ( Compiler . DottedLocalsTupleType , Compiler . DottedLocalsNameIndexMap );
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
/// Add any special variables to the session state variable table. This routine
/// must be called at construction time or if the variable table is reset.
/// </summary>
internal void InitializeSessionStateInternalSpecialVariables ( bool clearVariablesTable )
{
if ( clearVariablesTable )
{
// Clear the variable table
_globalScope . Variables . Clear ();
// Add in the per-scope default variables.
_globalScope . AddSessionStateScopeDefaultVariables ();
}
// Set variable $Error
PSVariable errorvariable = new PSVariable ( "Error" , new ArrayList (), ScopedItemOptions . Constant );
_globalScope . SetVariable ( errorvariable . Name , errorvariable , false , false , this , fastPath : true );
// Set variable $PSDefaultParameterValues
Collection < Attribute > attributes = new Collection < Attribute >();
attributes . Add ( new ArgumentTypeConverterAttribute ( typeof ( System . Management . Automation . DefaultParameterDictionary )));
PSVariable psDefaultParameterValuesVariable = new PSVariable ( SpecialVariables . PSDefaultParameterValues ,
new DefaultParameterDictionary (),
ScopedItemOptions . None , attributes ,
RunspaceInit . PSDefaultParameterValuesDescription );
_globalScope . SetVariable ( psDefaultParameterValuesVariable . Name , psDefaultParameterValuesVariable , false , false , this , fastPath : true );
}
#endregion Constructor
#region Private data
/// <summary>
/// Provides all the path manipulation and globbing for Monad paths.
/// </summary>
///
internal LocationGlobber Globber
{
get
{
2016-07-29 13:02:49 -07:00
if ( _globberPrivate == null )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
_globberPrivate = _context . LocationGlobber ;
2016-03-30 14:20:52 -07:00
}
2016-07-29 13:02:49 -07:00
return _globberPrivate ;
2016-03-30 14:20:52 -07:00
}
}
2016-07-29 13:02:49 -07:00
private LocationGlobber _globberPrivate ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// The context of the runspace to which this session state object belongs.
/// </summary>
internal ExecutionContext ExecutionContext
{
get { return _context ; }
}
private ExecutionContext _context ;
/// <summary>
/// Returns the public session state facade object for this session state instance.
/// </summary>
internal SessionState PublicSessionState
{
get
{
if ( _publicSessionState == null )
{
_publicSessionState = new SessionState ( this );
}
return _publicSessionState ;
}
set
{
_publicSessionState = value ;
}
}
private SessionState _publicSessionState ;
/// <summary>
/// Gets the engine APIs to access providers
/// </summary>
internal ProviderIntrinsics InvokeProvider
{
get
{
if ( _invokeProvider == null )
_invokeProvider = new ProviderIntrinsics ( this );
return _invokeProvider ;
} // get
} // InvokeProvider
private ProviderIntrinsics _invokeProvider ;
/// <summary>
/// The module info object associated with this session state
/// </summary>
internal PSModuleInfo Module
{
get { return _module ; }
set { _module = value ; }
}
private PSModuleInfo _module = null ;
// This is used to maintain the order in which modules were imported.
// This is used by Get-Command -All to order by last imported
internal List < string > ModuleTableKeys = new List < string >();
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
/// <summary>
/// The private module table for this session state object...
/// </summary>
internal Dictionary < string , PSModuleInfo > ModuleTable
{
get { return _moduleTable ; }
}
private Dictionary < string , PSModuleInfo > _moduleTable = new Dictionary < string , PSModuleInfo >( StringComparer . OrdinalIgnoreCase );
/// <summary>
/// Get/set constraints for this execution environemnt
/// </summary>
internal PSLanguageMode LanguageMode
{
get
{
return _context . LanguageMode ;
}
set
{
_context . LanguageMode = value ;
}
}
/// <summary>
/// If true the PowerShell debugger will use FullLanguage mode, otherwise it will use the current language mode
/// </summary>
internal bool UseFullLanguageModeInDebugger
{
get
{
return _context . UseFullLanguageModeInDebugger ;
}
}
/// <summary>
/// The list of scripts that are allowed to be run. If the name "*"
/// is in the list, then all scripts can be run. (This is the default.)
/// </summary>
public List < string > Scripts
{
get { return _scripts ; }
}
2016-07-29 13:02:49 -07:00
private List < String > _scripts = new List < string >( new string [] { "*" });
2016-03-30 14:20:52 -07:00
/// <summary>
/// See if a script is allowed to be run.
/// </summary>
/// <param name="scriptPath">Path to check</param>
/// <returns>true if script is allowed</returns>
internal SessionStateEntryVisibility CheckScriptVisibility ( string scriptPath )
{
return checkPathVisibility ( _scripts , scriptPath );
}
/// <summary>
/// The list of appications that are allowed to be run. If the name "*"
/// is in the list, then all applications can be run. (This is the default.)
/// </summary>
public List < string > Applications
{
get { return _applications ; }
}
2016-07-29 13:02:49 -07:00
private List < String > _applications = new List < string >( new string [] { "*" });
2016-03-30 14:20:52 -07:00
/// <summary>
/// List of functions/filters to export from this session state object...
/// </summary>
internal List < CmdletInfo > ExportedCmdlets
{
get
{
return _exportedCmdlets ;
}
}
private List < CmdletInfo > _exportedCmdlets = new List < CmdletInfo >();
/// <summary>
/// Defines the default command visibility for this session state. Binding an InitialSessionState instance
/// with private members will set this to Private.
/// </summary>
internal SessionStateEntryVisibility DefaultCommandVisibility = SessionStateEntryVisibility . Public ;
/// <summary>
/// Add an new SessionState cmdlet entry to this session state object...
/// </summary>
/// <param name="entry">The entry to add</param>
internal void AddSessionStateEntry ( SessionStateCmdletEntry entry )
{
AddSessionStateEntry ( entry , /*local*/ false );
}
/// <summary>
/// Add an new SessionState cmdlet entry to this session state object...
/// </summary>
/// <param name="entry">The entry to add</param>
/// <param name="local">If local, add cmdlet to current scope. Else, add to module scope</param>
internal void AddSessionStateEntry ( SessionStateCmdletEntry entry , bool local )
{
ExecutionContext . CommandDiscovery . AddSessionStateCmdletEntryToCache ( entry , local );
}
/// <summary>
/// Add an new SessionState cmdlet entry to this session state object...
/// </summary>
/// <param name="entry">The entry to add</param>
internal void AddSessionStateEntry ( SessionStateApplicationEntry entry )
{
this . Applications . Add ( entry . Path );
}
/// <summary>
/// Add an new SessionState cmdlet entry to this session state object...
/// </summary>
/// <param name="entry">The entry to add</param>
internal void AddSessionStateEntry ( SessionStateScriptEntry entry )
{
this . Scripts . Add ( entry . Path );
}
/// <summary>
/// Add the variables that must always be present in a SessionState instance...
/// </summary>
internal void InitializeFixedVariables ()
{
//
// BUGBUG
//
// String resources for aliases are currently associated with Runspace init
//
// $Host
PSVariable v = new PSVariable (
SpecialVariables . Host ,
_context . EngineHostInterface ,
ScopedItemOptions . Constant | ScopedItemOptions . AllScope ,
RunspaceInit . PSHostDescription );
this . GlobalScope . SetVariable ( v . Name , v , false , true , this , CommandOrigin . Internal , fastPath : true );
// $HOME = %USERPROFILE% - indicate where a user's home directory is located in the file system.
string home = Environment . GetEnvironmentVariable ( "USERPROFILE" ) ?? string . Empty ;
v = new PSVariable ( SpecialVariables . Home ,
home ,
ScopedItemOptions . ReadOnly | ScopedItemOptions . AllScope ,
RunspaceInit . HOMEDescription );
this . GlobalScope . SetVariable ( v . Name , v , false , true , this , CommandOrigin . Internal , fastPath : true );
// $ExecutionContext
v = new PSVariable ( SpecialVariables . ExecutionContext ,
_context . EngineIntrinsics ,
ScopedItemOptions . Constant | ScopedItemOptions . AllScope ,
RunspaceInit . ExecutionContextDescription );
this . GlobalScope . SetVariable ( v . Name , v , false , true , this , CommandOrigin . Internal , fastPath : true );
// $PSVersionTable
v = new PSVariable ( SpecialVariables . PSVersionTable ,
PSVersionInfo . GetPSVersionTable (),
ScopedItemOptions . Constant | ScopedItemOptions . AllScope ,
RunspaceInit . PSVersionTableDescription );
this . GlobalScope . SetVariable ( v . Name , v , false , true , this , CommandOrigin . Internal , fastPath : true );
// $PSEdition
v = new PSVariable ( SpecialVariables . PSEdition ,
PSVersionInfo . PSEditionValue ,
ScopedItemOptions . Constant | ScopedItemOptions . AllScope ,
RunspaceInit . PSEditionDescription );
this . GlobalScope . SetVariable ( v . Name , v , false , true , this , CommandOrigin . Internal , fastPath : true );
// $PID
Process currentProcess = Process . GetCurrentProcess ();
v = new PSVariable (
SpecialVariables . PID ,
currentProcess . Id ,
ScopedItemOptions . Constant | ScopedItemOptions . AllScope ,
RunspaceInit . PIDDescription );
this . GlobalScope . SetVariable ( v . Name , v , false , true , this , CommandOrigin . Internal , fastPath : true );
// $PSCulture
v = new PSCultureVariable ();
this . GlobalScope . SetVariable ( v . Name , v , false , true , this , CommandOrigin . Internal , fastPath : true );
// $PSUICulture
v = new PSUICultureVariable ();
this . GlobalScope . SetVariable ( v . Name , v , false , true , this , CommandOrigin . Internal , fastPath : true );
// $ShellId - if there is no runspace config, use the default string
string shellId = _context . ShellID ;
v = new PSVariable ( SpecialVariables . ShellId , shellId ,
ScopedItemOptions . Constant | ScopedItemOptions . AllScope ,
RunspaceInit . MshShellIdDescription );
this . GlobalScope . SetVariable ( v . Name , v , false , true , this , CommandOrigin . Internal , fastPath : true );
// $PSHOME
// This depends on the shellId. If we cannot read the application base
// registry key, set the variable to empty string
string applicationBase = "" ;
try
{
applicationBase = Utils . GetApplicationBase ( shellId );
}
catch ( SecurityException )
{
}
v = new PSVariable ( SpecialVariables . PSHome , applicationBase ,
ScopedItemOptions . Constant | ScopedItemOptions . AllScope ,
RunspaceInit . PSHOMEDescription );
this . GlobalScope . SetVariable ( v . Name , v , false , true , this , CommandOrigin . Internal , fastPath : true );
// $Console - set the console file for this shell, if there is one, "" otherwise...
SetConsoleVariable ();
}
/// <summary>
/// Set the $Console variable in this session state instance...
/// </summary>
internal void SetConsoleVariable ()
{
// $Console - set the console file for this shell, if there is one, "" otherwise...
string consoleFileName = string . Empty ;
RunspaceConfigForSingleShell rcss = _context . RunspaceConfiguration as RunspaceConfigForSingleShell ;
2016-07-29 13:02:49 -07:00
if ( rcss != null && rcss . ConsoleInfo != null && ! string . IsNullOrEmpty ( rcss . ConsoleInfo . Filename ))
2016-03-30 14:20:52 -07:00
{
consoleFileName = rcss . ConsoleInfo . Filename ;
}
PSVariable v = new PSVariable ( SpecialVariables . ConsoleFileName ,
consoleFileName ,
ScopedItemOptions . ReadOnly | ScopedItemOptions . AllScope ,
RunspaceInit . ConsoleDescription );
this . GlobalScope . SetVariable ( v . Name , v , false , true , this , CommandOrigin . Internal , fastPath : true );
}
/// <summary>
/// Add all of the default built-in functions to this session state instance...
/// </summary>
internal void AddBuiltInEntries ( bool addSetStrictMode )
{
// Other built-in variables
AddBuiltInVariables ();
AddBuiltInFunctions ();
AddBuiltInAliases ();
if ( addSetStrictMode )
{
SessionStateFunctionEntry f = new SessionStateFunctionEntry ( "Set-StrictMode" , "" );
this . AddSessionStateEntry ( f );
}
}
/// <summary>
/// Add the built-in variables to this instance of session state...
/// </summary>
internal void AddBuiltInVariables ()
{
foreach ( SessionStateVariableEntry e in InitialSessionState . BuiltInVariables )
{
this . AddSessionStateEntry ( e );
}
}
/// <summary>
/// Add the built-in functions to this instance of session state...
/// </summary>
internal void AddBuiltInFunctions ()
{
foreach ( SessionStateFunctionEntry f in InitialSessionState . BuiltInFunctions )
{
this . AddSessionStateEntry ( f );
}
}
/// <summary>
/// Add the built-in aliases to this instance of session state...
/// </summary>
internal void AddBuiltInAliases ()
{
foreach ( SessionStateAliasEntry ae in InitialSessionState . BuiltInAliases )
{
this . AddSessionStateEntry ( ae , StringLiterals . Global );
}
}
/// <summary>
/// Check to see if an application is allowed to be run.
/// </summary>
/// <param name="applicationPath">The path to the application to check</param>
/// <returns>True if application is permitted.</returns>
internal SessionStateEntryVisibility CheckApplicationVisibility ( string applicationPath )
{
return checkPathVisibility ( _applications , applicationPath );
}
private SessionStateEntryVisibility checkPathVisibility ( List < string > list , string path )
{
if ( list == null || list . Count == 0 ) return SessionStateEntryVisibility . Private ;
if ( String . IsNullOrEmpty ( path )) return SessionStateEntryVisibility . Private ;
if ( list . Contains ( "*" )) return SessionStateEntryVisibility . Public ;
foreach ( string p in list )
{
if ( String . Equals ( p , path , StringComparison . OrdinalIgnoreCase ))
return SessionStateEntryVisibility . Public ;
2016-07-29 13:02:49 -07:00
if ( WildcardPattern . ContainsWildcardCharacters ( p ))
2016-03-30 14:20:52 -07:00
{
WildcardPattern pattern = WildcardPattern . Get ( p , WildcardOptions . IgnoreCase );
if ( pattern . IsMatch ( path ))
{
return SessionStateEntryVisibility . Public ;
}
}
}
return SessionStateEntryVisibility . Private ;
}
2016-07-29 13:02:49 -07:00
#if RELATIONSHIP_SUPPORTED
2016-03-30 14:20:52 -07:00
// 2004/11/24-JeffJon - Relationships have been removed from the Exchange release
/// <summary>
/// Gets the collection of relationship providers
/// </summary>
///
internal RelationshipProviderCollection Relationships
{
get { return relationships ; }
}
private RelationshipProviderCollection relationships = null ;
#endif
#endregion Private data
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
/// <summary>
/// Notification for SessionState to do cleanup
/// before runspace is closed.
/// </summary>
///
internal void RunspaceClosingNotification ()
{
if ( this != _context . TopLevelSessionState && Providers . Count > 0 )
{
// Remove all providers at the top level...
2016-07-29 13:02:49 -07:00
CmdletProviderContext context = new CmdletProviderContext ( this . ExecutionContext );
2016-03-30 14:20:52 -07:00
2016-07-29 13:02:49 -07:00
Collection < string > keys = new Collection < string >();
2016-03-30 14:20:52 -07:00
foreach ( string key in Providers . Keys )
{
2016-07-29 13:02:49 -07:00
keys . Add ( key );
2016-03-30 14:20:52 -07:00
}
foreach ( string providerName in keys )
{
// All errors are ignored.
RemoveProvider ( providerName , true , context );
}
}
}
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
#region Errors
/// <summary>
/// Constructs a new instance of a ProviderInvocationException
/// using the specified data
/// </summary>
///
/// <param name="resourceId">
/// The resource ID to use as the format message for the error.
/// </param>
///
/// <param name="resourceStr">
/// This is the message template string.
/// </param>
///
/// <param name="provider">
/// The provider information used when formatting the error message.
/// </param>
///
/// <param name="path">
/// The path used when formatting the error message.
/// </param>
///
/// <param name="e">
/// The exception that was thrown by the provider. This will be set as
/// the ProviderInvocationException's InnerException and the message will
/// be used when formatting the error message.
/// </param>
///
/// <returns>
/// A new instance of a ProviderInvocationException.
/// </returns>
///
/// <exception cref="ProviderInvocationException">
/// Wraps <paramref name="e"/> in a ProviderInvocationException
/// and then throws it.
/// </exception>
///
internal ProviderInvocationException NewProviderInvocationException (
string resourceId ,
string resourceStr ,
ProviderInfo provider ,
string path ,
Exception e )
{
return NewProviderInvocationException ( resourceId , resourceStr , provider , path , e , true );
}
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
/// <summary>
/// Constructs a new instance of a ProviderInvocationException
/// using the specified data
/// </summary>
///
/// <param name="resourceId">
/// The resource ID to use as the format message for the error.
/// </param>
///
/// <param name="resourceStr">
/// This is the message template string.
/// </param>
///
/// <param name="provider">
/// The provider information used when formatting the error message.
/// </param>
///
/// <param name="path">
/// The path used when formatting the error message.
/// </param>
///
/// <param name="e">
/// The exception that was thrown by the provider. This will be set as
/// the ProviderInvocationException's InnerException and the message will
/// be used when formatting the error message.
/// </param>
///
/// <param name="useInnerExceptionErrorMessage">
/// If true, the error record from the inner exception will be used if it contains one.
/// If false, the error message specified by the resourceId will be used.
/// </param>
///
/// <returns>
/// A new instance of a ProviderInvocationException.
/// </returns>
///
/// <exception cref="ProviderInvocationException">
/// Wraps <paramref name="e"/> in a ProviderInvocationException
/// and then throws it.
/// </exception>
///
2016-07-29 13:02:49 -07:00
internal ProviderInvocationException NewProviderInvocationException (
2016-03-30 14:20:52 -07:00
string resourceId ,
string resourceStr ,
ProviderInfo provider ,
string path ,
Exception e ,
bool useInnerExceptionErrorMessage )
{
// If the innerException was itself thrown by
// ProviderBase.ThrowTerminatingError, it is already a
// ProviderInvocationException, and we don't want to
// re-wrap it.
ProviderInvocationException pie = e as ProviderInvocationException ;
if ( null != pie )
{
pie . _providerInfo = provider ;
return pie ;
}
2016-07-29 13:02:49 -07:00
pie = new ProviderInvocationException ( resourceId , resourceStr , provider , path , e , useInnerExceptionErrorMessage );
2016-03-30 14:20:52 -07:00
// Log a provider health event
MshLog . LogProviderHealthEvent (
2016-07-29 13:02:49 -07:00
_context ,
2016-03-30 14:20:52 -07:00
provider . Name ,
pie ,
Severity . Warning );
return pie ;
}
#endregion Errors
} // SessionStateInternal class
}