mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
This PR does 4 things: * Adds a new cmdlet `New-PSBreakpoint` which creates new `Breakpoint` objects and writes them to the pipeline * Adds a `-Breakpoint` parameter to `Debug-Runspace` which will receive `Breakpoint` objects * Makes the constructors for `*Breakpoint` public for use with the API * Makes `Debugger.GetBreakpoint(string id)` and `Debugger.GetBreakpoints()` public since `SetBreakpoints` is public Note: `New-PSBreakpoint` and `Set-PSBreakpoint` (which already exists) are similar... but `Set-PSBreakpoint` also sets the breakpoints in the _current_ runspace. This is not ideal if we want to set breakpoints in a _different runspace than the current one_. ## PR Context The "Attach to process" debugging experience in the PowerShell extension for VSCode is _ok_ but it's not great. The reason it's not great is due to the `BreakAll` feature of PowerShell debugging which, when you run `Debug-Runspace`, will break at the first piece of code that gets run. This is not ideal when you "Attach to process" _and then_ run your code in the other runspace. Today, the experience drops you in `PSReadLine`'s psm1 if PSRL is available or in the vscode PowerShell helper psm1. It's unexpected for the user and not ideal. This PR will allow the extension to pass in the breakpoints that need to be set initially with `BreakAll` turned off for none of this silly behavior. ### Silly behavior example If you want a repro, try this: PowerShell instance 1: ``` Enter-PSHostProcess -Id $otherprocesspid Debug-Runspace 1 ``` PowerShell instance 2: ``` ./runfoo.ps1 ``` Note that you end up NOT `runfoo.ps1`
5440 lines
239 KiB
C#
5440 lines
239 KiB
C#
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
|
|
using System.Collections;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Diagnostics.Tracing;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Management.Automation.Internal;
|
|
using System.Management.Automation.Provider;
|
|
using System.Management.Automation.Language;
|
|
using System.Management.Automation.Security;
|
|
using System.Reflection;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Threading;
|
|
using Microsoft.PowerShell.Commands;
|
|
using System.Management.Automation.Host;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Debug = System.Management.Automation.Diagnostics;
|
|
|
|
namespace System.Management.Automation.Runspaces
|
|
{
|
|
internal class EarlyStartup
|
|
{
|
|
internal static void Init()
|
|
{
|
|
// Code added here should:
|
|
// * run every time we start PowerSHell
|
|
// * have high CPU cost
|
|
// * be ordered from most expensive to least expensive, or at least needed earliest
|
|
// * this method should return quickly, so all work should be run in one or more tasks.
|
|
// * code called from here should correctly handle being called twice, in case initialization
|
|
// is needed in the main code path before the task completes.
|
|
//
|
|
// Code added here should not:
|
|
// * count on running - not all hosts will call this method
|
|
// * have high disk cost
|
|
|
|
// We shouldn't create too many tasks.
|
|
#if !UNIX
|
|
// Amsi initialize can be a little slow
|
|
Task.Run(() =>
|
|
{
|
|
AmsiUtils.WinScanContent(content: string.Empty, sourceMetadata: string.Empty, warmUp: true);
|
|
});
|
|
#endif
|
|
|
|
// One other task for other stuff that's faster, but still a little slow.
|
|
Task.Run(() =>
|
|
{
|
|
// Loading the resources for System.Management.Automation can be expensive, so force that to
|
|
// happen early on a background thread.
|
|
var unused0 = RunspaceInit.OutputEncodingDescription;
|
|
|
|
// This will init some tables and could load some assemblies.
|
|
var unused1 = TypeAccelerators.builtinTypeAccelerators;
|
|
|
|
// This will init some tables and could load some assemblies.
|
|
var unused2 = LanguagePrimitives.GetEnumerator(null);
|
|
});
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Baseclass for defining elements that can be added
|
|
/// to an InitialSessionState object.
|
|
/// </summary>
|
|
public abstract class InitialSessionStateEntry
|
|
{
|
|
/// <summary>
|
|
/// The ctor so that each derived class has a name.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
protected InitialSessionStateEntry(string name)
|
|
{
|
|
Name = name;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The name of this entry.
|
|
/// </summary>
|
|
public string Name { get; internal set; }
|
|
|
|
/// <summary>
|
|
/// The SnapIn to load from initially.
|
|
/// </summary>
|
|
public PSSnapInInfo PSSnapIn { get; private set; }
|
|
|
|
internal void SetPSSnapIn(PSSnapInInfo psSnapIn)
|
|
{
|
|
PSSnapIn = psSnapIn;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The SnapIn to load from initially.
|
|
/// </summary>
|
|
public PSModuleInfo Module { get; private set; }
|
|
|
|
internal void SetModule(PSModuleInfo module)
|
|
{
|
|
Module = module;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shallow-clone this object.
|
|
/// </summary>
|
|
/// <returns>The cloned object...</returns>
|
|
public abstract InitialSessionStateEntry Clone();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Class to constrain session state entries.
|
|
/// </summary>
|
|
public abstract class ConstrainedSessionStateEntry : InitialSessionStateEntry
|
|
{
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="visibility"></param>
|
|
protected ConstrainedSessionStateEntry(string name, SessionStateEntryVisibility visibility)
|
|
: base(name)
|
|
{
|
|
Visibility = visibility;
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public SessionStateEntryVisibility Visibility { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Command class so that all the commands can derive off this one.
|
|
/// Adds the flexibility of adding additional derived class,
|
|
/// such as ProxyCommand for Exchange.
|
|
/// Derived classes - Alias, Application, Cmdlet, Function, Script.
|
|
/// </summary>
|
|
public abstract class SessionStateCommandEntry : ConstrainedSessionStateEntry
|
|
{
|
|
/// <summary>
|
|
/// Base constructor for all SessionState commands.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
protected SessionStateCommandEntry(string name)
|
|
: base(name, SessionStateEntryVisibility.Public)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="visibility"></param>
|
|
protected internal SessionStateCommandEntry(string name, SessionStateEntryVisibility visibility)
|
|
: base(name, visibility)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the type of the command using an enum
|
|
/// instead of requiring a full reflection type check.
|
|
/// </summary>
|
|
public CommandTypes CommandType { get; internal set; }
|
|
|
|
/// <summary>
|
|
/// Is internal so it can be set by the engine code...
|
|
/// This is used to specify whether this command was imported or not
|
|
/// If noClobber is specified during Import-Module, it is set to false.
|
|
/// </summary>
|
|
internal bool _isImported = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Type file configuration entry...
|
|
/// </summary>
|
|
public sealed class SessionStateTypeEntry : InitialSessionStateEntry
|
|
{
|
|
/// <summary>
|
|
/// Loads all entries from the types file.
|
|
/// </summary>
|
|
/// <param name="fileName"></param>
|
|
public SessionStateTypeEntry(string fileName)
|
|
: base(fileName)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(fileName))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("fileName");
|
|
}
|
|
|
|
FileName = fileName.Trim();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads all the types specified in the typeTable.
|
|
/// </summary>
|
|
/// <param name="typeTable"></param>
|
|
public SessionStateTypeEntry(TypeTable typeTable)
|
|
: base("*")
|
|
{
|
|
if (typeTable == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("typeTable");
|
|
}
|
|
|
|
TypeTable = typeTable;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads all entries from the typeData.
|
|
/// </summary>
|
|
/// <param name="typeData"></param>
|
|
/// <param name="isRemove"></param>
|
|
public SessionStateTypeEntry(TypeData typeData, bool isRemove)
|
|
: base("*")
|
|
{
|
|
if (typeData == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("typeData");
|
|
}
|
|
|
|
TypeData = typeData;
|
|
IsRemove = isRemove;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shallow-clone this object.
|
|
/// </summary>
|
|
/// <returns>The cloned object.</returns>
|
|
public override InitialSessionStateEntry Clone()
|
|
{
|
|
SessionStateTypeEntry entry;
|
|
if (FileName != null)
|
|
{
|
|
entry = new SessionStateTypeEntry(FileName);
|
|
}
|
|
else if (TypeTable != null)
|
|
{
|
|
entry = new SessionStateTypeEntry(TypeTable);
|
|
}
|
|
else
|
|
{
|
|
entry = new SessionStateTypeEntry(TypeData, IsRemove);
|
|
}
|
|
|
|
entry.SetPSSnapIn(this.PSSnapIn);
|
|
entry.SetModule(this.Module);
|
|
return entry;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The pathname of the types.ps1xml file. This can be null if
|
|
/// TypeTable constructor or TypeData constructor is used.
|
|
/// </summary>
|
|
public string FileName { get; }
|
|
|
|
/// <summary>
|
|
/// The TypeTable specified with constructor. This can be null if
|
|
/// FileName constructor or TypeData constructor is used.
|
|
/// </summary>
|
|
public TypeTable TypeTable { get; }
|
|
|
|
/// <summary>
|
|
/// The TypeData we want to update with. This can be null if
|
|
/// FileName constructor or TypeTable constructor is used.
|
|
/// </summary>
|
|
public TypeData TypeData { get; }
|
|
|
|
/// <summary>
|
|
/// The operation will be done on the typedata. This is only
|
|
/// meaningful when the TypeData constructor is used.
|
|
/// </summary>
|
|
public bool IsRemove { get; }
|
|
|
|
// So that we can specify the type information on the fly,
|
|
// without using Types.ps1xml file
|
|
// public SessionStateTypeEntry(string name, xmlreader definition);
|
|
// public string Definition { get; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Format file configuration entry...
|
|
/// </summary>
|
|
public sealed class SessionStateFormatEntry : InitialSessionStateEntry
|
|
{
|
|
/// <summary>
|
|
/// Loads the entire formats file.
|
|
/// </summary>
|
|
/// <param name="fileName"></param>
|
|
public SessionStateFormatEntry(string fileName)
|
|
: base("*")
|
|
{
|
|
if (string.IsNullOrWhiteSpace(fileName))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("fileName");
|
|
}
|
|
|
|
FileName = fileName.Trim();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads all the format data specified in the formatTable.
|
|
/// </summary>
|
|
/// <param name="formattable"></param>
|
|
public SessionStateFormatEntry(FormatTable formattable)
|
|
: base("*")
|
|
{
|
|
if (formattable == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("formattable");
|
|
}
|
|
|
|
Formattable = formattable;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads all the format data specified in the typeDefinition.
|
|
/// </summary>
|
|
/// <param name="typeDefinition"></param>
|
|
public SessionStateFormatEntry(ExtendedTypeDefinition typeDefinition)
|
|
: base("*")
|
|
{
|
|
if (typeDefinition == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("typeDefinition");
|
|
}
|
|
|
|
FormatData = typeDefinition;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shallow-clone this object...
|
|
/// </summary>
|
|
/// <returns>The cloned object.</returns>
|
|
public override InitialSessionStateEntry Clone()
|
|
{
|
|
SessionStateFormatEntry entry;
|
|
|
|
if (FileName != null)
|
|
{
|
|
entry = new SessionStateFormatEntry(FileName);
|
|
}
|
|
else if (Formattable != null)
|
|
{
|
|
entry = new SessionStateFormatEntry(Formattable);
|
|
}
|
|
else
|
|
{
|
|
entry = new SessionStateFormatEntry(FormatData);
|
|
}
|
|
|
|
entry.SetPSSnapIn(this.PSSnapIn);
|
|
entry.SetModule(this.Module);
|
|
return entry;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The name of the format file referenced by this entry...
|
|
/// </summary>
|
|
public string FileName { get; }
|
|
|
|
/// <summary>
|
|
/// The FormatTable specified with constructor. This can be null if
|
|
/// FileName constructor is used.
|
|
/// </summary>
|
|
public FormatTable Formattable { get; }
|
|
|
|
/// <summary>
|
|
/// The FormatData specified with constructor.
|
|
/// This can be null if the FileName or FormatTable constructors are used.
|
|
/// </summary>
|
|
public ExtendedTypeDefinition FormatData { get; }
|
|
|
|
// So that we can specify the format information on the fly,
|
|
// without using Format.ps1xml file
|
|
// public SessionStateFormatEntry(string name, xmlreader definition);
|
|
// public string Definition { get; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// An assembly to load for this sessionstate...
|
|
/// </summary>
|
|
public sealed class SessionStateAssemblyEntry : InitialSessionStateEntry
|
|
{
|
|
/// <summary>
|
|
/// Create a named entry for the assembly to load with both the
|
|
/// name and the path to the assembly as a backup.
|
|
/// </summary>
|
|
/// <param name="name">The name of the assembly to load.</param>
|
|
/// <param name="fileName">The path to the assembly to use as an alternative.</param>
|
|
public SessionStateAssemblyEntry(string name, string fileName)
|
|
: base(name)
|
|
{
|
|
FileName = fileName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a named entry for the assembly to load, specifying
|
|
/// just the name.
|
|
/// </summary>
|
|
/// <param name="name">The name of the assembly to load.</param>
|
|
public SessionStateAssemblyEntry(string name)
|
|
: base(name)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shallow-clone this object.
|
|
/// </summary>
|
|
/// <returns>The cloned object.</returns>
|
|
public override InitialSessionStateEntry Clone()
|
|
{
|
|
SessionStateAssemblyEntry entry = new SessionStateAssemblyEntry(Name, FileName);
|
|
entry.SetPSSnapIn(this.PSSnapIn);
|
|
entry.SetModule(this.Module);
|
|
return entry;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the assembly file name...
|
|
/// </summary>
|
|
public string FileName { get; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// List a cmdlet to add to this session state entry.
|
|
/// </summary>
|
|
public sealed class SessionStateCmdletEntry : SessionStateCommandEntry
|
|
{
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="implementingType"></param>
|
|
/// <param name="helpFileName"></param>
|
|
public SessionStateCmdletEntry(string name, Type implementingType, string helpFileName)
|
|
: base(name, SessionStateEntryVisibility.Public)
|
|
{
|
|
ImplementingType = implementingType;
|
|
HelpFileName = helpFileName;
|
|
CommandType = CommandTypes.Cmdlet;
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="implementingType"></param>
|
|
/// <param name="helpFileName"></param>
|
|
/// <param name="visibility"></param>
|
|
internal SessionStateCmdletEntry(string name, Type implementingType, string helpFileName, SessionStateEntryVisibility visibility)
|
|
: base(name, visibility)
|
|
{
|
|
ImplementingType = implementingType;
|
|
HelpFileName = helpFileName;
|
|
CommandType = CommandTypes.Cmdlet;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shallow-clone this object...
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override InitialSessionStateEntry Clone()
|
|
{
|
|
SessionStateCmdletEntry entry = new SessionStateCmdletEntry(Name, ImplementingType, HelpFileName, Visibility);
|
|
entry.SetPSSnapIn(this.PSSnapIn);
|
|
entry.SetModule(this.Module);
|
|
return entry;
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public Type ImplementingType { get; }
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public string HelpFileName { get; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public sealed class SessionStateProviderEntry : ConstrainedSessionStateEntry
|
|
{
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="implementingType"></param>
|
|
/// <param name="helpFileName"></param>
|
|
public SessionStateProviderEntry(string name, Type implementingType, string helpFileName)
|
|
: base(name, SessionStateEntryVisibility.Public)
|
|
{
|
|
ImplementingType = implementingType;
|
|
HelpFileName = helpFileName;
|
|
}
|
|
|
|
internal SessionStateProviderEntry(string name, Type implementingType, string helpFileName, SessionStateEntryVisibility visibility)
|
|
: base(name, visibility)
|
|
{
|
|
ImplementingType = implementingType;
|
|
HelpFileName = helpFileName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shallow-clone this object...
|
|
/// </summary>
|
|
/// <returns>The cloned object.</returns>
|
|
public override InitialSessionStateEntry Clone()
|
|
{
|
|
SessionStateProviderEntry entry = new SessionStateProviderEntry(Name, ImplementingType, HelpFileName, this.Visibility);
|
|
entry.SetPSSnapIn(this.PSSnapIn);
|
|
entry.SetModule(this.Module);
|
|
return entry;
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public Type ImplementingType { get; }
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public string HelpFileName { get; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public sealed class SessionStateScriptEntry : SessionStateCommandEntry
|
|
{
|
|
/// <summary>
|
|
/// Create a session state command entry instance.
|
|
/// </summary>
|
|
/// <param name="path">The path to the script.</param>
|
|
public SessionStateScriptEntry(string path)
|
|
: base(path, SessionStateEntryVisibility.Public)
|
|
{
|
|
Path = path;
|
|
CommandType = CommandTypes.ExternalScript;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a session state command entry instance with the specified visibility.
|
|
/// </summary>
|
|
/// <param name="path">The path to the script.</param>
|
|
/// <param name="visibility">Visibility of the script.</param>
|
|
internal SessionStateScriptEntry(string path, SessionStateEntryVisibility visibility)
|
|
: base(path, visibility)
|
|
{
|
|
Path = path;
|
|
CommandType = CommandTypes.ExternalScript;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shallow-clone this object...
|
|
/// </summary>
|
|
/// <returns>The cloned object.</returns>
|
|
public override InitialSessionStateEntry Clone()
|
|
{
|
|
SessionStateScriptEntry entry = new SessionStateScriptEntry(Path, Visibility);
|
|
entry.SetModule(this.Module);
|
|
return entry;
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public string Path { get; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public sealed class SessionStateAliasEntry : SessionStateCommandEntry
|
|
{
|
|
/// <summary>
|
|
/// Define an alias entry to add to the initial session state.
|
|
/// </summary>
|
|
/// <param name="name">The name of the alias entry to add.</param>
|
|
/// <param name="definition">The name of the command it resolves to.</param>
|
|
public SessionStateAliasEntry(string name, string definition)
|
|
: base(name, SessionStateEntryVisibility.Public)
|
|
{
|
|
Definition = definition;
|
|
CommandType = CommandTypes.Alias;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Define an alias entry to add to the initial session state.
|
|
/// </summary>
|
|
/// <param name="name">The name of the alias entry to add.</param>
|
|
/// <param name="definition">The name of the command it resolves to.</param>
|
|
/// <param name="description">A description of the purpose of the alias.</param>
|
|
public SessionStateAliasEntry(string name, string definition, string description)
|
|
: base(name, SessionStateEntryVisibility.Public)
|
|
{
|
|
Definition = definition;
|
|
CommandType = CommandTypes.Alias;
|
|
Description = description;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Define an alias entry to add to the initial session state.
|
|
/// </summary>
|
|
/// <param name="name">The name of the alias entry to add.</param>
|
|
/// <param name="definition">The name of the command it resolves to.</param>
|
|
/// <param name="description">A description of the purpose of the alias.</param>
|
|
/// <param name="options">Options defining the scope visibility, readonly and constant.</param>
|
|
public SessionStateAliasEntry(string name, string definition, string description, ScopedItemOptions options)
|
|
: base(name, SessionStateEntryVisibility.Public)
|
|
{
|
|
Definition = definition;
|
|
CommandType = CommandTypes.Alias;
|
|
Description = description;
|
|
Options = options;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Define an alias entry to add to the initial session state.
|
|
/// </summary>
|
|
/// <param name="name">The name of the alias entry to add.</param>
|
|
/// <param name="definition">The name of the command it resolves to.</param>
|
|
/// <param name="description">A description of the purpose of the alias.</param>
|
|
/// <param name="options">Options defining the scope visibility, readonly and constant.</param>
|
|
/// <param name="visibility"></param>
|
|
internal SessionStateAliasEntry(string name, string definition, string description,
|
|
ScopedItemOptions options, SessionStateEntryVisibility visibility)
|
|
: base(name, visibility)
|
|
{
|
|
Definition = definition;
|
|
CommandType = CommandTypes.Alias;
|
|
Description = description;
|
|
Options = options;
|
|
}
|
|
/// <summary>
|
|
/// Shallow-clone this object...
|
|
/// </summary>
|
|
/// <returns>The cloned object.</returns>
|
|
public override InitialSessionStateEntry Clone()
|
|
{
|
|
SessionStateAliasEntry entry = new SessionStateAliasEntry(Name, Definition, Description, Options, Visibility);
|
|
entry.SetModule(this.Module);
|
|
return entry;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The string defining the body of this alias...
|
|
/// </summary>
|
|
public string Definition { get; }
|
|
|
|
/// <summary>
|
|
/// A string describing this alias...
|
|
/// </summary>
|
|
public string Description { get; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// Options controling scope visibility and setability for this entry.
|
|
/// </summary>
|
|
public ScopedItemOptions Options { get; } = ScopedItemOptions.None;
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public sealed class SessionStateApplicationEntry : SessionStateCommandEntry
|
|
{
|
|
/// <summary>
|
|
/// Used to define a permitted script in this session state. If the path is
|
|
/// "*", then any path is permitted.
|
|
/// </summary>
|
|
/// <param name="path">The full path to the application.</param>
|
|
public SessionStateApplicationEntry(string path)
|
|
: base(path, SessionStateEntryVisibility.Public)
|
|
{
|
|
Path = path;
|
|
CommandType = CommandTypes.Application;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to define a permitted script in this session state. If the path is
|
|
/// "*", then any path is permitted.
|
|
/// </summary>
|
|
/// <param name="path">The full path to the application.</param>
|
|
/// <param name="visibility">Sets the external visibility of the path.</param>
|
|
internal SessionStateApplicationEntry(string path, SessionStateEntryVisibility visibility)
|
|
: base(path, visibility)
|
|
{
|
|
Path = path;
|
|
CommandType = CommandTypes.Application;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shallow-clone this object...
|
|
/// </summary>
|
|
/// <returns>The cloned object.</returns>
|
|
public override InitialSessionStateEntry Clone()
|
|
{
|
|
SessionStateApplicationEntry entry = new SessionStateApplicationEntry(Path, Visibility);
|
|
entry.SetModule(this.Module);
|
|
return entry;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The path to this application...
|
|
/// </summary>
|
|
public string Path { get; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public sealed class SessionStateFunctionEntry : SessionStateCommandEntry
|
|
{
|
|
/// <summary>
|
|
/// Represents a function definition in an Initial session state object.
|
|
/// </summary>
|
|
/// <param name="name">The name of the function.</param>
|
|
/// <param name="definition">The definition of the function.</param>
|
|
/// <param name="options">Options controlling scope-related elements of this object.</param>
|
|
/// <param name="helpFile">The name of the help file associated with the function.</param>
|
|
public SessionStateFunctionEntry(string name, string definition, ScopedItemOptions options, string helpFile)
|
|
: base(name, SessionStateEntryVisibility.Public)
|
|
{
|
|
Definition = definition;
|
|
CommandType = CommandTypes.Function;
|
|
Options = options;
|
|
ScriptBlock = ScriptBlock.Create(Definition);
|
|
ScriptBlock.LanguageMode = PSLanguageMode.FullLanguage;
|
|
|
|
HelpFile = helpFile;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a function definition in an Initial session state object.
|
|
/// </summary>
|
|
/// <param name="name">The name of the function.</param>
|
|
/// <param name="definition">The definition of the function.</param>
|
|
/// <param name="helpFile">The name of the help file associated with the function.</param>
|
|
public SessionStateFunctionEntry(string name, string definition, string helpFile)
|
|
: this(name, definition, ScopedItemOptions.None, helpFile)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a function definition in an Initial session state object.
|
|
/// </summary>
|
|
/// <param name="name">The name of the function.</param>
|
|
/// <param name="definition">The definition of the function.</param>
|
|
public SessionStateFunctionEntry(string name, string definition)
|
|
: this(name, definition, ScopedItemOptions.None, null)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// This is an internal copy constructor.
|
|
/// </summary>
|
|
internal SessionStateFunctionEntry(string name, string definition, ScopedItemOptions options,
|
|
SessionStateEntryVisibility visibility, ScriptBlock scriptBlock, string helpFile)
|
|
: base(name, visibility)
|
|
{
|
|
Definition = definition;
|
|
CommandType = CommandTypes.Function;
|
|
Options = options;
|
|
ScriptBlock = scriptBlock;
|
|
HelpFile = helpFile;
|
|
}
|
|
|
|
internal static SessionStateFunctionEntry GetDelayParsedFunctionEntry(string name, string definition, bool isProductCode, PSLanguageMode languageMode)
|
|
{
|
|
var fnEntry = GetDelayParsedFunctionEntry(name, definition, isProductCode);
|
|
fnEntry.ScriptBlock.LanguageMode = languageMode;
|
|
return fnEntry;
|
|
}
|
|
|
|
internal static SessionStateFunctionEntry GetDelayParsedFunctionEntry(string name, string definition, bool isProductCode)
|
|
{
|
|
var sb = ScriptBlock.CreateDelayParsedScriptBlock(definition, isProductCode);
|
|
return new SessionStateFunctionEntry(name, definition, ScopedItemOptions.None,
|
|
SessionStateEntryVisibility.Public, sb, null);
|
|
}
|
|
|
|
internal static SessionStateFunctionEntry GetDelayParsedFunctionEntry(string name, string definition, ScriptBlock sb)
|
|
{
|
|
return new SessionStateFunctionEntry(name, definition, ScopedItemOptions.None,
|
|
SessionStateEntryVisibility.Public, sb, null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shallow-clone this object...
|
|
/// </summary>
|
|
/// <returns>The cloned object.</returns>
|
|
public override InitialSessionStateEntry Clone()
|
|
{
|
|
SessionStateFunctionEntry entry = new SessionStateFunctionEntry(Name, Definition, Options, Visibility, ScriptBlock, HelpFile);
|
|
entry.SetModule(this.Module);
|
|
return entry;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the name of the help file associated with the function.
|
|
/// </summary>
|
|
internal void SetHelpFile(string help)
|
|
{
|
|
HelpFile = help;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The string to use to define this function...
|
|
/// </summary>
|
|
public string Definition { get; }
|
|
|
|
/// <summary>
|
|
/// The script block for this function.
|
|
/// </summary>
|
|
internal ScriptBlock ScriptBlock { get; set; }
|
|
|
|
/// <summary>
|
|
/// Options controling scope visibility and setability for this entry.
|
|
/// </summary>
|
|
public ScopedItemOptions Options { get; } = ScopedItemOptions.None;
|
|
|
|
/// <summary>
|
|
/// The name of the help file associated with the function.
|
|
/// </summary>
|
|
public string HelpFile { get; private set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public sealed class SessionStateVariableEntry : ConstrainedSessionStateEntry
|
|
{
|
|
/// <summary>
|
|
/// Is used to define a variable that should be created when
|
|
/// the runspace is opened. Note - if this object is cloned,
|
|
/// then the clone will contain a reference to the original object
|
|
/// not a clone of it.
|
|
/// </summary>
|
|
/// <param name="name">The name of the variable.</param>
|
|
/// <param name="value">The value to set the variable to.</param>
|
|
/// <param name="description">A descriptive string to attach to the variable.</param>
|
|
public SessionStateVariableEntry(string name, object value, string description)
|
|
: base(name, SessionStateEntryVisibility.Public)
|
|
{
|
|
Value = value;
|
|
Description = description;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is used to define a variable that should be created when
|
|
/// the runspace is opened. Note - if this object is cloned,
|
|
/// then the clone will contain a reference to the original object
|
|
/// not a clone of it.
|
|
/// </summary>
|
|
/// <param name="name">The name of the variable.</param>
|
|
/// <param name="value">The value to set the variable to.</param>
|
|
/// <param name="description">A descriptive string to attach to the variable.</param>
|
|
/// <param name="options">Options like readonly, constant, allscope, etc.</param>
|
|
public SessionStateVariableEntry(string name, object value, string description, ScopedItemOptions options)
|
|
: base(name, SessionStateEntryVisibility.Public)
|
|
{
|
|
Value = value;
|
|
Description = description;
|
|
Options = options;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is used to define a variable that should be created when
|
|
/// the runspace is opened. Note - if this object is cloned,
|
|
/// then the clone will contain a reference to the original object
|
|
/// not a clone of it.
|
|
/// </summary>
|
|
/// <param name="name">The name of the variable.</param>
|
|
/// <param name="value">The value to set the variable to.</param>
|
|
/// <param name="description">A descriptive string to attach to the variable.</param>
|
|
/// <param name="options">Options like readonly, constant, allscope, etc.</param>
|
|
/// <param name="attributes">A list of attributes to attach to the variable.</param>
|
|
public SessionStateVariableEntry(string name, object value, string description,
|
|
ScopedItemOptions options, Collection<Attribute> attributes)
|
|
: base(name, SessionStateEntryVisibility.Public)
|
|
{
|
|
Value = value;
|
|
Description = description;
|
|
Options = options;
|
|
_attributes = attributes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is used to define a variable that should be created when
|
|
/// the runspace is opened. Note - if this object is cloned,
|
|
/// then the clone will contain a reference to the original object
|
|
/// not a clone of it.
|
|
/// </summary>
|
|
/// <param name="name">The name of the variable.</param>
|
|
/// <param name="value">The value to set the variable to.</param>
|
|
/// <param name="description">A descriptive string to attach to the variable.</param>
|
|
/// <param name="options">Options like readonly, constant, allscope, etc.</param>
|
|
/// <param name="attribute">A single attribute to attach to the variable.</param>
|
|
public SessionStateVariableEntry(string name, object value, string description,
|
|
ScopedItemOptions options, Attribute attribute)
|
|
: base(name, SessionStateEntryVisibility.Public)
|
|
{
|
|
Value = value;
|
|
Description = description;
|
|
Options = options;
|
|
_attributes = new Collection<Attribute>();
|
|
_attributes.Add(attribute);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is used to define a variable that should be created when
|
|
/// the runspace is opened. Note - if this object is cloned,
|
|
/// then the clone will contain a reference to the original object
|
|
/// not a clone of it.
|
|
/// </summary>
|
|
/// <param name="name">The name of the variable.</param>
|
|
/// <param name="value">The value to set the variable to.</param>
|
|
/// <param name="description">A descriptive string to attach to the variable.</param>
|
|
/// <param name="options">Options like readonly, constant, allscope, etc.</param>
|
|
/// <param name="attributes">A single attribute to attach to the variable.</param>
|
|
/// <param name="visibility"></param>
|
|
internal SessionStateVariableEntry(string name, object value, string description,
|
|
ScopedItemOptions options, Collection<Attribute> attributes, SessionStateEntryVisibility visibility)
|
|
: base(name, visibility)
|
|
{
|
|
Value = value;
|
|
Description = description;
|
|
Options = options;
|
|
_attributes = attributes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shallow-clone this object...
|
|
/// </summary>
|
|
/// <returns>The cloned object.</returns>
|
|
public override InitialSessionStateEntry Clone()
|
|
{
|
|
// Copy the attribute collection if necessary...
|
|
Collection<Attribute> attrs = null;
|
|
if (_attributes != null && _attributes.Count > 0)
|
|
attrs = new Collection<Attribute>(_attributes);
|
|
return new SessionStateVariableEntry(Name, Value, Description, Options, attrs, Visibility);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The value to bind to this variable.
|
|
/// </summary>
|
|
public object Value { get; }
|
|
|
|
/// <summary>
|
|
/// The description associated with this variable.
|
|
/// </summary>
|
|
public string Description { get; } = string.Empty;
|
|
|
|
/// <summary>
|
|
/// The options associated with this variable (e.g. readonly, allscope, etc.)
|
|
/// </summary>
|
|
public ScopedItemOptions Options { get; } = ScopedItemOptions.None;
|
|
|
|
/// <summary>
|
|
/// The attributes that will be attached to this object.
|
|
/// </summary>
|
|
public Collection<Attribute> Attributes
|
|
{
|
|
get { return _attributes ?? (_attributes = new Collection<Attribute>()); }
|
|
}
|
|
|
|
private Collection<Attribute> _attributes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
public sealed class InitialSessionStateEntryCollection<T> : IEnumerable<T> where T : InitialSessionStateEntry
|
|
{
|
|
/// <summary>
|
|
/// Create an empty collection...
|
|
/// </summary>
|
|
public InitialSessionStateEntryCollection()
|
|
{
|
|
_internalCollection = new Collection<T>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create an new collection, copying in the passed items...
|
|
/// </summary>
|
|
/// <param name="items"></param>
|
|
public InitialSessionStateEntryCollection(IEnumerable<T> items)
|
|
{
|
|
if (items == null)
|
|
{
|
|
throw new ArgumentNullException("items");
|
|
}
|
|
|
|
_internalCollection = new Collection<T>();
|
|
|
|
foreach (T item in items)
|
|
{
|
|
_internalCollection.Add(item);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clone this collection.
|
|
/// </summary>
|
|
/// <returns>The cloned collection.</returns>
|
|
public InitialSessionStateEntryCollection<T> Clone()
|
|
{
|
|
InitialSessionStateEntryCollection<T> result;
|
|
lock (_syncObject)
|
|
{
|
|
result = new InitialSessionStateEntryCollection<T>();
|
|
|
|
foreach (T item in _internalCollection)
|
|
{
|
|
result.Add((T)item.Clone());
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset the collection.
|
|
/// </summary>
|
|
public void Reset()
|
|
{
|
|
lock (_syncObject)
|
|
{
|
|
_internalCollection.Clear();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a count of the number of items in the collection...
|
|
/// </summary>
|
|
public int Count
|
|
{
|
|
get { return _internalCollection.Count; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <param name="index"></param>
|
|
/// <returns></returns>
|
|
public T this[int index]
|
|
{
|
|
get
|
|
{
|
|
T result;
|
|
lock (_syncObject)
|
|
{
|
|
result = _internalCollection[index];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// To find the entries based on name.
|
|
/// Why collection - Different SnapIn/modules and same entity names.
|
|
/// If used on command collection entry, then for the same name, one can have multiple output.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <returns></returns>
|
|
public Collection<T> this[string name]
|
|
{
|
|
get
|
|
{
|
|
Collection<T> result = new Collection<T>();
|
|
lock (_syncObject)
|
|
{
|
|
foreach (T element in _internalCollection)
|
|
{
|
|
if (element.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
result.Add(element);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find entries based on string name which can include wildcards.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <returns></returns>
|
|
internal Collection<T> LookUpByName(string name)
|
|
{
|
|
if (name == null) { throw new PSArgumentNullException("name"); }
|
|
|
|
Collection<T> result = new Collection<T>();
|
|
WildcardPattern namePattern = WildcardPattern.Get(name, WildcardOptions.IgnoreCase);
|
|
lock (_syncObject)
|
|
{
|
|
foreach (T element in _internalCollection)
|
|
{
|
|
if (namePattern.IsMatch(element.Name))
|
|
{
|
|
result.Add(element);
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <param name="index"></param>
|
|
public void RemoveItem(int index)
|
|
{
|
|
lock (_syncObject)
|
|
{
|
|
_internalCollection.RemoveAt(index);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a number of items starting at the specified index.
|
|
/// </summary>
|
|
/// <param name="index"></param>
|
|
/// <param name="count"></param>
|
|
public void RemoveItem(int index, int count)
|
|
{
|
|
lock (_syncObject)
|
|
{
|
|
while (count-- > 0)
|
|
{
|
|
_internalCollection.RemoveAt(index);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears the collection...
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
lock (_syncObject)
|
|
{
|
|
_internalCollection.Clear();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This overload exists so that we can remove items based on the item name, rather than
|
|
/// its position in the collection. The type argument can be null but we'll throw an error if
|
|
/// we can't distinguish between multiple entries of the same name but different types
|
|
/// and the type hasn't been specified.
|
|
/// BUGBUG - brucepay - the throw thing is not implemented yet...
|
|
/// </summary>
|
|
/// <param name="name">The name of the element to remove.</param>
|
|
/// <param name="type">The type of object to remove, can be null to remove any type.</param>
|
|
public void Remove(string name, object type)
|
|
{
|
|
if (name == null)
|
|
throw new ArgumentNullException("name");
|
|
|
|
lock (_syncObject)
|
|
{
|
|
Type objType = null;
|
|
|
|
if (type != null)
|
|
{
|
|
objType = type as Type ?? type.GetType();
|
|
}
|
|
|
|
// Work backwards through the collection...
|
|
for (int i = _internalCollection.Count - 1; i >= 0; i--)
|
|
{
|
|
T element = _internalCollection[i];
|
|
if (element == null)
|
|
continue;
|
|
if ((objType == null || element.GetType() == objType) &&
|
|
string.Equals(element.Name, name, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
_internalCollection.RemoveAt(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add an item to this collection.
|
|
/// </summary>
|
|
/// <param name="item">The item to add...</param>
|
|
public void Add(T item)
|
|
{
|
|
if (item == null)
|
|
throw new ArgumentNullException("item");
|
|
|
|
lock (_syncObject)
|
|
{
|
|
_internalCollection.Add(item);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add items to this collection.
|
|
/// </summary>
|
|
/// <param name="items"></param>
|
|
public void Add(IEnumerable<T> items)
|
|
{
|
|
if (items == null)
|
|
throw new ArgumentNullException("items");
|
|
|
|
lock (_syncObject)
|
|
{
|
|
foreach (T element in items)
|
|
{
|
|
_internalCollection.Add(element);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Special add for TypeTable type entries that removes redundant file entries.
|
|
/// </summary>
|
|
internal void AddTypeTableTypesInfo(IEnumerable<T> items)
|
|
{
|
|
if (typeof(T) != typeof(SessionStateTypeEntry)) { throw new PSInvalidOperationException(); }
|
|
|
|
lock (_syncObject)
|
|
{
|
|
foreach (var element in items)
|
|
{
|
|
if (element is SessionStateTypeEntry typeEntry && typeEntry.TypeData != null)
|
|
{
|
|
// Skip type file entries.
|
|
_internalCollection.Add(element);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get enumerator for this collection.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <!--
|
|
/// Enumerator work is not thread safe by default. Any code trying
|
|
/// to do enumeration on this collection should lock it first.
|
|
///
|
|
/// Need to document this.
|
|
/// -->
|
|
IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
|
{
|
|
return _internalCollection.GetEnumerator();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get enumerator for this collection.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
/// <!--
|
|
/// Enumerator work is not thread safe by default. Any code trying
|
|
/// to do enumeration on this collection should lock it first.
|
|
///
|
|
/// Need to document this.
|
|
/// -->
|
|
IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()
|
|
{
|
|
return _internalCollection.GetEnumerator();
|
|
}
|
|
|
|
private Collection<T> _internalCollection;
|
|
|
|
// object to use for locking
|
|
private object _syncObject = new object();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows you to define the set of elements that should be
|
|
/// present when Session State is created.
|
|
/// </summary>
|
|
public class InitialSessionState
|
|
{
|
|
#region Helper methods for restricting commands needed by implicit and interactive remoting
|
|
|
|
private static void RemoveDisallowedEntries<T>(InitialSessionStateEntryCollection<T> list, List<string> allowedNames, Func<T, string> nameGetter)
|
|
where T : InitialSessionStateEntry
|
|
{
|
|
List<string> namesToRemove = new List<string>();
|
|
foreach (T entry in list)
|
|
{
|
|
string entryName = nameGetter(entry);
|
|
|
|
// if entryName is not present in allowedNames list, then remove it
|
|
if (!allowedNames.Exists(allowedName => allowedName.Equals(entryName, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
namesToRemove.Add(entry.Name);
|
|
}
|
|
}
|
|
|
|
foreach (string nameToRemove in namesToRemove)
|
|
{
|
|
list.Remove(nameToRemove, null /* remove any type with this name */);
|
|
}
|
|
}
|
|
|
|
private static void MakeDisallowedEntriesPrivate<T>(InitialSessionStateEntryCollection<T> list, List<string> allowedNames, Func<T, string> nameGetter)
|
|
where T : ConstrainedSessionStateEntry
|
|
{
|
|
foreach (T entry in list)
|
|
{
|
|
string entryName = nameGetter(entry);
|
|
|
|
// Aliases to allowed commands are OK
|
|
SessionStateAliasEntry aliasEntry = entry as SessionStateAliasEntry;
|
|
if (aliasEntry != null)
|
|
{
|
|
if (allowedNames.Exists(allowedName => allowedName.Equals(aliasEntry.Definition, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
aliasEntry.Visibility = SessionStateEntryVisibility.Public;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// if entryName is not present in allowedNames list, then remove it
|
|
if (!allowedNames.Exists(allowedName => allowedName.Equals(entryName, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
entry.Visibility = SessionStateEntryVisibility.Private;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an initial session state from a PSSC configuration file.
|
|
/// </summary>
|
|
/// <param name="path">The path to the PSSC session configuration file.</param>
|
|
/// <returns></returns>
|
|
public static InitialSessionState CreateFromSessionConfigurationFile(string path)
|
|
{
|
|
return CreateFromSessionConfigurationFile(path, null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an initial session state from a PSSC configuration file.
|
|
/// </summary>
|
|
/// <param name="path">The path to the PSSC session configuration file.</param>
|
|
/// <param name="roleVerifier">
|
|
/// The verifier that PowerShell should call to determine if groups in the Role entry apply to the
|
|
/// target session. If you have a WindowsPrincipal for a user, for example, create a Function that
|
|
/// checks windowsPrincipal.IsInRole().
|
|
/// </param>
|
|
/// <returns></returns>
|
|
public static InitialSessionState CreateFromSessionConfigurationFile(string path, Func<string, bool> roleVerifier)
|
|
{
|
|
Remoting.DISCPowerShellConfiguration discConfiguration = new Remoting.DISCPowerShellConfiguration(path, roleVerifier);
|
|
return discConfiguration.GetInitialSessionState(null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an <see cref="InitialSessionState"/> instance that exposes only the minimal
|
|
/// set of commands needed by give set of <paramref name="sessionCapabilities"/>.
|
|
/// All commands that are not needed are made private in order to minimize the attack surface.
|
|
/// </summary>
|
|
/// <param name="sessionCapabilities">
|
|
/// What capabilities the session should have.
|
|
/// </param>
|
|
/// <returns></returns>
|
|
public static InitialSessionState CreateRestricted(SessionCapabilities sessionCapabilities)
|
|
{
|
|
// only remote server has been requested
|
|
if (SessionCapabilities.RemoteServer == sessionCapabilities)
|
|
{
|
|
return CreateRestrictedForRemoteServer();
|
|
}
|
|
|
|
return Create();
|
|
}
|
|
|
|
private static InitialSessionState CreateRestrictedForRemoteServer()
|
|
{
|
|
InitialSessionState iss = Create();
|
|
iss.LanguageMode = PSLanguageMode.NoLanguage;
|
|
iss.ThrowOnRunspaceOpenError = true;
|
|
iss.UseFullLanguageModeInDebugger = false;
|
|
|
|
iss.Commands.Add(BuiltInFunctions);
|
|
iss.Commands.Add(BuiltInAliases);
|
|
|
|
// Load the default snapins - all commands will be private by default.
|
|
Collection<PSSnapInInfo> defaultSnapins = PSSnapInReader.ReadEnginePSSnapIns();
|
|
foreach (PSSnapInInfo si in defaultSnapins)
|
|
{
|
|
// ImportPSSnapIn always sets "out warning" to "null"; all our internal calls ignore/discard "out warning"
|
|
PSSnapInException warning;
|
|
iss.ImportPSSnapIn(si, out warning);
|
|
}
|
|
|
|
//
|
|
// restrict what gets exposed
|
|
//
|
|
|
|
List<string> allowedCommands = new List<string>();
|
|
|
|
// required by implicit remoting and interactive remoting
|
|
allowedCommands.Add("Get-Command");
|
|
allowedCommands.Add("Get-FormatData");
|
|
allowedCommands.Add("Clear-Host");
|
|
allowedCommands.Add("Select-Object"); // used to serialize exactly the properties that we need (+ at the right depth)
|
|
// used if available by implicit remoting
|
|
allowedCommands.Add("Get-Help"); // used when displaying help for implicit remoting proxy functions
|
|
allowedCommands.Add("Measure-Object"); // used to have nice progress bars when import/export-pssession is running
|
|
// required by interactive remoting
|
|
allowedCommands.Add("Out-Default"); // appended to every command line
|
|
allowedCommands.Add("Exit-PSSession"); // used by the user to exit the session
|
|
|
|
// We don't remove these entries so that they can be called by commands in the runspace.
|
|
// Setting them to 'Private' ensures that the user can't call them.
|
|
MakeDisallowedEntriesPrivate(
|
|
iss.Commands,
|
|
allowedCommands,
|
|
commandEntry => commandEntry.Name);
|
|
|
|
// Ensure that only PowerShell core formats are included in the restricted session.
|
|
IncludePowerShellCoreFormats(iss);
|
|
|
|
List<string> allowedTypes = new List<string>();
|
|
allowedTypes.Add("types.ps1xml");
|
|
allowedTypes.Add("typesV3.ps1xml");
|
|
RemoveDisallowedEntries(
|
|
iss.Types,
|
|
allowedTypes,
|
|
typeEntry => IO.Path.GetFileName(typeEntry.FileName));
|
|
|
|
// No providers are visible by default
|
|
foreach (SessionStateProviderEntry provider in iss.Providers)
|
|
{
|
|
provider.Visibility = SessionStateEntryVisibility.Private;
|
|
}
|
|
|
|
// Add built-in variables.
|
|
iss.Variables.Add(BuiltInVariables);
|
|
|
|
//
|
|
// wrap some commands in a proxy function to restrict their parameters
|
|
//
|
|
foreach (KeyValuePair<string, CommandMetadata> proxyFunction in CommandMetadata.GetRestrictedCommands(SessionCapabilities.RemoteServer))
|
|
{
|
|
string commandName = proxyFunction.Key;
|
|
|
|
// make the cmdlet private
|
|
Collection<SessionStateCommandEntry> originalCmdlet = iss.Commands[commandName];
|
|
Diagnostics.Assert(originalCmdlet != null, "Proxied cmdlets should be imported at this point");
|
|
Diagnostics.Assert(originalCmdlet.Count == 1, "Exactly one cmdlet with a given name");
|
|
originalCmdlet[0].Visibility = SessionStateEntryVisibility.Private;
|
|
|
|
// and add a public proxy function
|
|
string proxyBody = ProxyCommand.Create(proxyFunction.Value, string.Empty, false);
|
|
iss.Commands.Add(new SessionStateFunctionEntry(commandName, proxyBody));
|
|
}
|
|
|
|
return iss;
|
|
}
|
|
|
|
// Porting note: moved to Platform so we have one list to maintain
|
|
private static string[] s_PSCoreFormatFileNames = Platform.FormatFileNames.ToArray();
|
|
|
|
private static void IncludePowerShellCoreFormats(InitialSessionState iss)
|
|
{
|
|
string psHome = Utils.DefaultPowerShellAppBase;
|
|
if (string.IsNullOrEmpty(psHome))
|
|
{
|
|
return;
|
|
}
|
|
|
|
iss.Formats.Clear();
|
|
foreach (var coreFormat in s_PSCoreFormatFileNames)
|
|
{
|
|
iss.Formats.Add(new SessionStateFormatEntry(Path.Combine(psHome, coreFormat)));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Ctor for Custom-Shell - Do we need this?
|
|
/// </summary>
|
|
protected InitialSessionState()
|
|
{
|
|
}
|
|
|
|
// Creates an empty EE
|
|
/// <summary>
|
|
/// Creates an empty InitialSessionState object...
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static InitialSessionState Create()
|
|
{
|
|
InitialSessionState iss = new InitialSessionState();
|
|
|
|
// TODO: the following code is probably needed for the hosted constrained runspace
|
|
// There are too many things that depend on the built-in variables. At the same time,
|
|
// these variables can't be public or they become a security issue.
|
|
// This change still needs to be spec-reviewed before turning it on. It also seems to
|
|
// be causing test failures - i suspect due to lack test isolation - brucepay Mar 06/2008
|
|
#if false
|
|
// Add the default variables and make them private...
|
|
iss.Variables.Add(BuiltInVariables);
|
|
foreach (SessionStateVariableEntry v in iss.Variables)
|
|
{
|
|
v.Visibility = SessionStateEntryVisibility.Private;
|
|
}
|
|
#endif
|
|
return iss;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates the default PowerShell one with default cmdlets, provider etc.
|
|
/// BuiltIn functions, aliases need to be available through default
|
|
/// InitialSessionstate constructor. Need to have this discussion for packaging as well.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static InitialSessionState CreateDefault()
|
|
{
|
|
// Read all of the registered PSSnapins
|
|
Collection<PSSnapInInfo> defaultSnapins;
|
|
PSSnapInException warning;
|
|
|
|
InitialSessionState ss = new InitialSessionState();
|
|
|
|
ss.Variables.Add(BuiltInVariables);
|
|
ss.Commands.Add(new SessionStateApplicationEntry("*"));
|
|
ss.Commands.Add(new SessionStateScriptEntry("*"));
|
|
ss.Commands.Add(BuiltInFunctions);
|
|
ss.Commands.Add(BuiltInAliases);
|
|
|
|
defaultSnapins = PSSnapInReader.ReadEnginePSSnapIns();
|
|
|
|
foreach (PSSnapInInfo si in defaultSnapins)
|
|
{
|
|
try
|
|
{
|
|
ss.ImportPSSnapIn(si, out warning);
|
|
}
|
|
catch (PSSnapInException pse)
|
|
{
|
|
throw pse;
|
|
}
|
|
#if DEBUG
|
|
// NOTE:
|
|
// This code is for testing a module-based shell. It is only available when the shell is complied
|
|
// in debug mode and is not intended to be a feature.
|
|
// July 31 2008 - brucepay
|
|
// Only load the core snapins at this point...
|
|
if (Environment.GetEnvironmentVariable("PowerShellMinimal") != null)
|
|
{
|
|
if (si.Name.Equals("Microsoft.PowerShell.Host", StringComparison.OrdinalIgnoreCase))
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Remove duplicated assemblies
|
|
HashSet<string> assemblyList = new HashSet<string>();
|
|
|
|
for (int i = ss.Assemblies.Count - 1; i >= 0; i--)
|
|
{
|
|
string assembly = ss.Assemblies[i].FileName;
|
|
if (!string.IsNullOrEmpty(assembly))
|
|
{
|
|
if (assemblyList.Contains(assembly))
|
|
{
|
|
ss.Assemblies.RemoveItem(i);
|
|
}
|
|
else
|
|
{
|
|
assemblyList.Add(assembly);
|
|
}
|
|
}
|
|
}
|
|
|
|
ss.LanguageMode = PSLanguageMode.FullLanguage;
|
|
ss.AuthorizationManager = new Microsoft.PowerShell.PSAuthorizationManager(Utils.DefaultPowerShellShellID);
|
|
|
|
return ss.Clone();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates the default PowerShell one with default cmdlets, provider etc.
|
|
/// The default cmdlets, provider, etc are loaded via Modules.
|
|
/// For loading Microsoft.PowerShell.Core module only.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static InitialSessionState CreateDefault2()
|
|
{
|
|
InitialSessionState ss = new InitialSessionState();
|
|
|
|
ss.Variables.Add(BuiltInVariables);
|
|
ss.Commands.Add(new SessionStateApplicationEntry("*"));
|
|
ss.Commands.Add(new SessionStateScriptEntry("*"));
|
|
ss.Commands.Add(BuiltInFunctions);
|
|
ss.Commands.Add(BuiltInAliases);
|
|
|
|
ss.ImportCorePSSnapIn();
|
|
ss.LanguageMode = PSLanguageMode.FullLanguage;
|
|
ss.AuthorizationManager = new Microsoft.PowerShell.PSAuthorizationManager(Utils.DefaultPowerShellShellID);
|
|
|
|
return ss.Clone();
|
|
}
|
|
|
|
internal static bool IsEngineModule(string moduleName)
|
|
{
|
|
return EngineModules.Contains(moduleName) || NestedEngineModules.Contains(moduleName);
|
|
}
|
|
|
|
internal static bool IsNestedEngineModule(string moduleName)
|
|
{
|
|
return NestedEngineModules.Contains(moduleName);
|
|
}
|
|
|
|
internal static bool IsConstantEngineModule(string moduleName)
|
|
{
|
|
return ConstantEngineModules.Contains(moduleName) || ConstantEngineNestedModules.Contains(moduleName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clone this InitialSessionState object. The collections are
|
|
/// recursively cloned as well as the elements in the collections.
|
|
/// Note however, that the contents of the individual entries
|
|
/// are not deep-cloned. This is only an issue for variable
|
|
/// entries which may have reference types. These objects
|
|
/// will be added by reference rather than by value.
|
|
/// </summary>
|
|
/// <returns>The cloned object.</returns>
|
|
public InitialSessionState Clone()
|
|
{
|
|
InitialSessionState ss = new InitialSessionState();
|
|
|
|
ss.Variables.Add(this.Variables.Clone());
|
|
ss.EnvironmentVariables.Add(this.EnvironmentVariables.Clone());
|
|
ss.Commands.Add(this.Commands.Clone());
|
|
ss.Assemblies.Add(this.Assemblies.Clone());
|
|
ss.Providers.Add(this.Providers.Clone());
|
|
ss.Types.Add(this.Types.Clone());
|
|
ss.Formats.Add(this.Formats.Clone());
|
|
|
|
foreach (string startupScript in this.StartupScripts)
|
|
{
|
|
ss.StartupScripts.Add(startupScript);
|
|
}
|
|
|
|
foreach (string unresolvedCommandsToExpose in this.UnresolvedCommandsToExpose)
|
|
{
|
|
ss.UnresolvedCommandsToExpose.Add(unresolvedCommandsToExpose);
|
|
}
|
|
|
|
foreach (Hashtable dynamicVariableToDefine in this.DynamicVariablesToDefine)
|
|
{
|
|
ss.DynamicVariablesToDefine.Add(dynamicVariableToDefine);
|
|
}
|
|
|
|
foreach (var pair in this.CommandModifications)
|
|
{
|
|
ss.CommandModifications.Add(pair.Key, pair.Value);
|
|
}
|
|
|
|
ss.DefaultCommandVisibility = this.DefaultCommandVisibility;
|
|
ss.AuthorizationManager = this.AuthorizationManager;
|
|
ss.LanguageMode = this.LanguageMode;
|
|
ss.TranscriptDirectory = this.TranscriptDirectory;
|
|
ss.UserDriveEnabled = this.UserDriveEnabled;
|
|
ss.UserDriveUserName = this.UserDriveUserName;
|
|
ss.UserDriveMaximumSize = this.UserDriveMaximumSize;
|
|
|
|
if (_wasExecutionPolicySet)
|
|
{
|
|
ss.ExecutionPolicy = this.ExecutionPolicy;
|
|
}
|
|
|
|
ss.UseFullLanguageModeInDebugger = this.UseFullLanguageModeInDebugger;
|
|
ss.ThreadOptions = this.ThreadOptions;
|
|
ss.ThrowOnRunspaceOpenError = this.ThrowOnRunspaceOpenError;
|
|
#if !CORECLR // No ApartmentState In CoreCLR
|
|
ss.ApartmentState = this.ApartmentState;
|
|
#endif
|
|
|
|
foreach (ModuleSpecification modSpec in this.ModuleSpecificationsToImport)
|
|
{
|
|
ss.ModuleSpecificationsToImport.Add(modSpec);
|
|
}
|
|
|
|
foreach (string mod in this.CoreModulesToImport)
|
|
{
|
|
ss.CoreModulesToImport.Add(mod);
|
|
}
|
|
|
|
ss.DisableFormatUpdates = this.DisableFormatUpdates;
|
|
|
|
foreach (var s in this.defaultSnapins)
|
|
{
|
|
ss.defaultSnapins.Add(s);
|
|
}
|
|
|
|
foreach (var s in ImportedSnapins)
|
|
{
|
|
ss.ImportedSnapins.Add(s.Key, s.Value);
|
|
}
|
|
|
|
return ss;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Want to get away from SnapIn and console file. Have modules and assemblies instead.
|
|
/// Specify the registered SnapIn name or name collection.
|
|
/// </summary>
|
|
/// <param name="snapInName"></param>
|
|
/// <returns></returns>
|
|
public static InitialSessionState Create(string snapInName)
|
|
{
|
|
return new InitialSessionState();
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <param name="snapInNameCollection"></param>
|
|
/// <param name="warning"></param>
|
|
/// <returns></returns>
|
|
public static InitialSessionState Create(string[] snapInNameCollection, out PSConsoleLoadException warning)
|
|
{
|
|
warning = null;
|
|
return new InitialSessionState();
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <param name="snapInPath"></param>
|
|
/// <param name="warnings"></param>
|
|
/// <returns></returns>
|
|
public static InitialSessionState CreateFrom(string snapInPath, out PSConsoleLoadException warnings)
|
|
{
|
|
warnings = null;
|
|
return new InitialSessionState();
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <param name="snapInPathCollection"></param>
|
|
/// <param name="warnings"></param>
|
|
/// <returns></returns>
|
|
public static InitialSessionState CreateFrom(string[] snapInPathCollection, out PSConsoleLoadException warnings)
|
|
{
|
|
warnings = null;
|
|
return new InitialSessionState();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies the language mode to be used for this session state instance.
|
|
/// </summary>
|
|
public PSLanguageMode LanguageMode { get; set; } = PSLanguageMode.NoLanguage;
|
|
|
|
/// <summary>
|
|
/// Specifies the directory to be used for collection session transcripts.
|
|
/// </summary>
|
|
public string TranscriptDirectory { get; set; } = null;
|
|
|
|
/// <summary>
|
|
/// True when session opted for a User PSDrive.
|
|
/// </summary>
|
|
internal bool UserDriveEnabled
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// User name for the user drive. This will be part of the root path for the User PSDrive.
|
|
/// </summary>
|
|
internal string UserDriveUserName
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Optional maximum size value for User drive (in bytes).
|
|
/// </summary>
|
|
internal long UserDriveMaximumSize
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Forces all session script input parameters to have validation.
|
|
/// </summary>
|
|
internal bool EnforceInputParameterValidation
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies the execution policy to be used for this session state instance.
|
|
/// </summary>
|
|
public Microsoft.PowerShell.ExecutionPolicy ExecutionPolicy
|
|
{
|
|
get { return _executionPolicy; }
|
|
|
|
set
|
|
{
|
|
_executionPolicy = value;
|
|
_wasExecutionPolicySet = true;
|
|
}
|
|
}
|
|
|
|
private Microsoft.PowerShell.ExecutionPolicy _executionPolicy = Microsoft.PowerShell.ExecutionPolicy.Default;
|
|
private bool _wasExecutionPolicySet = false;
|
|
|
|
/// <summary>
|
|
/// If true the PowerShell debugger will use FullLanguage mode, otherwise it will use the current language mode.
|
|
/// </summary>
|
|
public bool UseFullLanguageModeInDebugger { get; set; } = false;
|
|
|
|
#if !CORECLR // No ApartmentState In CoreCLR
|
|
/// <summary>
|
|
/// ApartmentState of the thread used to execute commands.
|
|
/// </summary>
|
|
public ApartmentState ApartmentState { get; set; } = Runspace.DefaultApartmentState;
|
|
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// This property determines whether a new thread is created for each invocation of a command.
|
|
/// </summary>
|
|
public PSThreadOptions ThreadOptions { get; set; } = PSThreadOptions.Default;
|
|
|
|
/// <summary>
|
|
/// If this property is set and there was a runspace creation error, then
|
|
/// throw an exception, otherwise just continue creating the runspace even though it may
|
|
/// be in an inconsistent state.
|
|
/// </summary>
|
|
public bool ThrowOnRunspaceOpenError { get; set; } = false;
|
|
|
|
/// <summary>
|
|
/// This property will be set only if we are refreshing the Type/Format settings by calling UpdateTypes/UpdateFormats directly.
|
|
/// In this case, we should wait until all type/format entries get processed. After that, if there were errors
|
|
/// generated, we throw them as an exception.
|
|
/// </summary>
|
|
internal bool RefreshTypeAndFormatSetting = false;
|
|
|
|
/// <summary>
|
|
/// Specifies the authorization manager to be used for this session state instance.
|
|
/// If no authorization manager is specified, then the default authorization manager
|
|
/// for PowerShell will be used which checks the ExecutionPolicy before running a command.
|
|
/// </summary>
|
|
public virtual AuthorizationManager AuthorizationManager { get; set; } = new Microsoft.PowerShell.PSAuthorizationManager(Utils.DefaultPowerShellShellID);
|
|
|
|
internal PSHost Host = null;
|
|
|
|
/// <summary>
|
|
/// Add a list of modules to import when the runspace is created.
|
|
/// </summary>
|
|
/// <param name="name">The modules to add.</param>
|
|
/// <returns></returns>
|
|
public void ImportPSModule(params string[] name)
|
|
{
|
|
if (name == null)
|
|
{
|
|
throw new ArgumentNullException("name");
|
|
}
|
|
|
|
foreach (string n in name)
|
|
{
|
|
ModuleSpecificationsToImport.Add(new ModuleSpecification(n));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears ImportPSModule list.
|
|
/// </summary>
|
|
internal void ClearPSModules()
|
|
{
|
|
ModuleSpecificationsToImport.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a list of modules to import when the runspace is created.
|
|
/// </summary>
|
|
/// <param name="modules">
|
|
/// The modules, whose specifications are specified by <paramref name="modules"/>,
|
|
/// to add.
|
|
/// </param>
|
|
public void ImportPSModule(IEnumerable<ModuleSpecification> modules)
|
|
{
|
|
if (modules == null)
|
|
{
|
|
throw new ArgumentNullException("modules");
|
|
}
|
|
|
|
foreach (var moduleSpecification in modules)
|
|
{
|
|
ModuleSpecificationsToImport.Add(moduleSpecification);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Imports all the modules from the specified module path by default.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// Path from which all modules need to be imported.
|
|
/// </param>
|
|
public void ImportPSModulesFromPath(string path)
|
|
{
|
|
string expandedpath = Environment.ExpandEnvironmentVariables(path);
|
|
var availableModuleFiles = ModuleUtils.GetDefaultAvailableModuleFiles(expandedpath);
|
|
ImportPSModule(availableModuleFiles.ToArray());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a list of core modules to import when the runspace is created.
|
|
/// </summary>
|
|
/// <param name="name">The modules to add.</param>
|
|
/// <returns></returns>
|
|
internal void ImportPSCoreModule(string[] name)
|
|
{
|
|
if (name == null)
|
|
throw new ArgumentNullException("name");
|
|
foreach (string n in name)
|
|
{
|
|
CoreModulesToImport.Add(n);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Imported modules.
|
|
/// </summary>
|
|
public ReadOnlyCollection<ModuleSpecification> Modules
|
|
{
|
|
get { return new ReadOnlyCollection<ModuleSpecification>(ModuleSpecificationsToImport); }
|
|
}
|
|
|
|
internal Collection<ModuleSpecification> ModuleSpecificationsToImport { get; } = new Collection<ModuleSpecification>();
|
|
|
|
internal Dictionary<string, PSSnapInInfo> ImportedSnapins { get; } = new Dictionary<string, PSSnapInInfo>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
/// <summary>
|
|
/// Gets the dictionary of core modules to import on runspace creation...
|
|
/// </summary>
|
|
internal HashSet<string> CoreModulesToImport { get; } = new HashSet<string>();
|
|
|
|
/// <summary>
|
|
/// The list of assemblies to load...
|
|
/// </summary>
|
|
public virtual InitialSessionStateEntryCollection<SessionStateAssemblyEntry> Assemblies
|
|
{
|
|
get
|
|
{
|
|
if (_assemblies == null)
|
|
Interlocked.CompareExchange(ref _assemblies, new InitialSessionStateEntryCollection<SessionStateAssemblyEntry>(), null);
|
|
return _assemblies;
|
|
}
|
|
}
|
|
|
|
private InitialSessionStateEntryCollection<SessionStateAssemblyEntry> _assemblies;
|
|
|
|
/// <summary>
|
|
/// List of types to use for this session state instance...
|
|
/// </summary>
|
|
public virtual InitialSessionStateEntryCollection<SessionStateTypeEntry> Types
|
|
{
|
|
get
|
|
{
|
|
if (_types == null)
|
|
Interlocked.CompareExchange(ref _types, new InitialSessionStateEntryCollection<SessionStateTypeEntry>(), null);
|
|
return _types;
|
|
}
|
|
}
|
|
|
|
private InitialSessionStateEntryCollection<SessionStateTypeEntry> _types;
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public virtual InitialSessionStateEntryCollection<SessionStateFormatEntry> Formats
|
|
{
|
|
get
|
|
{
|
|
if (_formats == null)
|
|
Interlocked.CompareExchange(ref _formats, new InitialSessionStateEntryCollection<SessionStateFormatEntry>(), null);
|
|
return _formats;
|
|
}
|
|
}
|
|
|
|
private InitialSessionStateEntryCollection<SessionStateFormatEntry> _formats;
|
|
|
|
/// <summary>
|
|
/// If set to true, disables any updates to format table. This includes disabling
|
|
/// format table updates through Update-FormatData, Import-Module etc.
|
|
/// All the disabling happens silently ie., the user will not get any exception.
|
|
/// By default, this is set to False.
|
|
/// </summary>
|
|
public bool DisableFormatUpdates { get; set; }
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public virtual InitialSessionStateEntryCollection<SessionStateProviderEntry> Providers
|
|
{
|
|
get
|
|
{
|
|
if (_providers == null)
|
|
Interlocked.CompareExchange(ref _providers, new InitialSessionStateEntryCollection<SessionStateProviderEntry>(), null);
|
|
return _providers;
|
|
}
|
|
}
|
|
|
|
private InitialSessionStateEntryCollection<SessionStateProviderEntry> _providers;
|
|
|
|
/// <summary>
|
|
/// List of commands (Alias, Application, Cmdlets, Function, Script) for this entry.
|
|
/// </summary>
|
|
public virtual InitialSessionStateEntryCollection<SessionStateCommandEntry> Commands
|
|
{
|
|
get
|
|
{
|
|
if (_commands == null)
|
|
Interlocked.CompareExchange(ref _commands, new InitialSessionStateEntryCollection<SessionStateCommandEntry>(), null);
|
|
return _commands;
|
|
}
|
|
}
|
|
|
|
private InitialSessionStateEntryCollection<SessionStateCommandEntry> _commands;
|
|
|
|
internal SessionStateEntryVisibility DefaultCommandVisibility { get; set; }
|
|
|
|
internal HashSet<string> UnresolvedCommandsToExpose
|
|
{
|
|
get
|
|
{
|
|
if (_unresolvedCommandsToExpose == null)
|
|
Interlocked.CompareExchange(ref _unresolvedCommandsToExpose, new HashSet<string>(StringComparer.OrdinalIgnoreCase), null);
|
|
return _unresolvedCommandsToExpose;
|
|
}
|
|
}
|
|
|
|
private HashSet<string> _unresolvedCommandsToExpose;
|
|
|
|
internal Dictionary<string, Hashtable> CommandModifications
|
|
{
|
|
get
|
|
{
|
|
if (_commandModifications == null)
|
|
Interlocked.CompareExchange(ref _commandModifications, new Dictionary<string, Hashtable>(StringComparer.OrdinalIgnoreCase), null);
|
|
return _commandModifications;
|
|
}
|
|
}
|
|
|
|
private Dictionary<string, Hashtable> _commandModifications;
|
|
|
|
internal List<Hashtable> DynamicVariablesToDefine
|
|
{
|
|
get
|
|
{
|
|
if (_dynamicVariablesToDefine == null)
|
|
Interlocked.CompareExchange(ref _dynamicVariablesToDefine, new List<Hashtable>(), null);
|
|
return _dynamicVariablesToDefine;
|
|
}
|
|
}
|
|
|
|
private List<Hashtable> _dynamicVariablesToDefine;
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public virtual InitialSessionStateEntryCollection<SessionStateVariableEntry> Variables
|
|
{
|
|
get
|
|
{
|
|
if (_variables == null)
|
|
Interlocked.CompareExchange(ref _variables, new InitialSessionStateEntryCollection<SessionStateVariableEntry>(), null);
|
|
return _variables;
|
|
}
|
|
}
|
|
|
|
private InitialSessionStateEntryCollection<SessionStateVariableEntry> _variables;
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public virtual InitialSessionStateEntryCollection<SessionStateVariableEntry> EnvironmentVariables
|
|
{
|
|
get
|
|
{
|
|
if (_environmentVariables == null)
|
|
Interlocked.CompareExchange(ref _environmentVariables, new InitialSessionStateEntryCollection<SessionStateVariableEntry>(), null);
|
|
return _environmentVariables;
|
|
}
|
|
}
|
|
|
|
private InitialSessionStateEntryCollection<SessionStateVariableEntry> _environmentVariables;
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
public virtual HashSet<string> StartupScripts
|
|
{
|
|
get
|
|
{
|
|
if (_startupScripts == null)
|
|
Interlocked.CompareExchange(ref _startupScripts, new HashSet<string>(), null);
|
|
return _startupScripts;
|
|
}
|
|
}
|
|
|
|
private HashSet<string> _startupScripts = new HashSet<string>();
|
|
|
|
private object _syncObject = new Object();
|
|
|
|
internal void Bind(ExecutionContext context, bool updateOnly)
|
|
{
|
|
Bind(context, updateOnly, null, /*noClobber*/false, /*local*/ false);
|
|
}
|
|
|
|
internal void Bind(ExecutionContext context, bool updateOnly, PSModuleInfo module, bool noClobber, bool local)
|
|
{
|
|
Host = context.EngineHostInterface;
|
|
lock (_syncObject)
|
|
{
|
|
SessionStateInternal ss = context.EngineSessionState;
|
|
|
|
// Clear the application and script collections...
|
|
if (!updateOnly)
|
|
{
|
|
ss.Applications.Clear();
|
|
ss.Scripts.Clear();
|
|
}
|
|
|
|
// If the initial session state made some commands private by way of
|
|
// VisibleCmdlets / etc., then change the default command visibility for
|
|
// the session state so that newly imported modules aren't exposed accidentally.
|
|
if (DefaultCommandVisibility == SessionStateEntryVisibility.Private)
|
|
{
|
|
ss.DefaultCommandVisibility = SessionStateEntryVisibility.Private;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Load assemblies before anything else - we may need to resolve types in the loaded
|
|
// assemblies as part of loading formats or types, and that can't be done in parallel.
|
|
Bind_LoadAssemblies(context);
|
|
|
|
var actions = new Action[]
|
|
{
|
|
() => Bind_UpdateTypes(context, updateOnly),
|
|
() => Bind_UpdateFormats(context, updateOnly),
|
|
() => Bind_BindCommands(module, noClobber, local, ss),
|
|
() => Bind_LoadProviders(ss),
|
|
() => Bind_SetVariables(ss),
|
|
Bind_SetEnvironment
|
|
};
|
|
|
|
if (updateOnly)
|
|
{
|
|
// We're typically called to import a module. It seems like this could
|
|
// still happen in parallel, but calls to WriteError on the wrong thread
|
|
// get silently swallowed, so instead just run the actions serially on this thread.
|
|
foreach (var action in actions)
|
|
{
|
|
action();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Parallel.Invoke(actions);
|
|
}
|
|
}
|
|
catch (AggregateException e)
|
|
{
|
|
e = e.Flatten();
|
|
foreach (var exception in e.InnerExceptions)
|
|
{
|
|
MshLog.LogEngineHealthEvent(
|
|
context,
|
|
MshLog.EVENT_ID_CONFIGURATION_FAILURE,
|
|
exception,
|
|
Severity.Warning);
|
|
}
|
|
|
|
if (this.ThrowOnRunspaceOpenError)
|
|
{
|
|
// Just throw the first error
|
|
throw e.InnerExceptions[0];
|
|
}
|
|
|
|
context.ReportEngineStartupError(e.Message);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
MshLog.LogEngineHealthEvent(
|
|
context,
|
|
MshLog.EVENT_ID_CONFIGURATION_FAILURE,
|
|
e,
|
|
Severity.Warning);
|
|
|
|
if (this.ThrowOnRunspaceOpenError)
|
|
{
|
|
throw;
|
|
}
|
|
|
|
context.ReportEngineStartupError(e.Message);
|
|
}
|
|
|
|
// Set the language mode
|
|
if (!updateOnly)
|
|
{
|
|
ss.LanguageMode = LanguageMode;
|
|
}
|
|
|
|
// Set the execution policy
|
|
if (_wasExecutionPolicySet)
|
|
{
|
|
string shellId = context.ShellID;
|
|
SecuritySupport.SetExecutionPolicy(Microsoft.PowerShell.ExecutionPolicyScope.Process, ExecutionPolicy, shellId);
|
|
}
|
|
}
|
|
|
|
SetSessionStateDrive(context, setLocation: false);
|
|
}
|
|
|
|
private void Bind_SetVariables(SessionStateInternal ss)
|
|
{
|
|
bool etwEnabled = RunspaceEventSource.Log.IsEnabled();
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadVariablesStart();
|
|
|
|
// Add all of the variables to session state...
|
|
foreach (SessionStateVariableEntry var in Variables)
|
|
{
|
|
ss.AddSessionStateEntry(var);
|
|
}
|
|
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadVariablesStop();
|
|
}
|
|
|
|
private void Bind_SetEnvironment()
|
|
{
|
|
bool etwEnabled = RunspaceEventSource.Log.IsEnabled();
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadEnvironmentVariablesStart();
|
|
|
|
foreach (SessionStateVariableEntry var in EnvironmentVariables)
|
|
{
|
|
Environment.SetEnvironmentVariable(var.Name, var.Value.ToString());
|
|
}
|
|
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadEnvironmentVariablesStop();
|
|
}
|
|
|
|
private void Bind_UpdateTypes(ExecutionContext context, bool updateOnly)
|
|
{
|
|
bool etwEnabled = RunspaceEventSource.Log.IsEnabled();
|
|
if (etwEnabled) RunspaceEventSource.Log.UpdateTypeTableStart();
|
|
this.UpdateTypes(context, updateOnly);
|
|
if (etwEnabled) RunspaceEventSource.Log.UpdateTypeTableStop();
|
|
}
|
|
|
|
private void Bind_UpdateFormats(ExecutionContext context, bool updateOnly)
|
|
{
|
|
bool etwEnabled = RunspaceEventSource.Log.IsEnabled();
|
|
if (etwEnabled) RunspaceEventSource.Log.UpdateFormatTableStart();
|
|
|
|
this.UpdateFormats(context, updateOnly);
|
|
|
|
if (etwEnabled) RunspaceEventSource.Log.UpdateFormatTableStop();
|
|
}
|
|
|
|
private void Bind_LoadProviders(SessionStateInternal ss)
|
|
{
|
|
bool etwEnabled = RunspaceEventSource.Log.IsEnabled();
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadProvidersStart();
|
|
|
|
// Add all of the providers to session state...
|
|
foreach (SessionStateProviderEntry provider in Providers)
|
|
{
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadProviderStart(provider.Name);
|
|
ss.AddSessionStateEntry(provider);
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadProviderStop(provider.Name);
|
|
}
|
|
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadProvidersStop();
|
|
}
|
|
|
|
private void Bind_BindCommands(PSModuleInfo module, bool noClobber, bool local, SessionStateInternal ss)
|
|
{
|
|
bool etwEnabled = RunspaceEventSource.Log.IsEnabled();
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadCommandsStart();
|
|
|
|
foreach (SessionStateCommandEntry cmd in Commands)
|
|
{
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadCommandStart(cmd.Name);
|
|
|
|
SessionStateCmdletEntry ssce = cmd as SessionStateCmdletEntry;
|
|
if (ssce != null)
|
|
{
|
|
if (noClobber && ModuleCmdletBase.CommandFound(ssce.Name, ss))
|
|
{
|
|
ssce._isImported = false;
|
|
continue;
|
|
}
|
|
|
|
ss.AddSessionStateEntry(ssce, local);
|
|
|
|
cmd.SetModule(module);
|
|
continue;
|
|
}
|
|
|
|
cmd.SetModule(module);
|
|
|
|
SessionStateFunctionEntry ssfe = cmd as SessionStateFunctionEntry;
|
|
if (ssfe != null)
|
|
{
|
|
ss.AddSessionStateEntry(ssfe);
|
|
continue;
|
|
}
|
|
|
|
SessionStateAliasEntry ssae = cmd as SessionStateAliasEntry;
|
|
if (ssae != null)
|
|
{
|
|
ss.AddSessionStateEntry(ssae, StringLiterals.Local);
|
|
continue;
|
|
}
|
|
|
|
SessionStateApplicationEntry ssappe = cmd as SessionStateApplicationEntry;
|
|
if (ssappe != null)
|
|
{
|
|
if (ssappe.Visibility == SessionStateEntryVisibility.Public)
|
|
{
|
|
ss.AddSessionStateEntry(ssappe);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
SessionStateScriptEntry ssse = cmd as SessionStateScriptEntry;
|
|
if (ssse != null)
|
|
{
|
|
if (ssse.Visibility == SessionStateEntryVisibility.Public)
|
|
{
|
|
ss.AddSessionStateEntry(ssse);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadCommandStop(cmd.Name);
|
|
}
|
|
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadCommandsStop();
|
|
}
|
|
|
|
private void Bind_LoadAssemblies(ExecutionContext context)
|
|
{
|
|
bool etwEnabled = RunspaceEventSource.Log.IsEnabled();
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadAssembliesStart();
|
|
|
|
// Load the assemblies and initialize the assembly cache...
|
|
foreach (SessionStateAssemblyEntry ssae in Assemblies)
|
|
{
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadAssemblyStart(ssae.Name, ssae.FileName);
|
|
Exception error = null;
|
|
Assembly asm = context.AddAssembly(ssae.Name, ssae.FileName, out error);
|
|
|
|
if (asm == null || error != null)
|
|
{
|
|
// If no module was found but there was no specific error, then
|
|
// create a not found error.
|
|
if (error == null)
|
|
{
|
|
string msg = StringUtil.Format(global::Modules.ModuleAssemblyFound, ssae.Name);
|
|
error = new DllNotFoundException(msg);
|
|
}
|
|
|
|
// If this occurs while loading a module manifest, just
|
|
// throw the exception instead of writing it out...
|
|
if ((!string.IsNullOrEmpty(context.ModuleBeingProcessed) &&
|
|
Path.GetExtension(context.ModuleBeingProcessed)
|
|
.Equals(StringLiterals.PowerShellDataFileExtension,
|
|
StringComparison.OrdinalIgnoreCase)) ||
|
|
ThrowOnRunspaceOpenError)
|
|
{
|
|
throw error;
|
|
}
|
|
else
|
|
{
|
|
context.ReportEngineStartupError(error.Message);
|
|
}
|
|
}
|
|
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadAssemblyStop(ssae.Name, ssae.FileName);
|
|
}
|
|
|
|
if (etwEnabled) RunspaceEventSource.Log.LoadAssembliesStop();
|
|
}
|
|
|
|
internal Exception BindRunspace(Runspace initializedRunspace, PSTraceSource runspaceInitTracer)
|
|
{
|
|
// Get the initial list of public commands from session in a lazy way, so that we can defer
|
|
// the work until it's actually needed.
|
|
//
|
|
// We could use Lazy<> with an initializer for the same purpose, but we can save allocations
|
|
// by using the local function. It avoids allocating the delegate, and it's more efficient on
|
|
// capturing variables from the enclosing scope by using a struct.
|
|
HashSet<CommandInfo> publicCommands = null;
|
|
HashSet<CommandInfo> GetPublicCommands()
|
|
{
|
|
if (publicCommands != null)
|
|
{
|
|
return publicCommands;
|
|
}
|
|
|
|
publicCommands = new HashSet<CommandInfo>();
|
|
foreach (CommandInfo sessionCommand in initializedRunspace.ExecutionContext.SessionState.InvokeCommand.GetCommands(
|
|
"*", CommandTypes.Alias | CommandTypes.Function | CommandTypes.Filter | CommandTypes.Cmdlet, nameIsPattern: true))
|
|
{
|
|
if (sessionCommand.Visibility == SessionStateEntryVisibility.Public)
|
|
{
|
|
publicCommands.Add(sessionCommand);
|
|
}
|
|
}
|
|
|
|
return publicCommands;
|
|
}
|
|
|
|
var unresolvedCmdsToExpose = new HashSet<string>(this.UnresolvedCommandsToExpose, StringComparer.OrdinalIgnoreCase);
|
|
if (CoreModulesToImport.Count > 0 || unresolvedCmdsToExpose.Count > 0)
|
|
{
|
|
// If a user has any module with the same name as that of the core module( or nested module inside the core module)
|
|
// in his module path, then that will get loaded instead of the actual nested module (from the GAC - in our case)
|
|
// Hence, searching only from the system module path while loading the core modules
|
|
ProcessModulesToImport(initializedRunspace, CoreModulesToImport, ModuleIntrinsics.GetPSHomeModulePath(), GetPublicCommands(), unresolvedCmdsToExpose);
|
|
}
|
|
|
|
// Win8:328748 - functions defined in global scope end up in a module
|
|
// Since we import the core modules, EngineSessionState's module is set to the last imported module. So, if a function is defined in global scope, it ends up in that module.
|
|
// Setting the module to null fixes that.
|
|
initializedRunspace.ExecutionContext.EngineSessionState.Module = null;
|
|
|
|
if (ModuleSpecificationsToImport.Count > 0 || unresolvedCmdsToExpose.Count > 0)
|
|
{
|
|
Exception moduleImportException = ProcessModulesToImport(initializedRunspace, ModuleSpecificationsToImport, string.Empty, GetPublicCommands(), unresolvedCmdsToExpose);
|
|
if (moduleImportException != null)
|
|
{
|
|
runspaceInitTracer.WriteLine(
|
|
"Runspace open failed while loading module: First error {1}", moduleImportException);
|
|
return moduleImportException;
|
|
}
|
|
}
|
|
|
|
// If we still have unresolved commands after importing specified modules, then try finding associated module for
|
|
// each unresolved command and import that module.
|
|
if (unresolvedCmdsToExpose.Count > 0)
|
|
{
|
|
string[] foundModuleList = GetModulesForUnResolvedCommands(unresolvedCmdsToExpose, initializedRunspace.ExecutionContext);
|
|
if (foundModuleList.Length > 0)
|
|
{
|
|
ProcessModulesToImport(initializedRunspace, foundModuleList, string.Empty, GetPublicCommands(), unresolvedCmdsToExpose);
|
|
}
|
|
}
|
|
|
|
// Process dynamic variables if any are defined.
|
|
if (DynamicVariablesToDefine.Count > 0)
|
|
{
|
|
ProcessDynamicVariables(initializedRunspace);
|
|
}
|
|
|
|
// Process command modifications if any are defined.
|
|
if (CommandModifications.Count > 0)
|
|
{
|
|
ProcessCommandModifications(initializedRunspace);
|
|
}
|
|
|
|
// Process the 'User:' drive if 'UserDriveEnabled' is set.
|
|
if (UserDriveEnabled)
|
|
{
|
|
Exception userDriveException = ProcessUserDrive(initializedRunspace);
|
|
if (userDriveException != null)
|
|
{
|
|
runspaceInitTracer.WriteLine(
|
|
"Runspace open failed while processing user drive with error {1}", userDriveException);
|
|
|
|
Exception result = PSTraceSource.NewInvalidOperationException(userDriveException, RemotingErrorIdStrings.UserDriveProcessingThrewTerminatingError, userDriveException.Message);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// Process startup scripts
|
|
if (StartupScripts.Count > 0)
|
|
{
|
|
Exception startupScriptException = ProcessStartupScripts(initializedRunspace);
|
|
if (startupScriptException != null)
|
|
{
|
|
runspaceInitTracer.WriteLine(
|
|
"Runspace open failed while running startup script: First error {1}", startupScriptException);
|
|
|
|
Exception result = PSTraceSource.NewInvalidOperationException(startupScriptException, RemotingErrorIdStrings.StartupScriptThrewTerminatingError, startupScriptException.Message);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// Start transcribing
|
|
if (!string.IsNullOrEmpty(TranscriptDirectory))
|
|
{
|
|
using (PowerShell psToInvoke = PowerShell.Create())
|
|
{
|
|
psToInvoke.AddCommand(new Command("Start-Transcript")).AddParameter("OutputDirectory", TranscriptDirectory);
|
|
Exception exceptionToReturn = ProcessPowerShellCommand(psToInvoke, initializedRunspace);
|
|
if (exceptionToReturn != null)
|
|
{
|
|
// ThrowOnRunspaceOpenError handling is done by ProcessPowerShellCommand
|
|
return exceptionToReturn;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private string[] GetModulesForUnResolvedCommands(IEnumerable<string> unresolvedCommands, ExecutionContext context)
|
|
{
|
|
Collection<string> modulesToImport = new Collection<string>();
|
|
HashSet<string> commandsToResolve = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (var unresolvedCommand in unresolvedCommands)
|
|
{
|
|
string moduleName;
|
|
string command = Utils.ParseCommandName(unresolvedCommand, out moduleName);
|
|
if (!string.IsNullOrEmpty(moduleName))
|
|
{
|
|
// Skip fully qualified module names since they are already processed.
|
|
continue;
|
|
}
|
|
|
|
if (WildcardPattern.ContainsWildcardCharacters(command))
|
|
{
|
|
// Skip names with wild cards.
|
|
continue;
|
|
}
|
|
|
|
commandsToResolve.Add(command);
|
|
}
|
|
|
|
if (commandsToResolve.Count > 0)
|
|
{
|
|
Runspace restoreRunspace = Runspace.DefaultRunspace;
|
|
try
|
|
{
|
|
// Create a temporary default runspace for the analysis cache to use.
|
|
using (Runspace tempRunspace = RunspaceFactory.CreateRunspace())
|
|
{
|
|
tempRunspace.Open();
|
|
Runspace.DefaultRunspace = tempRunspace;
|
|
|
|
foreach (var unresolvedCommand in commandsToResolve)
|
|
{
|
|
// Use the analysis cache to find the first module containing the unresolved command.
|
|
foreach (string modulePath in ModuleUtils.GetDefaultAvailableModuleFiles(isForAutoDiscovery: true, context))
|
|
{
|
|
string expandedModulePath = IO.Path.GetFullPath(modulePath);
|
|
var exportedCommands = AnalysisCache.GetExportedCommands(expandedModulePath, false, context);
|
|
|
|
if (exportedCommands != null && exportedCommands.ContainsKey(unresolvedCommand))
|
|
{
|
|
modulesToImport.Add(System.IO.Path.GetFileNameWithoutExtension(expandedModulePath));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Runspace.DefaultRunspace = restoreRunspace;
|
|
}
|
|
}
|
|
|
|
return modulesToImport.ToArray<string>();
|
|
}
|
|
|
|
private void ProcessCommandModifications(Runspace initializedRunspace)
|
|
{
|
|
foreach (var pair in CommandModifications)
|
|
{
|
|
string commandName = pair.Key;
|
|
Hashtable commandModification = pair.Value;
|
|
|
|
CommandInfo existingCommand = initializedRunspace.SessionStateProxy.InvokeCommand.GetCommand(commandName, CommandTypes.Cmdlet | CommandTypes.Function);
|
|
if (existingCommand == null)
|
|
{
|
|
// Could not find the command - just continue, rather than generating an error. This could just be a missing module
|
|
// or something similar.
|
|
continue;
|
|
}
|
|
|
|
// If we are wrapping a function, rename it.
|
|
FunctionInfo commandAsFunction = existingCommand as FunctionInfo;
|
|
if (commandAsFunction != null)
|
|
{
|
|
string newCommandName = commandAsFunction.Name + "_" + Guid.NewGuid().ToString("N");
|
|
commandAsFunction.Rename(newCommandName);
|
|
initializedRunspace.ExecutionContext.EngineSessionState.GlobalScope.FunctionTable.Add(newCommandName, commandAsFunction);
|
|
initializedRunspace.ExecutionContext.EngineSessionState.GlobalScope.FunctionTable.Remove(commandName);
|
|
existingCommand = initializedRunspace.SessionStateProxy.InvokeCommand.GetCommand(newCommandName, CommandTypes.Function);
|
|
}
|
|
|
|
CommandMetadata metadata = new CommandMetadata(existingCommand);
|
|
List<string> unprocessedCommandModifications = new List<string>();
|
|
foreach (string commandModificationParameter in commandModification.Keys)
|
|
{
|
|
unprocessedCommandModifications.Add(commandModificationParameter);
|
|
}
|
|
|
|
// Visit all parameters of the command we're wrapping
|
|
foreach (string existingParameter in metadata.Parameters.Keys.ToArray<string>())
|
|
{
|
|
// If it's not allowed, remove it
|
|
if (!commandModification.ContainsKey(existingParameter))
|
|
{
|
|
metadata.Parameters.Remove(existingParameter);
|
|
}
|
|
else
|
|
{
|
|
// Remember that we've processed this parameter, so that we can add the remainder
|
|
// as virtual command modifications over (what we must assume to be) dynamic parameters.
|
|
unprocessedCommandModifications.Remove(existingParameter);
|
|
|
|
ProcessCommandModification(commandModification, metadata, existingParameter);
|
|
}
|
|
}
|
|
|
|
// Now, process the command modifications that the user requested (but there was no parameter
|
|
// in the cmdlet that matched the requested parameter)
|
|
foreach (string unprocessedCommandModification in unprocessedCommandModifications)
|
|
{
|
|
ProcessCommandModification(commandModification, metadata, unprocessedCommandModification);
|
|
}
|
|
|
|
string proxyBody = ProxyCommand.Create(metadata, string.Empty, false);
|
|
ScriptBlock proxyScriptBlock = ScriptBlock.Create(proxyBody);
|
|
proxyScriptBlock.LanguageMode = PSLanguageMode.FullLanguage;
|
|
|
|
initializedRunspace.ExecutionContext.EngineSessionState.GlobalScope.FunctionTable.Add(
|
|
commandName, new FunctionInfo(commandName, proxyScriptBlock, initializedRunspace.ExecutionContext));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Process a command modification for a specific parameter.
|
|
/// </summary>
|
|
/// <param name="commandModification">The hashtable of command modifications for this command.</param>
|
|
/// <param name="metadata">The metadata for the command being processed.</param>
|
|
/// <param name="parameterName">The parameter being modified.</param>
|
|
private static void ProcessCommandModification(Hashtable commandModification, CommandMetadata metadata, string parameterName)
|
|
{
|
|
// If the metadata doesn't actually contain the parameter, then we need to create one.
|
|
if (!metadata.Parameters.ContainsKey(parameterName))
|
|
{
|
|
metadata.Parameters[parameterName] = new ParameterMetadata(parameterName);
|
|
}
|
|
|
|
// Add validation attributes
|
|
Hashtable parameterValidations = (Hashtable)commandModification[parameterName];
|
|
foreach (object parameterValidation in parameterValidations.Keys)
|
|
{
|
|
string[] parameterValidationValues =
|
|
((HashSet<string>)parameterValidations[parameterValidation]).ToList<string>().ToArray();
|
|
|
|
switch (parameterValidation.ToString())
|
|
{
|
|
case "ValidateSet":
|
|
ValidateSetAttribute validateSet = new ValidateSetAttribute(parameterValidationValues);
|
|
metadata.Parameters[parameterName].Attributes.Add(validateSet);
|
|
break;
|
|
|
|
case "ValidatePattern":
|
|
string pattern = "^(" + string.Join("|", parameterValidationValues) + ")$";
|
|
ValidatePatternAttribute validatePattern = new ValidatePatternAttribute(pattern);
|
|
metadata.Parameters[parameterName].Attributes.Add(validatePattern);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private Exception ProcessDynamicVariables(Runspace initializedRunspace)
|
|
{
|
|
foreach (Hashtable variable in DynamicVariablesToDefine)
|
|
{
|
|
if (variable.ContainsKey("Name"))
|
|
{
|
|
string name = variable["Name"].ToString();
|
|
ScriptBlock sb = variable["Value"] as ScriptBlock;
|
|
|
|
if (!string.IsNullOrEmpty(name) && (sb != null))
|
|
{
|
|
sb.SessionStateInternal = initializedRunspace.ExecutionContext.EngineSessionState;
|
|
|
|
using (PowerShell psToInvoke = PowerShell.Create())
|
|
{
|
|
psToInvoke.AddCommand(new Command("Invoke-Command")).AddParameter("ScriptBlock", sb).AddParameter("NoNewScope");
|
|
psToInvoke.AddCommand(new Command("Set-Variable")).AddParameter("Name", name);
|
|
|
|
Exception exceptionToReturn = ProcessPowerShellCommand(psToInvoke, initializedRunspace);
|
|
if (exceptionToReturn != null)
|
|
{
|
|
// ThrowOnRunspaceOpenError handling is done by ProcessPowerShellCommand
|
|
return exceptionToReturn;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private Exception ProcessUserDrive(Runspace initializedRunspace)
|
|
{
|
|
Exception ex = null;
|
|
try
|
|
{
|
|
List<ProviderInfo> fileSystemProviders = initializedRunspace.ExecutionContext.EngineSessionState.Providers["FileSystem"];
|
|
if (fileSystemProviders.Count == 0)
|
|
{
|
|
throw new PSInvalidOperationException(RemotingErrorIdStrings.UserDriveCannotGetFileSystemProvider);
|
|
}
|
|
|
|
// Create the User drive path directory in current user local appdata location:
|
|
// SystemDrive\Users\[user]\AppData\Local\Microsoft\PowerShell\DriveRoots\[UserName]
|
|
// Or for virtual accounts
|
|
// WinDir\System32\Microsoft\PowerShell\DriveRoots\[UserName]
|
|
string directoryName = MakeUserNamePath();
|
|
#if UNIX
|
|
string userDrivePath = Path.Combine(Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE), "DriveRoots", directoryName);
|
|
#else
|
|
string userDrivePath = Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
|
@"Microsoft\PowerShell\DriveRoots",
|
|
directoryName);
|
|
#endif
|
|
|
|
// Create directory if it doesn't exist.
|
|
if (!System.IO.Directory.Exists(userDrivePath))
|
|
{
|
|
System.IO.Directory.CreateDirectory(userDrivePath);
|
|
}
|
|
|
|
// Create the PSDrive.
|
|
var newDriveInfo = new PSDriveInfo(
|
|
"User",
|
|
fileSystemProviders[0],
|
|
userDrivePath,
|
|
null,
|
|
null);
|
|
var userDriveInfo = initializedRunspace.ExecutionContext.SessionState.Drive.New(newDriveInfo, null);
|
|
|
|
// Set User drive maximum size. Default maximum size is 50MB
|
|
userDriveInfo.MaximumSize = (UserDriveMaximumSize > 0) ? UserDriveMaximumSize : 50000000;
|
|
}
|
|
catch (ArgumentNullException e) { ex = e; }
|
|
catch (ArgumentException e) { ex = e; }
|
|
catch (NotSupportedException e) { ex = e; }
|
|
catch (ProviderNotFoundException e) { ex = e; }
|
|
catch (ProviderInvocationException e) { ex = e; }
|
|
catch (KeyNotFoundException e) { ex = e; }
|
|
catch (IOException e) { ex = e; }
|
|
catch (UnauthorizedAccessException e) { ex = e; }
|
|
|
|
return ex;
|
|
}
|
|
|
|
private string MakeUserNamePath()
|
|
{
|
|
// Use the user name passed to initial session state if available, or
|
|
// otherwise use the current user name.
|
|
var userName = !string.IsNullOrEmpty(this.UserDriveUserName)
|
|
? this.UserDriveUserName
|
|
// domain\user on Windows, just user on Unix
|
|
#if UNIX
|
|
: Platform.Unix.UserName;
|
|
#else
|
|
: System.Security.Principal.WindowsIdentity.GetCurrent().Name;
|
|
#endif
|
|
|
|
// Ensure that user name contains no invalid path characters.
|
|
// MSDN indicates that logon names cannot contain any of these invalid characters,
|
|
// but this check will ensure safety.
|
|
if (userName.IndexOfAny(System.IO.Path.GetInvalidPathChars()) > -1)
|
|
{
|
|
throw new PSInvalidOperationException(RemotingErrorIdStrings.InvalidUserDriveName);
|
|
}
|
|
|
|
return userName.Replace("\\", "_");
|
|
}
|
|
|
|
private Exception ProcessStartupScripts(Runspace initializedRunspace)
|
|
{
|
|
foreach (string startupScript in StartupScripts)
|
|
{
|
|
using (PowerShell psToInvoke = PowerShell.Create())
|
|
{
|
|
psToInvoke.AddCommand(new Command(startupScript, false, false));
|
|
|
|
Exception exceptionToReturn = ProcessPowerShellCommand(psToInvoke, initializedRunspace);
|
|
if (exceptionToReturn != null)
|
|
{
|
|
// ThrowOnRunspaceOpenError handling is done by ProcessPowerShellCommand
|
|
return exceptionToReturn;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private Exception ProcessPowerShellCommand(PowerShell psToInvoke, Runspace initializedRunspace)
|
|
{
|
|
PSLanguageMode originalLanguageMode = initializedRunspace.SessionStateProxy.LanguageMode;
|
|
|
|
try
|
|
{
|
|
initializedRunspace.SessionStateProxy.LanguageMode = PSLanguageMode.FullLanguage;
|
|
|
|
psToInvoke.Runspace = initializedRunspace;
|
|
|
|
foreach (Command command in psToInvoke.Commands.Commands)
|
|
{
|
|
command.CommandOrigin = CommandOrigin.Internal;
|
|
}
|
|
|
|
try
|
|
{
|
|
psToInvoke.Invoke();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (ThrowOnRunspaceOpenError)
|
|
{
|
|
return e;
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
// Restore the langauge mode, but not if it was altered by the startup script itself.
|
|
if (initializedRunspace.SessionStateProxy.LanguageMode == PSLanguageMode.FullLanguage)
|
|
{
|
|
initializedRunspace.SessionStateProxy.LanguageMode = originalLanguageMode;
|
|
}
|
|
}
|
|
|
|
if (ThrowOnRunspaceOpenError)
|
|
{
|
|
// find out if there are any error records reported. If there is one, report the error..
|
|
// this will result in the runspace getting closed/broken.
|
|
ArrayList errorList = (ArrayList)initializedRunspace.GetExecutionContext.DollarErrorVariable;
|
|
if (errorList.Count > 0)
|
|
{
|
|
ErrorRecord lastErrorRecord = errorList[0] as ErrorRecord;
|
|
if (lastErrorRecord != null)
|
|
{
|
|
return new Exception(lastErrorRecord.ToString());
|
|
}
|
|
else
|
|
{
|
|
Exception lastException = errorList[0] as Exception;
|
|
if (lastException != null)
|
|
{
|
|
return lastException;
|
|
}
|
|
else
|
|
{
|
|
return new Exception(errorList[0].ToString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private RunspaceOpenModuleLoadException ProcessModulesToImport(
|
|
Runspace initializedRunspace,
|
|
IEnumerable moduleList,
|
|
string path,
|
|
HashSet<CommandInfo> publicCommands,
|
|
HashSet<string> unresolvedCmdsToExpose)
|
|
{
|
|
RunspaceOpenModuleLoadException exceptionToReturn = null;
|
|
|
|
foreach (object module in moduleList)
|
|
{
|
|
string moduleName = module as string;
|
|
if (moduleName != null)
|
|
{
|
|
exceptionToReturn = ProcessOneModule(initializedRunspace, moduleName, null, path, publicCommands);
|
|
}
|
|
else
|
|
{
|
|
ModuleSpecification moduleSpecification = module as ModuleSpecification;
|
|
if (moduleSpecification != null)
|
|
{
|
|
if ((moduleSpecification.RequiredVersion == null) && (moduleSpecification.Version == null) && (moduleSpecification.MaximumVersion == null) && (moduleSpecification.Guid == null))
|
|
{
|
|
// if only name is specified in the module spec, just try import the module
|
|
// ie., don't take the performance overhead of calling GetModule.
|
|
exceptionToReturn = ProcessOneModule(initializedRunspace, moduleSpecification.Name, null, path, publicCommands);
|
|
}
|
|
else
|
|
{
|
|
Collection<PSModuleInfo> moduleInfos = ModuleCmdletBase.GetModuleIfAvailable(moduleSpecification, initializedRunspace);
|
|
|
|
if (moduleInfos != null && moduleInfos.Count > 0)
|
|
{
|
|
exceptionToReturn = ProcessOneModule(initializedRunspace, moduleSpecification.Name, moduleInfos[0], path, publicCommands);
|
|
}
|
|
else
|
|
{
|
|
var version = "0.0.0.0";
|
|
|
|
if (moduleSpecification.RequiredVersion != null)
|
|
{
|
|
version = moduleSpecification.RequiredVersion.ToString();
|
|
}
|
|
else if (moduleSpecification.Version != null)
|
|
{
|
|
version = moduleSpecification.Version.ToString();
|
|
if (moduleSpecification.MaximumVersion != null)
|
|
{
|
|
version = version + " - " + moduleSpecification.MaximumVersion;
|
|
}
|
|
}
|
|
else if (moduleSpecification.MaximumVersion != null)
|
|
{
|
|
version = moduleSpecification.MaximumVersion;
|
|
}
|
|
|
|
string message = StringUtil.Format(global::Modules.RequiredModuleNotFoundWrongGuidVersion,
|
|
moduleSpecification.Name,
|
|
moduleSpecification.Guid,
|
|
version);
|
|
RunspaceOpenModuleLoadException rome = new RunspaceOpenModuleLoadException(message);
|
|
exceptionToReturn = ValidateAndReturnRunspaceOpenModuleLoadException(null, moduleSpecification.Name, rome);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.Assert(false, "ProcessImportModule can import a module using name or module specification.");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (exceptionToReturn == null)
|
|
{
|
|
// Now go through the list of commands not yet resolved to ensure they are public if requested
|
|
foreach (string unresolvedCommand in unresolvedCmdsToExpose.ToArray<string>())
|
|
{
|
|
string moduleName;
|
|
string commandToMakeVisible = Utils.ParseCommandName(unresolvedCommand, out moduleName);
|
|
bool found = false;
|
|
|
|
foreach (CommandInfo cmd in LookupCommands(commandToMakeVisible, moduleName, initializedRunspace.ExecutionContext))
|
|
{
|
|
if (!found) { found = true; }
|
|
|
|
try
|
|
{
|
|
// Special case for wild card lookups.
|
|
// "Import-Module" or "ipmo" cannot be visible when exposing commands via VisibleCmdlets, etc.
|
|
if ((cmd.Name.Equals("Import-Module", StringComparison.OrdinalIgnoreCase) &&
|
|
(!string.IsNullOrEmpty(cmd.ModuleName) && cmd.ModuleName.Equals("Microsoft.PowerShell.Core", StringComparison.OrdinalIgnoreCase))
|
|
) ||
|
|
cmd.Name.Equals("ipmo", StringComparison.OrdinalIgnoreCase)
|
|
)
|
|
{
|
|
cmd.Visibility = SessionStateEntryVisibility.Private;
|
|
}
|
|
else
|
|
{
|
|
cmd.Visibility = SessionStateEntryVisibility.Public;
|
|
publicCommands.Add(cmd);
|
|
}
|
|
}
|
|
// Some CommandInfo derivations throw on the Visibility setter.
|
|
catch (PSNotImplementedException) { }
|
|
}
|
|
|
|
if (found && !WildcardPattern.ContainsWildcardCharacters(commandToMakeVisible))
|
|
{
|
|
unresolvedCmdsToExpose.Remove(unresolvedCommand);
|
|
}
|
|
}
|
|
}
|
|
|
|
return exceptionToReturn;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to search for commands matching the provided commandPattern.
|
|
/// Supports wild cards and if the commandPattern contains wildcard characters then multiple
|
|
/// results can be returned. Otherwise a single (and first) match will be returned.
|
|
/// If a moduleName is provided then only commands associated with that module will be returned.
|
|
/// Only public commands are searched to start with. If no results are found then a search on
|
|
/// internal commands is performed.
|
|
/// </summary>
|
|
/// <param name="commandPattern"></param>
|
|
/// <param name="moduleName"></param>
|
|
/// <param name="context"></param>
|
|
/// <returns></returns>
|
|
private IEnumerable<CommandInfo> LookupCommands(
|
|
string commandPattern,
|
|
string moduleName,
|
|
ExecutionContext context)
|
|
{
|
|
bool isWildCardPattern = WildcardPattern.ContainsWildcardCharacters(commandPattern);
|
|
var searchOptions = (isWildCardPattern) ?
|
|
SearchResolutionOptions.CommandNameIsPattern | SearchResolutionOptions.ResolveFunctionPatterns | SearchResolutionOptions.SearchAllScopes :
|
|
SearchResolutionOptions.ResolveFunctionPatterns | SearchResolutionOptions.SearchAllScopes;
|
|
|
|
bool found = false;
|
|
bool haveModuleName = !string.IsNullOrEmpty(moduleName);
|
|
|
|
// Start with public search
|
|
CommandOrigin cmdOrigin = CommandOrigin.Runspace;
|
|
while (true)
|
|
{
|
|
foreach (CommandInfo commandInfo in context.SessionState.InvokeCommand.GetCommands(commandPattern, CommandTypes.All, searchOptions, cmdOrigin))
|
|
{
|
|
// If module name is provided then use it to restrict returned results.
|
|
if (haveModuleName && !moduleName.Equals(commandInfo.ModuleName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!found) { found = true; }
|
|
|
|
yield return commandInfo;
|
|
|
|
// Return first match unless a wild card pattern is submitted.
|
|
if (!isWildCardPattern) { break; }
|
|
}
|
|
|
|
if (found || (cmdOrigin == CommandOrigin.Internal))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Next try internal search.
|
|
cmdOrigin = CommandOrigin.Internal;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// If <paramref name="moduleInfoToLoad"/> is null, import module using <paramref name="name"/>. Otherwise,
|
|
/// import module using <paramref name="moduleInfoToLoad"/>
|
|
/// </summary>
|
|
private RunspaceOpenModuleLoadException ProcessOneModule(Runspace initializedRunspace, string name, PSModuleInfo moduleInfoToLoad, string path, HashSet<CommandInfo> publicCommands)
|
|
{
|
|
using (PowerShell pse = PowerShell.Create())
|
|
{
|
|
CommandInfo c = new CmdletInfo("Import-Module", typeof(ImportModuleCommand), null, null, initializedRunspace.ExecutionContext);
|
|
Command cmd = new Command(c);
|
|
if (moduleInfoToLoad != null)
|
|
{
|
|
cmd.Parameters.Add("ModuleInfo", moduleInfoToLoad);
|
|
name = moduleInfoToLoad.Name;
|
|
}
|
|
else
|
|
{
|
|
// If FullyQualifiedPath is supplied then use it.
|
|
// In this scenario, the FullyQualifiedPath would
|
|
// refer to $pshome\Modules location where core
|
|
// modules are deployed.
|
|
if (!string.IsNullOrEmpty(path))
|
|
{
|
|
name = Path.Combine(path, name);
|
|
}
|
|
|
|
cmd.Parameters.Add("Name", name);
|
|
}
|
|
|
|
if (!ThrowOnRunspaceOpenError)
|
|
{
|
|
cmd.MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
|
|
}
|
|
|
|
pse.AddCommand(cmd);
|
|
|
|
if (!ThrowOnRunspaceOpenError)
|
|
{
|
|
c = new CmdletInfo("Out-Default", typeof(OutDefaultCommand), null, null, initializedRunspace.ExecutionContext);
|
|
pse.AddCommand(new Command(c));
|
|
}
|
|
|
|
pse.Runspace = initializedRunspace;
|
|
// Module import should be run in FullLanguage mode since it is running in
|
|
// a trusted context.
|
|
var savedLanguageMode = pse.Runspace.ExecutionContext.LanguageMode;
|
|
pse.Runspace.ExecutionContext.LanguageMode = PSLanguageMode.FullLanguage;
|
|
try
|
|
{
|
|
pse.Invoke();
|
|
}
|
|
finally
|
|
{
|
|
pse.Runspace.ExecutionContext.LanguageMode = savedLanguageMode;
|
|
}
|
|
|
|
// Lock down the command visibility to respect default command visibility
|
|
if (this.DefaultCommandVisibility != SessionStateEntryVisibility.Public)
|
|
{
|
|
foreach (CommandInfo importedCommand in initializedRunspace.ExecutionContext.SessionState.InvokeCommand.GetCommands(
|
|
"*", CommandTypes.Alias | CommandTypes.Function | CommandTypes.Filter | CommandTypes.Cmdlet, true))
|
|
{
|
|
try
|
|
{
|
|
// All commands except for the initial session public commands should be made private.
|
|
if ((importedCommand.Visibility != this.DefaultCommandVisibility) &&
|
|
!publicCommands.Contains(importedCommand))
|
|
{
|
|
importedCommand.Visibility = this.DefaultCommandVisibility;
|
|
}
|
|
}
|
|
// Some CommandInfo derivations throw on the Visibility setter.
|
|
catch (PSNotImplementedException) { }
|
|
}
|
|
}
|
|
|
|
// Now see if there were any errors. Because the only way we have to
|
|
// return an error at this point is a single exception, we'll take the first
|
|
// error and throw it...
|
|
return ValidateAndReturnRunspaceOpenModuleLoadException(pse, name, null);
|
|
}
|
|
}
|
|
|
|
private RunspaceOpenModuleLoadException ValidateAndReturnRunspaceOpenModuleLoadException(PowerShell pse, string moduleName, RunspaceOpenModuleLoadException exception)
|
|
{
|
|
// Only throw the exception if ThrowOnRunspaceOpenError is set.
|
|
if (ThrowOnRunspaceOpenError)
|
|
{
|
|
RunspaceOpenModuleLoadException rome = null;
|
|
if (exception != null)
|
|
{
|
|
rome = exception;
|
|
}
|
|
else if (pse.Streams.Error.Count > 0)
|
|
{
|
|
ErrorRecord er;
|
|
Exception firstError;
|
|
// Merge errors from pse.Streams and errors
|
|
PSDataCollection<ErrorRecord> mergedErrors = new PSDataCollection<ErrorRecord>();
|
|
er = pse.Streams.Error[0];
|
|
firstError = er.Exception;
|
|
foreach (var e in pse.Streams.Error)
|
|
{
|
|
mergedErrors.Add(e);
|
|
}
|
|
|
|
rome = new RunspaceOpenModuleLoadException(moduleName, mergedErrors);
|
|
}
|
|
|
|
if (rome != null)
|
|
{
|
|
return rome;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reinitializes elements of the associated runspace to their initial values.
|
|
/// This allows for runspace reuse with minimal chance for contamination.
|
|
/// </summary>
|
|
/// <param name="context"></param>
|
|
internal void ResetRunspaceState(ExecutionContext context)
|
|
{
|
|
lock (_syncObject)
|
|
{
|
|
SessionStateInternal ss = context.EngineSessionState;
|
|
|
|
// Reset the global variable table
|
|
ss.InitializeSessionStateInternalSpecialVariables(true);
|
|
|
|
// Add the built-in variables
|
|
foreach (SessionStateVariableEntry e in InitialSessionState.BuiltInVariables)
|
|
{
|
|
PSVariable v = new PSVariable(e.Name, e.Value,
|
|
e.Options, e.Attributes, e.Description)
|
|
{ Visibility = e.Visibility };
|
|
ss.GlobalScope.SetVariable(e.Name, v, false, true, ss, fastPath: true);
|
|
}
|
|
|
|
ss.InitializeFixedVariables();
|
|
|
|
// Then re-initialize it with variables to session state...
|
|
foreach (SessionStateVariableEntry e in Variables)
|
|
{
|
|
PSVariable v = new PSVariable(e.Name, e.Value,
|
|
e.Options, e.Attributes, e.Description)
|
|
{ Visibility = e.Visibility };
|
|
ss.GlobalScope.SetVariable(e.Name, v, false, true, ss, fastPath: true);
|
|
}
|
|
|
|
InitialSessionState.CreateQuestionVariable(context);
|
|
|
|
// Reset the path for this runspace.
|
|
SetSessionStateDrive(context, true);
|
|
|
|
// Reset the event, transaction and debug managers.
|
|
context.ResetManagers();
|
|
|
|
// Reset tracing/debugging to the off state.
|
|
context.PSDebugTraceLevel = 0;
|
|
context.PSDebugTraceStep = false;
|
|
}
|
|
}
|
|
|
|
internal static void SetSessionStateDrive(ExecutionContext context, bool setLocation)
|
|
{
|
|
// Set the starting location to the current process working directory
|
|
// Ignore any errors as the file system provider may not be loaded or
|
|
// a drive with the same name as the real file system drive may not have
|
|
// been mounted.
|
|
try
|
|
{
|
|
bool proceedWithSetLocation = true;
|
|
|
|
if (context.EngineSessionState.ProviderCount > 0)
|
|
{
|
|
// NTRAID#Windows Out Of Band Releases-908481-2005/07/01-JeffJon
|
|
// Make sure we have a CurrentDrive set so that we can deal with
|
|
// UNC paths
|
|
|
|
if (context.EngineSessionState.CurrentDrive == null)
|
|
{
|
|
bool fsDriveSet = false;
|
|
try
|
|
{
|
|
// Set the current drive to the first FileSystem drive if it exists.
|
|
ProviderInfo fsProvider = context.EngineSessionState.GetSingleProvider(context.ProviderNames.FileSystem);
|
|
|
|
Collection<PSDriveInfo> fsDrives = fsProvider.Drives;
|
|
if (fsDrives != null && fsDrives.Count > 0)
|
|
{
|
|
context.EngineSessionState.CurrentDrive = fsDrives[0];
|
|
fsDriveSet = true;
|
|
}
|
|
}
|
|
catch (ProviderNotFoundException)
|
|
{
|
|
}
|
|
|
|
if (!fsDriveSet)
|
|
{
|
|
Collection<PSDriveInfo> allDrives = context.EngineSessionState.Drives(null);
|
|
|
|
if (allDrives != null && allDrives.Count > 0)
|
|
{
|
|
context.EngineSessionState.CurrentDrive = allDrives[0];
|
|
}
|
|
else
|
|
{
|
|
ItemNotFoundException itemNotFound =
|
|
new ItemNotFoundException(Directory.GetCurrentDirectory(), "PathNotFound", SessionStateStrings.PathNotFound);
|
|
|
|
context.ReportEngineStartupError(itemNotFound);
|
|
proceedWithSetLocation = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (proceedWithSetLocation && setLocation)
|
|
{
|
|
CmdletProviderContext providerContext = new CmdletProviderContext(context);
|
|
|
|
try
|
|
{
|
|
providerContext.SuppressWildcardExpansion = true;
|
|
context.EngineSessionState.SetLocation(Directory.GetCurrentDirectory(), providerContext);
|
|
}
|
|
catch (ItemNotFoundException)
|
|
{
|
|
// If we can't access the Environment.CurrentDirectory, we may be in an AppContainer. Set the
|
|
// default drive to $pshome
|
|
System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess();
|
|
string defaultPath = System.IO.Path.GetDirectoryName(PsUtils.GetMainModule(currentProcess).FileName);
|
|
context.EngineSessionState.SetLocation(defaultPath, providerContext);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
|
|
internal static void CreateQuestionVariable(ExecutionContext context)
|
|
{
|
|
QuestionMarkVariable qv = new QuestionMarkVariable(context);
|
|
context.EngineSessionState.SetVariableAtScope(qv, "global", true, CommandOrigin.Internal);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove anything that would have been bound by this ISS instance.
|
|
/// At this point, it removes assemblies and cmdlet entries at the top level.
|
|
/// It also removes types and formats.
|
|
/// The other entry types - functions, variables, aliases
|
|
/// are not removed by this function.
|
|
/// </summary>
|
|
/// <param name="context"></param>
|
|
internal void Unbind(ExecutionContext context)
|
|
{
|
|
lock (_syncObject)
|
|
{
|
|
SessionStateInternal ss = context.EngineSessionState;
|
|
|
|
// Remove the assemblies from the assembly cache...
|
|
foreach (SessionStateAssemblyEntry ssae in Assemblies)
|
|
{
|
|
context.RemoveAssembly(ssae.Name);
|
|
}
|
|
|
|
// Remove all of the commands from the top-level session state.
|
|
foreach (SessionStateCommandEntry cmd in Commands)
|
|
{
|
|
SessionStateCmdletEntry ssce = cmd as SessionStateCmdletEntry;
|
|
if (ssce != null)
|
|
{
|
|
List<CmdletInfo> matches;
|
|
if (context.TopLevelSessionState.GetCmdletTable().TryGetValue(ssce.Name, out matches))
|
|
{
|
|
// Remove the name from the list...
|
|
for (int i = matches.Count - 1; i >= 0; i--)
|
|
{
|
|
if (matches[i].ModuleName.Equals(cmd.PSSnapIn.Name))
|
|
{
|
|
string name = matches[i].Name;
|
|
matches.RemoveAt(i);
|
|
context.TopLevelSessionState.RemoveCmdlet(name, i, /*force*/ true);
|
|
}
|
|
}
|
|
// And remove the entry if the list is now empty...
|
|
if (matches.Count == 0)
|
|
{
|
|
context.TopLevelSessionState.RemoveCmdletEntry(ssce.Name, true);
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Remove all of the providers from the top-level provider table.
|
|
if (_providers != null && _providers.Count > 0)
|
|
{
|
|
Dictionary<string, List<ProviderInfo>> providerTable = context.TopLevelSessionState.Providers;
|
|
|
|
foreach (SessionStateProviderEntry sspe in _providers)
|
|
{
|
|
List<ProviderInfo> pl;
|
|
if (providerTable.TryGetValue(sspe.Name, out pl))
|
|
{
|
|
Diagnostics.Assert(pl != null, "There should never be a null list of entries in the provider table");
|
|
// For each provider with the same name...
|
|
for (int i = pl.Count - 1; i >= 0; i--)
|
|
{
|
|
ProviderInfo pi = pl[i];
|
|
|
|
// If it was implemented by this entry, remove it
|
|
if (pi.ImplementingType == sspe.ImplementingType)
|
|
{
|
|
RemoveAllDrivesForProvider(pi, context.TopLevelSessionState);
|
|
pl.RemoveAt(i);
|
|
}
|
|
}
|
|
|
|
// If there are no providers left with this name, remove the key.
|
|
if (pl.Count == 0)
|
|
{
|
|
providerTable.Remove(sspe.Name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
List<string> formatFilesToRemove = new List<string>();
|
|
if (this.Formats != null)
|
|
{
|
|
formatFilesToRemove.AddRange(this.Formats.Select(f => f.FileName));
|
|
}
|
|
|
|
List<string> typeFilesToRemove = new List<string>();
|
|
if (this.Types != null)
|
|
{
|
|
typeFilesToRemove.AddRange(this.Types.Select(t => t.FileName));
|
|
}
|
|
|
|
RemoveTypesAndFormats(context, formatFilesToRemove, typeFilesToRemove);
|
|
}
|
|
}
|
|
|
|
internal static void RemoveTypesAndFormats(ExecutionContext context, IList<string> formatFilesToRemove, IList<string> typeFilesToRemove)
|
|
{
|
|
// The formats and types tables are implemented in such a way that
|
|
// we can't simply remove an entry. We need to edit the list, clear the
|
|
// exiting composed table and then rebuild the entire table.
|
|
if (formatFilesToRemove != null && formatFilesToRemove.Count > 0)
|
|
{
|
|
var newFormats = new InitialSessionStateEntryCollection<SessionStateFormatEntry>();
|
|
HashSet<string> formatFilesToRemoveSet = new HashSet<string>(formatFilesToRemove, StringComparer.OrdinalIgnoreCase);
|
|
foreach (SessionStateFormatEntry entry in context.InitialSessionState.Formats)
|
|
{
|
|
if (!formatFilesToRemoveSet.Contains(entry.FileName))
|
|
{
|
|
newFormats.Add(entry);
|
|
}
|
|
}
|
|
|
|
context.InitialSessionState.Formats.Clear();
|
|
context.InitialSessionState.Formats.Add(newFormats);
|
|
context.InitialSessionState.UpdateFormats(context, false);
|
|
}
|
|
|
|
if (typeFilesToRemove != null && typeFilesToRemove.Count > 0)
|
|
{
|
|
// The types table has the same issue as the format table - we need to rebuild the entire table.
|
|
var newTypes = new InitialSessionStateEntryCollection<SessionStateTypeEntry>();
|
|
List<string> resolvedTypeFilesToRemove = new List<string>();
|
|
foreach (var typeFile in typeFilesToRemove)
|
|
{
|
|
resolvedTypeFilesToRemove.Add(ModuleCmdletBase.ResolveRootedFilePath(typeFile, context) ?? typeFile);
|
|
}
|
|
|
|
foreach (SessionStateTypeEntry entry in context.InitialSessionState.Types)
|
|
{
|
|
if (entry.FileName == null)
|
|
{
|
|
// The entry is associated with a TypeData instance
|
|
newTypes.Add(entry);
|
|
}
|
|
else
|
|
{
|
|
// Resolving the file path because the path to the types file in module manifest is now specified as
|
|
// ..\..\types.ps1xml which expands to C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Microsoft.PowerShell.Core\..\..\types.ps1xml
|
|
string filePath = ModuleCmdletBase.ResolveRootedFilePath(entry.FileName, context) ?? entry.FileName;
|
|
if (!resolvedTypeFilesToRemove.Contains(filePath))
|
|
{
|
|
newTypes.Add(entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there are any types that need to be added to the typetable, update them.
|
|
// Else, clear the typetable
|
|
if (newTypes.Count > 0)
|
|
{
|
|
context.InitialSessionState.Types.Clear();
|
|
context.InitialSessionState.Types.Add(newTypes);
|
|
context.InitialSessionState.UpdateTypes(context, false);
|
|
}
|
|
else
|
|
{
|
|
context.TypeTable.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update the type metadata loaded into this runspace.
|
|
/// </summary>
|
|
/// <param name="context">The execution context for the runspace to update.</param>
|
|
/// <param name="updateOnly">If true, re-initialize the metadata collection...</param>
|
|
internal void UpdateTypes(ExecutionContext context, bool updateOnly)
|
|
{
|
|
if (Types.Count == 1)
|
|
{
|
|
TypeTable typeTable = Types[0].TypeTable;
|
|
if (typeTable != null)
|
|
{
|
|
// reuse the TypeTable instance specified in the sste.
|
|
// this essentially allows for TypeTable sharing across
|
|
// multiple runspaces.
|
|
|
|
context.TypeTable = typeTable;
|
|
|
|
Types.Clear();
|
|
|
|
// A TypeTable contains types info along with type file references used to create the types info,
|
|
// which is redundant information. When resused in a runspace the ISS unpacks the file types again
|
|
// resulting in duplicate types and duplication errors when processed.
|
|
// So use this special Add method to filter all types files found in the TypeTable.
|
|
Types.AddTypeTableTypesInfo(typeTable.typesInfo);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!updateOnly)
|
|
{
|
|
context.TypeTable.Clear();
|
|
}
|
|
|
|
ConcurrentBag<string> errors = new ConcurrentBag<string>();
|
|
// Use at most 3 locks (we don't expect contention on that many cores anyways,
|
|
// and typically we'll be processing just 2 or 3 files anyway, hence capacity=3.
|
|
ConcurrentDictionary<string, string> filesProcessed
|
|
= new ConcurrentDictionary<string, string>(/*concurrencyLevel*/3, /*capacity*/3, StringComparer.OrdinalIgnoreCase);
|
|
Parallel.ForEach(Types, sste =>
|
|
// foreach (var sste in Types)
|
|
{
|
|
if (sste.FileName != null)
|
|
{
|
|
if (filesProcessed.TryAdd(sste.FileName, null))
|
|
{
|
|
string moduleName = string.Empty;
|
|
if (sste.PSSnapIn != null && !string.IsNullOrEmpty(sste.PSSnapIn.Name))
|
|
{
|
|
moduleName = sste.PSSnapIn.Name;
|
|
}
|
|
|
|
bool unused;
|
|
context.TypeTable.Update(moduleName, sste.FileName, errors,
|
|
context.AuthorizationManager, context.EngineHostInterface, out unused);
|
|
}
|
|
}
|
|
else if (sste.TypeTable != null)
|
|
{
|
|
// We get here only if it's NOT updating the existing type table
|
|
// because we cannot do the update with a type table instance
|
|
errors.Add(TypesXmlStrings.TypeTableCannotCoExist);
|
|
}
|
|
else
|
|
{
|
|
context.TypeTable.Update(sste.TypeData, errors, sste.IsRemove);
|
|
}
|
|
});
|
|
// }
|
|
|
|
context.TypeTable.ClearConsolidatedMembers();
|
|
|
|
if (updateOnly)
|
|
{
|
|
// Put the SessionStateTypeEntry into the cache if we are updating the type table
|
|
foreach (var sste in Types)
|
|
{
|
|
context.InitialSessionState.Types.Add(sste);
|
|
}
|
|
}
|
|
|
|
if (errors.Count > 0)
|
|
{
|
|
var allErrors = new StringBuilder();
|
|
allErrors.Append('\n');
|
|
foreach (string error in errors)
|
|
{
|
|
if (!string.IsNullOrEmpty(error))
|
|
{
|
|
if (this.ThrowOnRunspaceOpenError || this.RefreshTypeAndFormatSetting)
|
|
{
|
|
allErrors.Append(error);
|
|
allErrors.Append('\n');
|
|
}
|
|
else
|
|
{
|
|
context.ReportEngineStartupError(ExtendedTypeSystem.TypesXmlError, error);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.ThrowOnRunspaceOpenError)
|
|
{
|
|
string resource = ExtendedTypeSystem.TypesXmlError;
|
|
ThrowTypeOrFormatErrors(resource, allErrors.ToString(), "ErrorsUpdatingTypes");
|
|
}
|
|
|
|
if (this.RefreshTypeAndFormatSetting)
|
|
{
|
|
string resource = ExtendedTypeSystem.TypesXmlError;
|
|
ThrowTypeOrFormatErrors(resource, allErrors.ToString(), "ErrorsUpdatingTypes");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update the formatting information for a runspace.
|
|
/// </summary>
|
|
/// <param name="context">The execution context for the runspace to be updated.</param>
|
|
/// <param name="update">True if we only want to add stuff, false if we want to reinitialize.</param>
|
|
internal void UpdateFormats(ExecutionContext context, bool update)
|
|
{
|
|
if (DisableFormatUpdates || this.Formats.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Collection<PSSnapInTypeAndFormatErrors> entries = new Collection<PSSnapInTypeAndFormatErrors>();
|
|
InitialSessionStateEntryCollection<SessionStateFormatEntry> formatsToLoad;
|
|
|
|
// If we're just updating the current runspace, then we'll add our entries
|
|
// to the current list otherwise, we'll build a new list...
|
|
if (update && context.InitialSessionState != null)
|
|
{
|
|
formatsToLoad = context.InitialSessionState.Formats;
|
|
formatsToLoad.Add(this.Formats);
|
|
}
|
|
else
|
|
{
|
|
formatsToLoad = this.Formats;
|
|
}
|
|
|
|
HashSet<string> filesProcessed = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (SessionStateFormatEntry ssfe in formatsToLoad)
|
|
{
|
|
string name = ssfe.FileName;
|
|
PSSnapInInfo snapin = ssfe.PSSnapIn;
|
|
if (snapin != null && !string.IsNullOrEmpty(snapin.Name))
|
|
{
|
|
name = snapin.Name;
|
|
}
|
|
|
|
if (ssfe.Formattable != null)
|
|
{
|
|
if (formatsToLoad.Count == 1)
|
|
{
|
|
context.FormatDBManager = ssfe.Formattable.FormatDBManager;
|
|
}
|
|
else
|
|
{
|
|
// if a SharedFormatTable is allowed then only one
|
|
// entry can be specified.
|
|
throw PSTraceSource.NewInvalidOperationException(FormatAndOutXmlLoadingStrings.FormatTableCannotCoExist);
|
|
}
|
|
}
|
|
else if (ssfe.FormatData != null)
|
|
{
|
|
entries.Add(new PSSnapInTypeAndFormatErrors(name, ssfe.FormatData));
|
|
}
|
|
else
|
|
{
|
|
if (!filesProcessed.Contains(ssfe.FileName))
|
|
{
|
|
filesProcessed.Add(ssfe.FileName);
|
|
entries.Add(new PSSnapInTypeAndFormatErrors(name, ssfe.FileName));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (entries.Count > 0)
|
|
{
|
|
context.FormatDBManager.UpdateDataBase(entries, context.AuthorizationManager, context.EngineHostInterface, true);
|
|
|
|
var allErrors = new StringBuilder("\n");
|
|
bool hasErrors = false;
|
|
|
|
// Now see if there were any errors in the format files and report them
|
|
// if this is the case...
|
|
foreach (PSSnapInTypeAndFormatErrors entry in entries)
|
|
{
|
|
if (entry.Errors != null && entry.Errors.Count > 0)
|
|
{
|
|
foreach (string error in entry.Errors)
|
|
{
|
|
if (!string.IsNullOrEmpty(error))
|
|
{
|
|
hasErrors = true;
|
|
if (this.ThrowOnRunspaceOpenError || this.RefreshTypeAndFormatSetting)
|
|
{
|
|
allErrors.Append(error);
|
|
allErrors.Append('\n');
|
|
}
|
|
else
|
|
{
|
|
context.ReportEngineStartupError(FormatAndOutXmlLoadingStrings.FormatLoadingErrors, error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((this.ThrowOnRunspaceOpenError || this.RefreshTypeAndFormatSetting) && hasErrors)
|
|
{
|
|
string resource = FormatAndOutXmlLoadingStrings.FormatLoadingErrors;
|
|
ThrowTypeOrFormatErrors(resource, allErrors.ToString(), "ErrorsUpdatingFormats");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void ThrowTypeOrFormatErrors(string resourceString, string errorMsg, string errorId)
|
|
{
|
|
string message = StringUtil.Format(resourceString, errorMsg);
|
|
var ex = new RuntimeException(message);
|
|
ex.SetErrorId(errorId);
|
|
throw ex;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Need to have SnapIn support till we move to modules.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="warning"></param>
|
|
/// <returns></returns>
|
|
public PSSnapInInfo ImportPSSnapIn(string name, out PSSnapInException warning)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
PSTraceSource.NewArgumentNullException("name");
|
|
}
|
|
|
|
// Check whether the mshsnapin is present in the registry.
|
|
// TODO: Note the hard-coded version number here, this was part of the SingleShell
|
|
// implementation and should be refactored.
|
|
PSSnapInInfo newPSSnapIn = PSSnapInReader.Read("2", name);
|
|
|
|
if (!Utils.IsPSVersionSupported(newPSSnapIn.PSVersion.ToString()))
|
|
{
|
|
s_PSSnapInTracer.TraceError("MshSnapin {0} and current monad engine's versions don't match.", name);
|
|
|
|
throw PSTraceSource.NewArgumentException("mshSnapInID",
|
|
ConsoleInfoErrorStrings.AddPSSnapInBadMonadVersion,
|
|
newPSSnapIn.PSVersion.ToString(),
|
|
"2.0");
|
|
}
|
|
|
|
// Now actually load the snapin...
|
|
PSSnapInInfo snapin = ImportPSSnapIn(newPSSnapIn, out warning);
|
|
if (snapin != null)
|
|
{
|
|
ImportedSnapins.Add(snapin.Name, snapin);
|
|
}
|
|
|
|
return snapin;
|
|
}
|
|
|
|
internal PSSnapInInfo ImportCorePSSnapIn()
|
|
{
|
|
// Load Microsoft.PowerShell.Core as a snapin
|
|
PSSnapInInfo coreSnapin = PSSnapInReader.ReadCoreEngineSnapIn();
|
|
this.defaultSnapins.Add(coreSnapin);
|
|
try
|
|
{
|
|
PSSnapInException warning;
|
|
this.ImportPSSnapIn(coreSnapin, out warning);
|
|
}
|
|
catch (PSSnapInException pse)
|
|
{
|
|
throw pse;
|
|
}
|
|
|
|
return coreSnapin;
|
|
}
|
|
|
|
internal PSSnapInInfo ImportPSSnapIn(PSSnapInInfo psSnapInInfo, out PSSnapInException warning)
|
|
{
|
|
// See if the snapin is already loaded. If has been then there will be an entry in the
|
|
// Assemblies list for it already...
|
|
bool reload = true;
|
|
|
|
foreach (SessionStateAssemblyEntry ae in this.Assemblies)
|
|
{
|
|
PSSnapInInfo loadedPSSnapInInfo = ae.PSSnapIn;
|
|
if (loadedPSSnapInInfo != null)
|
|
{
|
|
// See if the assembly-qualified names match and return the existing PSSnapInInfo
|
|
// if they do.
|
|
string loadedSnapInName = ae.PSSnapIn.AssemblyName;
|
|
if (!string.IsNullOrEmpty(loadedSnapInName)
|
|
&& string.Equals(loadedSnapInName, psSnapInInfo.AssemblyName, System.StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
warning = null;
|
|
|
|
// the previous implementation used to return the
|
|
// same loaded snap-in value. This results in the
|
|
// commands/types/formats exposed in the snap-in
|
|
// to be not populated in the InitialSessionState
|
|
// object. This is being fixed
|
|
reload = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Dictionary<string, SessionStateCmdletEntry> cmdlets = null;
|
|
Dictionary<string, List<SessionStateAliasEntry>> aliases = null;
|
|
Dictionary<string, SessionStateProviderEntry> providers = null;
|
|
|
|
if (psSnapInInfo == null)
|
|
{
|
|
ArgumentNullException e = new ArgumentNullException("psSnapInInfo");
|
|
throw e;
|
|
}
|
|
|
|
Assembly assembly = null;
|
|
string helpFile = null;
|
|
|
|
if (reload)
|
|
{
|
|
s_PSSnapInTracer.WriteLine("Loading assembly for psSnapIn {0}", psSnapInInfo.Name);
|
|
|
|
assembly = PSSnapInHelpers.LoadPSSnapInAssembly(psSnapInInfo);
|
|
|
|
if (assembly == null)
|
|
{
|
|
s_PSSnapInTracer.TraceError("Loading assembly for psSnapIn {0} failed", psSnapInInfo.Name);
|
|
warning = null;
|
|
return null; // BUGBUG - should add something to the warnings list here instead of quitting...
|
|
}
|
|
|
|
s_PSSnapInTracer.WriteLine("Loading assembly for psSnapIn {0} succeeded", psSnapInInfo.Name);
|
|
|
|
PSSnapInHelpers.AnalyzePSSnapInAssembly(assembly, psSnapInInfo.Name, psSnapInInfo, moduleInfo: null, out cmdlets, out aliases, out providers, out helpFile);
|
|
}
|
|
|
|
// We skip checking if the file exists when it's in $PSHOME because of magic
|
|
// where we have the former contents of those files built into the engine directly.
|
|
var psHome = Utils.DefaultPowerShellAppBase;
|
|
|
|
foreach (string file in psSnapInInfo.Types)
|
|
{
|
|
string path = Path.Combine(psSnapInInfo.ApplicationBase, file);
|
|
|
|
if (!string.Equals(psHome, psSnapInInfo.ApplicationBase, StringComparison.OrdinalIgnoreCase)
|
|
&& !File.Exists(path))
|
|
{
|
|
// Remove the application base directory if assembly doesn't exist in it.
|
|
path = file;
|
|
}
|
|
|
|
SessionStateTypeEntry typeEntry = new SessionStateTypeEntry(path);
|
|
typeEntry.SetPSSnapIn(psSnapInInfo);
|
|
this.Types.Add(typeEntry);
|
|
}
|
|
|
|
foreach (string file in psSnapInInfo.Formats)
|
|
{
|
|
string path = Path.Combine(psSnapInInfo.ApplicationBase, file);
|
|
|
|
if (!string.Equals(psHome, psSnapInInfo.ApplicationBase, StringComparison.OrdinalIgnoreCase)
|
|
&& !File.Exists(path))
|
|
{
|
|
path = file;
|
|
}
|
|
|
|
SessionStateFormatEntry formatEntry = new SessionStateFormatEntry(path);
|
|
formatEntry.SetPSSnapIn(psSnapInInfo);
|
|
this.Formats.Add(formatEntry);
|
|
}
|
|
|
|
SessionStateAssemblyEntry assemblyEntry =
|
|
new SessionStateAssemblyEntry(psSnapInInfo.AssemblyName, psSnapInInfo.AbsoluteModulePath);
|
|
|
|
assemblyEntry.SetPSSnapIn(psSnapInInfo);
|
|
|
|
this.Assemblies.Add(assemblyEntry);
|
|
|
|
// entry from types.ps1xml references a type (Microsoft.PowerShell.Commands.SecurityDescriptorCommandsBase) in this assembly
|
|
if (psSnapInInfo.Name.Equals(CoreSnapin, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
assemblyEntry = new SessionStateAssemblyEntry("Microsoft.PowerShell.Security", null);
|
|
this.Assemblies.Add(assemblyEntry);
|
|
}
|
|
|
|
if (cmdlets != null)
|
|
{
|
|
foreach (SessionStateCmdletEntry cmdlet in cmdlets.Values)
|
|
{
|
|
SessionStateCmdletEntry newEntry = (SessionStateCmdletEntry)cmdlet.Clone();
|
|
newEntry.Visibility = this.DefaultCommandVisibility;
|
|
|
|
this.Commands.Add(newEntry);
|
|
}
|
|
}
|
|
|
|
if (aliases != null)
|
|
{
|
|
foreach (var cmdletAliasesEntry in aliases.Values)
|
|
{
|
|
foreach (var sessionStateAliasEntry in cmdletAliasesEntry)
|
|
{
|
|
sessionStateAliasEntry.Visibility = this.DefaultCommandVisibility;
|
|
this.Commands.Add(sessionStateAliasEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (providers != null)
|
|
{
|
|
foreach (SessionStateProviderEntry provider in providers.Values)
|
|
{
|
|
this.Providers.Add(provider);
|
|
}
|
|
}
|
|
|
|
warning = null;
|
|
|
|
// Add help file information for built-in functions
|
|
if (psSnapInInfo.Name.Equals(CoreSnapin, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
foreach (var f in BuiltInFunctions)
|
|
{
|
|
Collection<SessionStateCommandEntry> funcList = Commands[f.Name];
|
|
foreach (var func in funcList)
|
|
{
|
|
if (func is SessionStateFunctionEntry)
|
|
{
|
|
((SessionStateFunctionEntry)func).SetHelpFile(helpFile);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return psSnapInInfo;
|
|
}
|
|
|
|
internal List<PSSnapInInfo> GetPSSnapIn(string psSnapinName)
|
|
{
|
|
List<PSSnapInInfo> loadedSnapins = null;
|
|
foreach (var defaultSnapin in defaultSnapins)
|
|
{
|
|
if (defaultSnapin.Name.Equals(psSnapinName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (loadedSnapins == null)
|
|
{
|
|
loadedSnapins = new List<PSSnapInInfo>();
|
|
}
|
|
|
|
loadedSnapins.Add(defaultSnapin);
|
|
}
|
|
}
|
|
|
|
PSSnapInInfo importedSnapin = null;
|
|
if (ImportedSnapins.TryGetValue(psSnapinName, out importedSnapin))
|
|
{
|
|
if (loadedSnapins == null)
|
|
{
|
|
loadedSnapins = new List<PSSnapInInfo>();
|
|
}
|
|
|
|
loadedSnapins.Add(importedSnapin);
|
|
}
|
|
|
|
return loadedSnapins;
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")]
|
|
internal static Assembly LoadAssemblyFromFile(string fileName)
|
|
{
|
|
s_PSSnapInTracer.WriteLine("Loading assembly for psSnapIn {0}", fileName);
|
|
|
|
Assembly assembly = Assembly.LoadFrom(fileName);
|
|
if (assembly == null)
|
|
{
|
|
s_PSSnapInTracer.TraceError("Loading assembly for psSnapIn {0} failed", fileName);
|
|
return null;
|
|
}
|
|
|
|
s_PSSnapInTracer.WriteLine("Loading assembly for psSnapIn {0} succeeded", fileName);
|
|
|
|
return assembly;
|
|
}
|
|
|
|
internal void ImportCmdletsFromAssembly(Assembly assembly, PSModuleInfo module)
|
|
{
|
|
if (assembly == null)
|
|
{
|
|
ArgumentNullException e = new ArgumentNullException("assembly");
|
|
throw e;
|
|
}
|
|
|
|
Dictionary<string, SessionStateCmdletEntry> cmdlets = null;
|
|
Dictionary<string, List<SessionStateAliasEntry>> aliases = null;
|
|
Dictionary<string, SessionStateProviderEntry> providers = null;
|
|
|
|
string assemblyPath = assembly.Location;
|
|
PSSnapInHelpers.AnalyzePSSnapInAssembly(assembly, assemblyPath, psSnapInInfo: null, module, out cmdlets, out aliases, out providers, helpFile: out _);
|
|
|
|
// If this is an in-memory assembly, don't added it to the list of AssemblyEntries
|
|
// since it can't be loaded by path or name
|
|
if (!string.IsNullOrEmpty(assembly.Location))
|
|
{
|
|
SessionStateAssemblyEntry assemblyEntry =
|
|
new SessionStateAssemblyEntry(assembly.FullName, assemblyPath);
|
|
this.Assemblies.Add(assemblyEntry);
|
|
}
|
|
|
|
if (cmdlets != null)
|
|
{
|
|
foreach (SessionStateCmdletEntry cmdlet in cmdlets.Values)
|
|
{
|
|
this.Commands.Add(cmdlet);
|
|
}
|
|
}
|
|
|
|
if (aliases != null)
|
|
{
|
|
foreach (var cmdletAliasesEntry in aliases.Values)
|
|
{
|
|
foreach (var sessionStateAliasEntry in cmdletAliasesEntry)
|
|
{
|
|
this.Commands.Add(sessionStateAliasEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (providers != null)
|
|
{
|
|
foreach (SessionStateProviderEntry provider in providers.Values)
|
|
{
|
|
this.Providers.Add(provider);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now define a bunch of functions that describe the rest of the default session state...
|
|
//
|
|
internal const string FormatEnumerationLimit = "FormatEnumerationLimit";
|
|
internal const int DefaultFormatEnumerationLimit = 4;
|
|
|
|
/// <summary>
|
|
/// This is the default function to use for tab expansion.
|
|
/// </summary>
|
|
private static string s_tabExpansionFunctionText = @"
|
|
<# Options include:
|
|
RelativeFilePaths - [bool]
|
|
Always resolve file paths using Resolve-Path -Relative.
|
|
The default is to use some heuristics to guess if relative or absolute is better.
|
|
|
|
To customize your own custom options, pass a hashtable to CompleteInput, e.g.
|
|
return [System.Management.Automation.CommandCompletion]::CompleteInput($inputScript, $cursorColumn,
|
|
@{ RelativeFilePaths=$false }
|
|
#>
|
|
|
|
[CmdletBinding(DefaultParameterSetName = 'ScriptInputSet')]
|
|
Param(
|
|
[Parameter(ParameterSetName = 'ScriptInputSet', Mandatory = $true, Position = 0)]
|
|
[string] $inputScript,
|
|
|
|
[Parameter(ParameterSetName = 'ScriptInputSet', Mandatory = $true, Position = 1)]
|
|
[int] $cursorColumn,
|
|
|
|
[Parameter(ParameterSetName = 'AstInputSet', Mandatory = $true, Position = 0)]
|
|
[System.Management.Automation.Language.Ast] $ast,
|
|
|
|
[Parameter(ParameterSetName = 'AstInputSet', Mandatory = $true, Position = 1)]
|
|
[System.Management.Automation.Language.Token[]] $tokens,
|
|
|
|
[Parameter(ParameterSetName = 'AstInputSet', Mandatory = $true, Position = 2)]
|
|
[System.Management.Automation.Language.IScriptPosition] $positionOfCursor,
|
|
|
|
[Parameter(ParameterSetName = 'ScriptInputSet', Position = 2)]
|
|
[Parameter(ParameterSetName = 'AstInputSet', Position = 3)]
|
|
[Hashtable] $options = $null
|
|
)
|
|
|
|
End
|
|
{
|
|
if ($psCmdlet.ParameterSetName -eq 'ScriptInputSet')
|
|
{
|
|
return [System.Management.Automation.CommandCompletion]::CompleteInput(
|
|
<#inputScript#> $inputScript,
|
|
<#cursorColumn#> $cursorColumn,
|
|
<#options#> $options)
|
|
}
|
|
else
|
|
{
|
|
return [System.Management.Automation.CommandCompletion]::CompleteInput(
|
|
<#ast#> $ast,
|
|
<#tokens#> $tokens,
|
|
<#positionOfCursor#> $positionOfCursor,
|
|
<#options#> $options)
|
|
}
|
|
}
|
|
";
|
|
|
|
/// <summary>
|
|
/// This is the default function to use for clear-host.
|
|
/// </summary>
|
|
internal static string GetClearHostFunctionText()
|
|
{
|
|
if (Platform.IsWindows)
|
|
{
|
|
// use $RawUI so this works over remoting where there isn't a physical console
|
|
return @"
|
|
$RawUI = $Host.UI.RawUI
|
|
$RawUI.CursorPosition = @{X=0;Y=0}
|
|
$RawUI.SetBufferContents(
|
|
@{Top = -1; Bottom = -1; Right = -1; Left = -1},
|
|
@{Character = ' '; ForegroundColor = $rawui.ForegroundColor; BackgroundColor = $rawui.BackgroundColor})
|
|
# .Link
|
|
# https://go.microsoft.com/fwlink/?LinkID=225747
|
|
# .ExternalHelp System.Management.Automation.dll-help.xml
|
|
";
|
|
}
|
|
else
|
|
{
|
|
// Porting note: non-Windows platforms use `clear`
|
|
return @"
|
|
& (Get-Command -CommandType Application clear | Select-Object -First 1).Definition
|
|
# .Link
|
|
# https://go.microsoft.com/fwlink/?LinkID=225747
|
|
# .ExternalHelp System.Management.Automation.dll-help.xml
|
|
";
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This is the default function to use for man/help. It uses
|
|
/// splatting to pass in the parameters.
|
|
/// </summary>
|
|
internal static string GetHelpPagingFunctionText()
|
|
{
|
|
// We used to generate the text for this function so you could add a parameter
|
|
// to Get-Help and not worry about adding it here. That was a little slow at
|
|
// startup, so it's hard coded, with a test to make sure the parameters match.
|
|
return @"
|
|
<#
|
|
.FORWARDHELPTARGETNAME Get-Help
|
|
.FORWARDHELPCATEGORY Cmdlet
|
|
#>
|
|
[CmdletBinding(DefaultParameterSetName='AllUsersView', HelpUri='https://go.microsoft.com/fwlink/?LinkID=113316')]
|
|
param(
|
|
[Parameter(Position=0, ValueFromPipelineByPropertyName=$true)]
|
|
[string]
|
|
${Name},
|
|
|
|
[string]
|
|
${Path},
|
|
|
|
[ValidateSet('Alias','Cmdlet','Provider','General','FAQ','Glossary','HelpFile','ScriptCommand','Function','Filter','ExternalScript','All','DefaultHelp','Workflow','DscResource','Class','Configuration')]
|
|
[string[]]
|
|
${Category},
|
|
|
|
[Parameter(ParameterSetName='DetailedView', Mandatory=$true)]
|
|
[switch]
|
|
${Detailed},
|
|
|
|
[Parameter(ParameterSetName='AllUsersView')]
|
|
[switch]
|
|
${Full},
|
|
|
|
[Parameter(ParameterSetName='Examples', Mandatory=$true)]
|
|
[switch]
|
|
${Examples},
|
|
|
|
[Parameter(ParameterSetName='Parameters', Mandatory=$true)]
|
|
[string[]]
|
|
${Parameter},
|
|
|
|
[string[]]
|
|
${Component},
|
|
|
|
[string[]]
|
|
${Functionality},
|
|
|
|
[string[]]
|
|
${Role},
|
|
|
|
[Parameter(ParameterSetName='Online', Mandatory=$true)]
|
|
[switch]
|
|
${Online},
|
|
|
|
[Parameter(ParameterSetName='ShowWindow', Mandatory=$true)]
|
|
[switch]
|
|
${ShowWindow})
|
|
|
|
# Display the full help topic by default but only for the AllUsersView parameter set.
|
|
if (($psCmdlet.ParameterSetName -eq 'AllUsersView') -and !$Full) {
|
|
$PSBoundParameters['Full'] = $true
|
|
}
|
|
|
|
# Nano needs to use Unicode, but Windows and Linux need the default
|
|
$OutputEncoding = if ([System.Management.Automation.Platform]::IsNanoServer -or [System.Management.Automation.Platform]::IsIoT) {
|
|
[System.Text.Encoding]::Unicode
|
|
} else {
|
|
[System.Console]::OutputEncoding
|
|
}
|
|
|
|
$help = Get-Help @PSBoundParameters
|
|
|
|
# If a list of help is returned or AliasHelpInfo (because it is small), don't pipe to more
|
|
$psTypeNames = ($help | Select-Object -First 1).PSTypeNames
|
|
if ($psTypeNames -Contains 'HelpInfoShort' -Or $psTypeNames -Contains 'AliasHelpInfo')
|
|
{
|
|
$help
|
|
}
|
|
elseif ($help -ne $null)
|
|
{
|
|
# By default use more on Windows and less on Linux.
|
|
if ($IsWindows) {
|
|
$pagerCommand = 'more.com'
|
|
$pagerArgs = $null
|
|
}
|
|
else {
|
|
$pagerCommand = 'less'
|
|
$pagerArgs = '-Ps""Page %db?B of %D:.\. Press h for help or q to quit\.$""'
|
|
}
|
|
|
|
# Respect PAGER environment variable which allows user to specify a custom pager.
|
|
# Ignore a pure whitespace PAGER value as that would cause the tokenizer to return 0 tokens.
|
|
if (![string]::IsNullOrWhitespace($env:PAGER)) {
|
|
if (Get-Command $env:PAGER -ErrorAction Ignore) {
|
|
# Entire PAGER value corresponds to a single command.
|
|
$pagerCommand = $env:PAGER
|
|
$pagerArgs = $null
|
|
}
|
|
else {
|
|
# PAGER value is not a valid command, check if PAGER command and arguments have been specified.
|
|
# Tokenize the specified $env:PAGER value. Ignore tokenizing errors since any errors may be valid
|
|
# argument syntax for the paging utility.
|
|
$errs = $null
|
|
$tokens = [System.Management.Automation.PSParser]::Tokenize($env:PAGER, [ref]$errs)
|
|
|
|
$customPagerCommand = $tokens[0].Content
|
|
if (!(Get-Command $customPagerCommand -ErrorAction Ignore)) {
|
|
# Custom pager command is invalid, issue a warning.
|
|
Write-Warning ""Custom-paging utility command not found. Ignoring command specified in `$env:PAGER: $env:PAGER""
|
|
}
|
|
else {
|
|
# This approach will preserve all the pagers args.
|
|
$pagerCommand = $customPagerCommand
|
|
$pagerArgs = if ($tokens.Count -gt 1) {$env:PAGER.Substring($tokens[1].Start)} else {$null}
|
|
}
|
|
}
|
|
}
|
|
|
|
# If the pager is an application, format the output width before sending to the app.
|
|
if ((Get-Command $pagerCommand -ErrorAction Ignore).CommandType -eq 'Application') {
|
|
$consoleWidth = [System.Math]::Max([System.Console]::WindowWidth, 20)
|
|
|
|
if ($pagerArgs) {
|
|
# Supply pager arguments to an application without any PowerShell parsing of the arguments.
|
|
# Leave environment variable to help user debug arguments supplied in $env:PAGER.
|
|
$env:__PSPAGER_ARGS = $pagerArgs
|
|
$help | Out-String -Stream -Width ($consoleWidth - 1) | & $pagerCommand --% %__PSPAGER_ARGS%
|
|
}
|
|
else {
|
|
$help | Out-String -Stream -Width ($consoleWidth - 1) | & $pagerCommand
|
|
}
|
|
}
|
|
else {
|
|
# The pager command is a PowerShell function, script or alias, so pipe directly into it.
|
|
$help | & $pagerCommand $pagerArgs
|
|
}
|
|
}
|
|
";
|
|
}
|
|
|
|
internal static string GetMkdirFunctionText()
|
|
{
|
|
return @"
|
|
<#
|
|
.FORWARDHELPTARGETNAME New-Item
|
|
.FORWARDHELPCATEGORY Cmdlet
|
|
#>
|
|
|
|
[CmdletBinding(DefaultParameterSetName='pathSet',
|
|
SupportsShouldProcess=$true,
|
|
SupportsTransactions=$true,
|
|
ConfirmImpact='Medium')]
|
|
[OutputType([System.IO.DirectoryInfo])]
|
|
param(
|
|
[Parameter(ParameterSetName='nameSet', Position=0, ValueFromPipelineByPropertyName=$true)]
|
|
[Parameter(ParameterSetName='pathSet', Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)]
|
|
[System.String[]]
|
|
${Path},
|
|
|
|
[Parameter(ParameterSetName='nameSet', Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
|
|
[AllowNull()]
|
|
[AllowEmptyString()]
|
|
[System.String]
|
|
${Name},
|
|
|
|
[Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
|
|
[System.Object]
|
|
${Value},
|
|
|
|
[Switch]
|
|
${Force},
|
|
|
|
[Parameter(ValueFromPipelineByPropertyName=$true)]
|
|
[System.Management.Automation.PSCredential]
|
|
${Credential}
|
|
)
|
|
|
|
begin {
|
|
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('New-Item', [System.Management.Automation.CommandTypes]::Cmdlet)
|
|
$scriptCmd = {& $wrappedCmd -Type Directory @PSBoundParameters }
|
|
|
|
$steppablePipeline = $scriptCmd.GetSteppablePipeline()
|
|
$steppablePipeline.Begin($PSCmdlet)
|
|
}
|
|
|
|
process {
|
|
$steppablePipeline.Process($_)
|
|
}
|
|
|
|
end {
|
|
$steppablePipeline.End()
|
|
}
|
|
|
|
";
|
|
}
|
|
|
|
internal static string GetOSTFunctionText()
|
|
{
|
|
return @"
|
|
[CmdletBinding()]
|
|
param(
|
|
[ValidateRange(2, 2147483647)]
|
|
[int]
|
|
${Width},
|
|
|
|
[Parameter(ValueFromPipeline=$true)]
|
|
[psobject]
|
|
${InputObject})
|
|
|
|
begin {
|
|
$PSBoundParameters['Stream'] = $true
|
|
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Out-String',[System.Management.Automation.CommandTypes]::Cmdlet)
|
|
$scriptCmd = {& $wrappedCmd @PSBoundParameters }
|
|
|
|
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
|
|
$steppablePipeline.Begin($PSCmdlet)
|
|
}
|
|
|
|
process {
|
|
$steppablePipeline.Process($_)
|
|
}
|
|
|
|
end {
|
|
$steppablePipeline.End()
|
|
}
|
|
<#
|
|
.ForwardHelpTargetName Out-String
|
|
.ForwardHelpCategory Cmdlet
|
|
#>
|
|
";
|
|
}
|
|
|
|
internal const ActionPreference defaultDebugPreference = ActionPreference.SilentlyContinue;
|
|
internal const ActionPreference defaultErrorActionPreference = ActionPreference.Continue;
|
|
internal const ActionPreference defaultProgressPreference = ActionPreference.Continue;
|
|
internal const ActionPreference defaultVerbosePreference = ActionPreference.SilentlyContinue;
|
|
internal const ActionPreference defaultWarningPreference = ActionPreference.Continue;
|
|
internal const ActionPreference defaultInformationPreference = ActionPreference.SilentlyContinue;
|
|
internal const bool defaultWhatIfPreference = false;
|
|
internal const ConfirmImpact defaultConfirmPreference = ConfirmImpact.High;
|
|
|
|
internal static SessionStateVariableEntry[] BuiltInVariables = new SessionStateVariableEntry[]
|
|
{
|
|
// Engine variables that should be precreated before running profile
|
|
// Bug fix for Win7:2202228 Engine halts if initial command fulls up variable table
|
|
// Anytime a new variable that the engine depends on to run is added, this table
|
|
// must be updated...
|
|
new SessionStateVariableEntry(SpecialVariables.LastToken, null, string.Empty),
|
|
new SessionStateVariableEntry(SpecialVariables.FirstToken, null, string.Empty),
|
|
new SessionStateVariableEntry(SpecialVariables.StackTrace, null, string.Empty),
|
|
|
|
// Variable which controls the encoding for piping data to a NativeCommand
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.OutputEncoding,
|
|
Utils.utf8NoBom,
|
|
RunspaceInit.OutputEncodingDescription,
|
|
ScopedItemOptions.None,
|
|
new ArgumentTypeConverterAttribute(typeof(System.Text.Encoding))
|
|
),
|
|
|
|
// Preferences
|
|
|
|
// NTRAID#Windows Out Of Band Releases-931461-2006/03/13
|
|
// ArgumentTypeConverterAttribute is applied to these variables,
|
|
// but this only reaches the global variable. If these are
|
|
// redefined in script scope etc, the type conversion
|
|
// is not applicable.
|
|
|
|
// Variables typed to ActionPreference
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.ConfirmPreference,
|
|
defaultConfirmPreference,
|
|
RunspaceInit.ConfirmPreferenceDescription,
|
|
ScopedItemOptions.None,
|
|
new ArgumentTypeConverterAttribute(typeof(ConfirmImpact))
|
|
),
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.DebugPreference,
|
|
defaultDebugPreference,
|
|
RunspaceInit.DebugPreferenceDescription,
|
|
ScopedItemOptions.None,
|
|
new ArgumentTypeConverterAttribute(typeof(ActionPreference))
|
|
),
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.ErrorActionPreference,
|
|
defaultErrorActionPreference,
|
|
RunspaceInit.ErrorActionPreferenceDescription,
|
|
ScopedItemOptions.None,
|
|
new ArgumentTypeConverterAttribute(typeof(ActionPreference))
|
|
),
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.ProgressPreference,
|
|
defaultProgressPreference,
|
|
RunspaceInit.ProgressPreferenceDescription,
|
|
ScopedItemOptions.None,
|
|
new ArgumentTypeConverterAttribute(typeof(ActionPreference))
|
|
),
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.VerbosePreference,
|
|
defaultVerbosePreference,
|
|
RunspaceInit.VerbosePreferenceDescription,
|
|
ScopedItemOptions.None,
|
|
new ArgumentTypeConverterAttribute(typeof(ActionPreference))
|
|
),
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.WarningPreference,
|
|
defaultWarningPreference,
|
|
RunspaceInit.WarningPreferenceDescription,
|
|
ScopedItemOptions.None,
|
|
new ArgumentTypeConverterAttribute(typeof(ActionPreference))
|
|
),
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.InformationPreference,
|
|
defaultInformationPreference,
|
|
RunspaceInit.InformationPreferenceDescription,
|
|
ScopedItemOptions.None,
|
|
new ArgumentTypeConverterAttribute(typeof(ActionPreference))
|
|
),
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.ErrorView,
|
|
"NormalView",
|
|
RunspaceInit.ErrorViewDescription
|
|
),
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.NestedPromptLevel,
|
|
0,
|
|
RunspaceInit.NestedPromptLevelDescription
|
|
),
|
|
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.WhatIfPreference,
|
|
defaultWhatIfPreference,
|
|
RunspaceInit.WhatIfPreferenceDescription
|
|
),
|
|
new SessionStateVariableEntry(
|
|
FormatEnumerationLimit,
|
|
DefaultFormatEnumerationLimit,
|
|
RunspaceInit.FormatEnumerationLimitDescription
|
|
),
|
|
|
|
// variable for PSEmailServer
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.PSEmailServer,
|
|
string.Empty,
|
|
RunspaceInit.PSEmailServerDescription
|
|
),
|
|
|
|
// Start: Variables which control remoting behavior
|
|
new SessionStateVariableEntry(
|
|
Microsoft.PowerShell.Commands.PSRemotingBaseCmdlet.DEFAULT_SESSION_OPTION,
|
|
new System.Management.Automation.Remoting.PSSessionOption(),
|
|
RemotingErrorIdStrings.PSDefaultSessionOptionDescription,
|
|
ScopedItemOptions.None),
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.PSSessionConfigurationName,
|
|
"http://schemas.microsoft.com/powershell/Microsoft.PowerShell",
|
|
RemotingErrorIdStrings.PSSessionConfigurationName,
|
|
ScopedItemOptions.None),
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.PSSessionApplicationName,
|
|
"wsman",
|
|
RemotingErrorIdStrings.PSSessionAppName,
|
|
ScopedItemOptions.None),
|
|
// End: Variables which control remoting behavior
|
|
|
|
#region Platform
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.IsLinux,
|
|
Platform.IsLinux,
|
|
string.Empty,
|
|
ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope),
|
|
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.IsMacOS,
|
|
Platform.IsMacOS,
|
|
string.Empty,
|
|
ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope),
|
|
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.IsWindows,
|
|
Platform.IsWindows,
|
|
string.Empty,
|
|
ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope),
|
|
|
|
new SessionStateVariableEntry(
|
|
SpecialVariables.IsCoreCLR,
|
|
Platform.IsCoreCLR,
|
|
string.Empty,
|
|
ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope),
|
|
#endregion
|
|
};
|
|
|
|
/// <summary>
|
|
/// Returns a new array of alias entries everytime it's called. This
|
|
/// can't be static because the elements may be mutated in different session
|
|
/// state objects so each session state must have a copy of the entry.
|
|
/// </summary>
|
|
internal static SessionStateAliasEntry[] BuiltInAliases
|
|
{
|
|
get
|
|
{
|
|
// Too many AllScope entries hurts performance because an entry is
|
|
// created in each new scope, so we limit the use of AllScope to the
|
|
// most commonly used commands - primarily so command lookup is faster,
|
|
// though if we speed up command lookup significantly, then removing
|
|
// AllScope for all of these aliases makes sense.
|
|
|
|
const ScopedItemOptions AllScope = ScopedItemOptions.AllScope;
|
|
const ScopedItemOptions ReadOnly_AllScope = ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope;
|
|
const ScopedItemOptions ReadOnly = ScopedItemOptions.ReadOnly;
|
|
|
|
return new SessionStateAliasEntry[] {
|
|
new SessionStateAliasEntry("foreach", "ForEach-Object", string.Empty, ReadOnly_AllScope),
|
|
new SessionStateAliasEntry("%", "ForEach-Object", string.Empty, ReadOnly_AllScope),
|
|
new SessionStateAliasEntry("where", "Where-Object", string.Empty, ReadOnly_AllScope),
|
|
new SessionStateAliasEntry("?", "Where-Object", string.Empty, ReadOnly_AllScope),
|
|
new SessionStateAliasEntry("clc", "Clear-Content", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("cli", "Clear-Item", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("clp", "Clear-ItemProperty", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("clv", "Clear-Variable", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("cpi", "Copy-Item", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("cvpa", "Convert-Path", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("dbp", "Disable-PSBreakpoint", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("ebp", "Enable-PSBreakpoint", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("epal", "Export-Alias", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("epcsv", "Export-Csv", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("fl", "Format-List", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("ft", "Format-Table", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("fw", "Format-Wide", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gal", "Get-Alias", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gbp", "Get-PSBreakpoint", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gc", "Get-Content", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gci", "Get-ChildItem", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gcm", "Get-Command", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gdr", "Get-PSDrive", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gcs", "Get-PSCallStack", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("ghy", "Get-History", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gi", "Get-Item", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gl", "Get-Location", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gm", "Get-Member", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gmo", "Get-Module", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gp", "Get-ItemProperty", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gpv", "Get-ItemPropertyValue", string.Empty,ReadOnly),
|
|
new SessionStateAliasEntry("gps", "Get-Process", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("group", "Group-Object", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gu", "Get-Unique", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gv", "Get-Variable", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("iex", "Invoke-Expression", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("ihy", "Invoke-History", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("ii", "Invoke-Item", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("ipmo", "Import-Module", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("ipal", "Import-Alias", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("ipcsv", "Import-Csv", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("measure", "Measure-Object", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("mi", "Move-Item", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("mp", "Move-ItemProperty", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("nal", "New-Alias", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("nbp", "New-PSBreakpoint", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("ndr", "New-PSDrive", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("ni", "New-Item", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("nv", "New-Variable", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("nmo", "New-Module", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("oh", "Out-Host", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("rbp", "Remove-PSBreakpoint", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("rdr", "Remove-PSDrive", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("ri", "Remove-Item", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("rni", "Rename-Item", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("rnp", "Rename-ItemProperty", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("rp", "Remove-ItemProperty", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("rmo", "Remove-Module", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("rv", "Remove-Variable", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("rvpa", "Resolve-Path", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("sal", "Set-Alias", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("sbp", "Set-PSBreakpoint", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("select", "Select-Object", string.Empty, ReadOnly_AllScope),
|
|
new SessionStateAliasEntry("si", "Set-Item", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("sl", "Set-Location", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("sp", "Set-ItemProperty", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("saps", "Start-Process", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("spps", "Stop-Process", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("sv", "Set-Variable", string.Empty, ReadOnly),
|
|
// Web cmdlets aliases
|
|
new SessionStateAliasEntry("irm", "Invoke-RestMethod", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("iwr", "Invoke-WebRequest", string.Empty, ReadOnly),
|
|
// Porting note: #if !UNIX is used to disable aliases for cmdlets which conflict with Linux / macOS
|
|
#if !UNIX
|
|
// ac is a native command on macOS
|
|
new SessionStateAliasEntry("ac", "Add-Content", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("clear", "Clear-Host"),
|
|
new SessionStateAliasEntry("compare", "Compare-Object", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("cpp", "Copy-ItemProperty", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("diff", "Compare-Object", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("gsv", "Get-Service", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("sleep", "Start-Sleep", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("sort", "Sort-Object", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("start", "Start-Process", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("sasv", "Start-Service", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("spsv", "Stop-Service", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("tee", "Tee-Object", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("write", "Write-Output", string.Empty, ReadOnly),
|
|
// These were transferred from the "transferred from the profile" section
|
|
new SessionStateAliasEntry("cat", "Get-Content"),
|
|
new SessionStateAliasEntry("cp", "Copy-Item", string.Empty, AllScope),
|
|
new SessionStateAliasEntry("ls", "Get-ChildItem"),
|
|
new SessionStateAliasEntry("man", "help"),
|
|
new SessionStateAliasEntry("mount", "New-PSDrive"),
|
|
new SessionStateAliasEntry("mv", "Move-Item"),
|
|
new SessionStateAliasEntry("ps", "Get-Process"),
|
|
new SessionStateAliasEntry("rm", "Remove-Item"),
|
|
new SessionStateAliasEntry("rmdir", "Remove-Item"),
|
|
new SessionStateAliasEntry("cnsn", "Connect-PSSession", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("dnsn", "Disconnect-PSSession", string.Empty, ReadOnly),
|
|
#endif
|
|
// Bash built-ins we purposefully keep even if they override native commands
|
|
new SessionStateAliasEntry("cd", "Set-Location", string.Empty, AllScope),
|
|
new SessionStateAliasEntry("dir", "Get-ChildItem", string.Empty, AllScope),
|
|
new SessionStateAliasEntry("echo", "Write-Output", string.Empty, AllScope),
|
|
new SessionStateAliasEntry("fc", "Format-Custom", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("kill", "Stop-Process"),
|
|
new SessionStateAliasEntry("pwd", "Get-Location"),
|
|
new SessionStateAliasEntry("type", "Get-Content"),
|
|
// #if !CORECLR is used to disable aliases for cmdlets which are not available on OneCore or not appropriate for PSCore6 due to conflicts
|
|
#if !CORECLR
|
|
new SessionStateAliasEntry("gwmi", "Get-WmiObject", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("iwmi", "Invoke-WMIMethod", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("ogv", "Out-GridView", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("ise", "powershell_ise.exe", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("rwmi", "Remove-WMIObject", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("sc", "Set-Content", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("swmi", "Set-WMIInstance", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("shcm", "Show-Command", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("trcm", "Trace-Command", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("lp", "Out-Printer"),
|
|
#endif
|
|
// Aliases transferred from the profile
|
|
new SessionStateAliasEntry("h", "Get-History"),
|
|
new SessionStateAliasEntry("history", "Get-History"),
|
|
new SessionStateAliasEntry("md", "mkdir", string.Empty, AllScope),
|
|
new SessionStateAliasEntry("popd", "Pop-Location", string.Empty, AllScope),
|
|
new SessionStateAliasEntry("pushd", "Push-Location", string.Empty, AllScope),
|
|
new SessionStateAliasEntry("r", "Invoke-History"),
|
|
new SessionStateAliasEntry("cls", "Clear-Host"),
|
|
new SessionStateAliasEntry("chdir", "Set-Location"),
|
|
new SessionStateAliasEntry("copy", "Copy-Item", string.Empty, AllScope),
|
|
new SessionStateAliasEntry("del", "Remove-Item", string.Empty, AllScope),
|
|
new SessionStateAliasEntry("erase", "Remove-Item"),
|
|
new SessionStateAliasEntry("move", "Move-Item", string.Empty, AllScope),
|
|
new SessionStateAliasEntry("rd", "Remove-Item"),
|
|
new SessionStateAliasEntry("ren", "Rename-Item"),
|
|
new SessionStateAliasEntry("set", "Set-Variable"),
|
|
new SessionStateAliasEntry("icm", "Invoke-Command"),
|
|
new SessionStateAliasEntry("clhy", "Clear-History", string.Empty, ReadOnly),
|
|
// Job Specific aliases
|
|
new SessionStateAliasEntry("gjb", "Get-Job"),
|
|
new SessionStateAliasEntry("rcjb", "Receive-Job"),
|
|
new SessionStateAliasEntry("rjb", "Remove-Job"),
|
|
new SessionStateAliasEntry("sajb", "Start-Job"),
|
|
new SessionStateAliasEntry("spjb", "Stop-Job"),
|
|
new SessionStateAliasEntry("wjb", "Wait-Job"),
|
|
#if !CORECLR
|
|
new SessionStateAliasEntry("sujb", "Suspend-Job"),
|
|
new SessionStateAliasEntry("rujb", "Resume-Job"),
|
|
// Remoting Cmdlets Specific aliases
|
|
new SessionStateAliasEntry("npssc", "New-PSSessionConfigurationFile", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("ipsn", "Import-PSSession"),
|
|
new SessionStateAliasEntry("epsn", "Export-PSSession"),
|
|
#endif
|
|
new SessionStateAliasEntry("nsn", "New-PSSession"),
|
|
new SessionStateAliasEntry("gsn", "Get-PSSession"),
|
|
new SessionStateAliasEntry("rsn", "Remove-PSSession"),
|
|
new SessionStateAliasEntry("etsn", "Enter-PSSession"),
|
|
new SessionStateAliasEntry("rcsn", "Receive-PSSession", string.Empty, ReadOnly),
|
|
new SessionStateAliasEntry("exsn", "Exit-PSSession"),
|
|
// Win8: 121662/169179 Add "sls" alias for Select-String cmdlet
|
|
// - do not use AllScope - this causes errors in profiles that set this somewhat commonly used alias.
|
|
new SessionStateAliasEntry("sls", "Select-String"),
|
|
};
|
|
}
|
|
}
|
|
|
|
internal const string DefaultPromptFunctionText = @"
|
|
""PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "";
|
|
# .Link
|
|
# https://go.microsoft.com/fwlink/?LinkID=225750
|
|
# .ExternalHelp System.Management.Automation.dll-help.xml
|
|
";
|
|
|
|
internal const string DefaultSetDriveFunctionText = "Set-Location $MyInvocation.MyCommand.Name";
|
|
internal static ScriptBlock SetDriveScriptBlock = ScriptBlock.CreateDelayParsedScriptBlock(DefaultSetDriveFunctionText, isProductCode: true);
|
|
|
|
private static PSLanguageMode systemLanguageMode = (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce) ? PSLanguageMode.ConstrainedLanguage : PSLanguageMode.FullLanguage;
|
|
internal static SessionStateFunctionEntry[] BuiltInFunctions = new SessionStateFunctionEntry[]
|
|
{
|
|
// Functions that don't require full language mode
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("cd..", "Set-Location ..", isProductCode: true, languageMode: systemLanguageMode),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("cd\\", "Set-Location \\", isProductCode: true, languageMode: systemLanguageMode),
|
|
// Win8: 320909. Retaining the original definition to ensure backward compatability.
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("Pause",
|
|
string.Concat("$null = Read-Host '", CodeGeneration.EscapeSingleQuotedStringContent(RunspaceInit.PauseDefinitionString),"'"), isProductCode: true, languageMode: systemLanguageMode),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("help", GetHelpPagingFunctionText(), isProductCode: true, languageMode: systemLanguageMode),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("prompt", DefaultPromptFunctionText, isProductCode: true, languageMode: systemLanguageMode),
|
|
|
|
// Functions that require full language mode and are trusted
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("Clear-Host", GetClearHostFunctionText(), isProductCode: true, languageMode: PSLanguageMode.FullLanguage),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("TabExpansion2", s_tabExpansionFunctionText, isProductCode: true, languageMode: PSLanguageMode.FullLanguage),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("oss", GetOSTFunctionText(), isProductCode: true, languageMode: PSLanguageMode.FullLanguage),
|
|
#if !UNIX
|
|
// Porting note: we remove mkdir on Linux because of a conflict
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("mkdir", GetMkdirFunctionText(), isProductCode: true, languageMode: PSLanguageMode.FullLanguage),
|
|
#endif
|
|
#if !UNIX
|
|
// Porting note: we remove the drive functions from Linux because they make no sense in that environment
|
|
// Default drives
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("A:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("B:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("C:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("D:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("E:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("F:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("G:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("H:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("I:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("J:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("K:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("L:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("M:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("N:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("O:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("P:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("Q:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("R:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("S:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("T:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("U:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("V:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("W:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("X:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("Y:", DefaultSetDriveFunctionText, SetDriveScriptBlock),
|
|
SessionStateFunctionEntry.GetDelayParsedFunctionEntry("Z:", DefaultSetDriveFunctionText, SetDriveScriptBlock)
|
|
#endif
|
|
};
|
|
|
|
internal static void RemoveAllDrivesForProvider(ProviderInfo pi, SessionStateInternal ssi)
|
|
{
|
|
foreach (PSDriveInfo di in ssi.GetDrivesForProvider(pi.FullName))
|
|
{
|
|
try
|
|
{
|
|
ssi.RemoveDrive(di, true, null);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
private static PSTraceSource s_PSSnapInTracer = PSTraceSource.GetTracer("PSSnapInLoadUnload", "Loading and unloading mshsnapins", false);
|
|
|
|
internal static string CoreSnapin = "Microsoft.PowerShell.Core";
|
|
internal static string CoreModule = "Microsoft.PowerShell.Core";
|
|
internal Collection<PSSnapInInfo> defaultSnapins = new Collection<PSSnapInInfo>();
|
|
|
|
// The list of engine modules to create warnings when you try to remove them
|
|
internal static HashSet<string> EngineModules = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
"Microsoft.PowerShell.Utility",
|
|
"Microsoft.PowerShell.Management",
|
|
"Microsoft.PowerShell.Diagnostics",
|
|
"Microsoft.PowerShell.Host",
|
|
"Microsoft.PowerShell.Security",
|
|
"Microsoft.WSMan.Management"
|
|
};
|
|
|
|
internal static HashSet<string> NestedEngineModules = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
"Microsoft.PowerShell.Commands.Utility",
|
|
"Microsoft.PowerShell.Commands.Management",
|
|
"Microsoft.PowerShell.Commands.Diagnostics",
|
|
"Microsoft.PowerShell.ConsoleHost"
|
|
};
|
|
|
|
internal static Dictionary<string, string> EngineModuleNestedModuleMapping = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
{ "Microsoft.PowerShell.Utility", "Microsoft.PowerShell.Commands.Utility"},
|
|
{ "Microsoft.PowerShell.Management", "Microsoft.PowerShell.Commands.Management"},
|
|
{ "Microsoft.PowerShell.Diagnostics", "Microsoft.PowerShell.Commands.Diagnostics"},
|
|
{ "Microsoft.PowerShell.Host", "Microsoft.PowerShell.ConsoleHost"},
|
|
};
|
|
internal static Dictionary<string, string> NestedModuleEngineModuleMapping = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
{ "Microsoft.PowerShell.Commands.Utility", "Microsoft.PowerShell.Utility"},
|
|
{ "Microsoft.PowerShell.Commands.Management", "Microsoft.PowerShell.Management"},
|
|
{ "Microsoft.PowerShell.Commands.Diagnostics", "Microsoft.PowerShell.Diagnostics"},
|
|
{ "Microsoft.PowerShell.ConsoleHost", "Microsoft.PowerShell.Host"},
|
|
{ "Microsoft.PowerShell.Security", "Microsoft.PowerShell.Security"},
|
|
{ "Microsoft.WSMan.Management", "Microsoft.WSMan.Management"},
|
|
};
|
|
|
|
// The list of engine modules that we will not allow users to remove
|
|
internal static HashSet<string> ConstantEngineModules = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
CoreModule,
|
|
};
|
|
|
|
// The list of nested engine modules that we will not allow users to remove
|
|
internal static HashSet<string> ConstantEngineNestedModules = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
"System.Management.Automation",
|
|
};
|
|
|
|
internal static string GetNestedModuleDllName(string moduleName)
|
|
{
|
|
string result = null;
|
|
|
|
if (!EngineModuleNestedModuleMapping.TryGetValue(moduleName, out result))
|
|
{
|
|
result = string.Empty;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set of helper methods fro loading assemblies containing cmdlets...
|
|
/// </summary>
|
|
internal static class PSSnapInHelpers
|
|
{
|
|
[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")]
|
|
internal static Assembly LoadPSSnapInAssembly(PSSnapInInfo psSnapInInfo)
|
|
{
|
|
Assembly assembly = null;
|
|
s_PSSnapInTracer.WriteLine("Loading assembly from GAC. Assembly Name: {0}", psSnapInInfo.AssemblyName);
|
|
|
|
try
|
|
{
|
|
assembly = Assembly.Load(new AssemblyName(psSnapInInfo.AssemblyName));
|
|
}
|
|
catch (BadImageFormatException e)
|
|
{
|
|
s_PSSnapInTracer.TraceWarning("Not able to load assembly {0}: {1}", psSnapInInfo.AssemblyName, e.Message);
|
|
}
|
|
catch (FileNotFoundException e)
|
|
{
|
|
s_PSSnapInTracer.TraceWarning("Not able to load assembly {0}: {1}", psSnapInInfo.AssemblyName, e.Message);
|
|
}
|
|
catch (FileLoadException e)
|
|
{
|
|
s_PSSnapInTracer.TraceWarning("Not able to load assembly {0}: {1}", psSnapInInfo.AssemblyName, e.Message);
|
|
}
|
|
|
|
if (assembly != null)
|
|
{
|
|
return assembly;
|
|
}
|
|
|
|
s_PSSnapInTracer.WriteLine("Loading assembly from path: {0}", psSnapInInfo.AssemblyName);
|
|
|
|
try
|
|
{
|
|
AssemblyName assemblyName = AssemblyName.GetAssemblyName(psSnapInInfo.AbsoluteModulePath);
|
|
|
|
if (!string.Equals(assemblyName.FullName, psSnapInInfo.AssemblyName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
string message = StringUtil.Format(ConsoleInfoErrorStrings.PSSnapInAssemblyNameMismatch, psSnapInInfo.AbsoluteModulePath, psSnapInInfo.AssemblyName);
|
|
s_PSSnapInTracer.TraceError(message);
|
|
throw new PSSnapInException(psSnapInInfo.Name, message);
|
|
}
|
|
|
|
assembly = Assembly.LoadFrom(psSnapInInfo.AbsoluteModulePath);
|
|
}
|
|
catch (FileLoadException e)
|
|
{
|
|
s_PSSnapInTracer.TraceError("Not able to load assembly {0}: {1}", psSnapInInfo.AssemblyName, e.Message);
|
|
throw new PSSnapInException(psSnapInInfo.Name, e.Message);
|
|
}
|
|
catch (BadImageFormatException e)
|
|
{
|
|
s_PSSnapInTracer.TraceError("Not able to load assembly {0}: {1}", psSnapInInfo.AssemblyName, e.Message);
|
|
throw new PSSnapInException(psSnapInInfo.Name, e.Message);
|
|
}
|
|
catch (FileNotFoundException e)
|
|
{
|
|
s_PSSnapInTracer.TraceError("Not able to load assembly {0}: {1}", psSnapInInfo.AssemblyName, e.Message);
|
|
throw new PSSnapInException(psSnapInInfo.Name, e.Message);
|
|
}
|
|
|
|
return assembly;
|
|
}
|
|
|
|
private static bool TryGetCustomAttribute<T>(Type decoratedType, out T attribute) where T : Attribute
|
|
{
|
|
var attributes = decoratedType.GetCustomAttributes<T>(inherit: false);
|
|
attribute = attributes.FirstOrDefault();
|
|
return attribute != null;
|
|
}
|
|
|
|
internal static void AnalyzePSSnapInAssembly(
|
|
Assembly assembly,
|
|
string name,
|
|
PSSnapInInfo psSnapInInfo,
|
|
PSModuleInfo moduleInfo,
|
|
out Dictionary<string, SessionStateCmdletEntry> cmdlets,
|
|
out Dictionary<string, List<SessionStateAliasEntry>> aliases,
|
|
out Dictionary<string, SessionStateProviderEntry> providers,
|
|
out string helpFile)
|
|
{
|
|
helpFile = null;
|
|
if (assembly == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(assembly));
|
|
}
|
|
|
|
cmdlets = null;
|
|
aliases = null;
|
|
providers = null;
|
|
|
|
// See if this assembly has already been scanned...
|
|
|
|
Dictionary<string, Tuple<SessionStateCmdletEntry, List<SessionStateAliasEntry>>> cachedCmdlets;
|
|
if (s_cmdletCache.Value.TryGetValue(assembly, out cachedCmdlets))
|
|
{
|
|
cmdlets = new Dictionary<string, SessionStateCmdletEntry>(cachedCmdlets.Count, StringComparer.OrdinalIgnoreCase);
|
|
aliases = new Dictionary<string, List<SessionStateAliasEntry>>(cachedCmdlets.Count, StringComparer.OrdinalIgnoreCase);
|
|
|
|
foreach (var pair in cachedCmdlets)
|
|
{
|
|
var key = pair.Key;
|
|
var entry = pair.Value;
|
|
if (entry.Item1.PSSnapIn == null && psSnapInInfo != null)
|
|
{
|
|
entry.Item1.SetPSSnapIn(psSnapInInfo);
|
|
}
|
|
|
|
var newEntry = (SessionStateCmdletEntry)entry.Item1.Clone();
|
|
if (newEntry.PSSnapIn != null && psSnapInInfo == null)
|
|
{
|
|
newEntry.SetPSSnapIn(null);
|
|
}
|
|
|
|
cmdlets[key] = newEntry;
|
|
|
|
if (entry.Item2 != null)
|
|
{
|
|
var aliasList = new List<SessionStateAliasEntry>();
|
|
foreach (var alias in entry.Item2)
|
|
{
|
|
if (alias.PSSnapIn == null && psSnapInInfo != null)
|
|
{
|
|
alias.SetPSSnapIn(psSnapInInfo);
|
|
}
|
|
|
|
var newAliasEntry = (SessionStateAliasEntry)alias.Clone();
|
|
if (newAliasEntry.PSSnapIn != null && psSnapInInfo == null)
|
|
{
|
|
newAliasEntry.SetPSSnapIn(null);
|
|
}
|
|
|
|
aliasList.Add(newAliasEntry);
|
|
}
|
|
|
|
aliases[key] = aliasList;
|
|
}
|
|
}
|
|
}
|
|
|
|
Dictionary<string, SessionStateProviderEntry> cachedProviders;
|
|
if (s_providerCache.Value.TryGetValue(assembly, out cachedProviders))
|
|
{
|
|
providers = new Dictionary<string, SessionStateProviderEntry>(s_providerCache.Value.Count, StringComparer.OrdinalIgnoreCase);
|
|
foreach (var pair in cachedProviders)
|
|
{
|
|
var key = pair.Key;
|
|
var entry = pair.Value;
|
|
if (entry.PSSnapIn == null && psSnapInInfo != null)
|
|
{
|
|
entry.SetPSSnapIn(psSnapInInfo);
|
|
}
|
|
|
|
var newEntry = (SessionStateProviderEntry)entry.Clone();
|
|
if (newEntry.PSSnapIn != null && psSnapInInfo == null)
|
|
{
|
|
newEntry.SetPSSnapIn(null);
|
|
}
|
|
|
|
providers[key] = newEntry;
|
|
}
|
|
}
|
|
|
|
string assemblyPath = assembly.Location;
|
|
if (cmdlets != null || providers != null)
|
|
{
|
|
if (!s_assembliesWithModuleInitializerCache.Value.ContainsKey(assembly))
|
|
{
|
|
s_PSSnapInTracer.WriteLine("Returning cached cmdlet and provider entries for {0}", assemblyPath);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
s_PSSnapInTracer.WriteLine("Executing IModuleAssemblyInitializer.Import for {0}", assemblyPath);
|
|
var assemblyTypes = GetAssemblyTypes(assembly, name);
|
|
ExecuteModuleInitializer(assembly, assemblyTypes);
|
|
return;
|
|
}
|
|
}
|
|
|
|
s_PSSnapInTracer.WriteLine("Analyzing assembly {0} for cmdlet and providers", assemblyPath);
|
|
helpFile = GetHelpFile(assemblyPath);
|
|
|
|
if (psSnapInInfo != null && psSnapInInfo.Name.Equals(InitialSessionState.CoreSnapin, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
InitializeCoreCmdletsAndProviders(psSnapInInfo, out cmdlets, out providers, helpFile);
|
|
|
|
#if DEBUG
|
|
// Make sure the pre-built cmdlet and provider tables match what reflection finds. This will help
|
|
// avoid issues where you add a cmdlet but forget to update the table in InitializeCoreCmdletsAndProviders.
|
|
|
|
Dictionary<string, SessionStateCmdletEntry> cmdletsCheck = null;
|
|
Dictionary<string, SessionStateProviderEntry> providersCheck = null;
|
|
Dictionary<string, List<SessionStateAliasEntry>> aliasesCheck = null;
|
|
AnalyzeModuleAssemblyWithReflection(assembly, name, psSnapInInfo, moduleInfo, helpFile, ref cmdletsCheck, ref aliasesCheck, ref providersCheck);
|
|
|
|
Diagnostics.Assert(aliasesCheck == null, "InitializeCoreCmdletsAndProviders assumes no aliases are defined in System.Management.Automation.dll");
|
|
Diagnostics.Assert(providersCheck.Count == providers.Count, "new Provider added to System.Management.Automation.dll - update InitializeCoreCmdletsAndProviders");
|
|
foreach (var pair in providersCheck)
|
|
{
|
|
SessionStateProviderEntry other;
|
|
if (providers.TryGetValue(pair.Key, out other))
|
|
{
|
|
Diagnostics.Assert((object)pair.Value.HelpFileName == (object)other.HelpFileName, "Pre-generated Provider help file incorrect");
|
|
Diagnostics.Assert(pair.Value.ImplementingType == other.ImplementingType, "Pre-generated Provider implementing type incorrect");
|
|
Diagnostics.Assert(string.Equals(pair.Value.Name, other.Name, StringComparison.Ordinal), "Pre-generated Provider name incorrect");
|
|
Diagnostics.Assert(pair.Value.PSSnapIn == other.PSSnapIn, "Pre-generated Provider snapin type incorrect");
|
|
Diagnostics.Assert(pair.Value.Module == other.Module, "Pre-generated Provider module incorrect");
|
|
Diagnostics.Assert(pair.Value.Visibility == other.Visibility, "Pre-generated Provider visibility incorrect");
|
|
}
|
|
else
|
|
{
|
|
Diagnostics.Assert(false, "Missing provider: " + pair.Key);
|
|
}
|
|
}
|
|
|
|
Diagnostics.Assert(cmdletsCheck.Count == cmdlets.Count, "new Cmdlet added to System.Management.Automation.dll - update InitializeCoreCmdletsAndProviders");
|
|
|
|
foreach (var pair in cmdletsCheck)
|
|
{
|
|
SessionStateCmdletEntry other;
|
|
if (cmdlets.TryGetValue(pair.Key, out other))
|
|
{
|
|
Diagnostics.Assert((object)pair.Value.HelpFileName == (object)other.HelpFileName, "Pre-generated Provider help file incorrect");
|
|
Diagnostics.Assert(pair.Value.ImplementingType == other.ImplementingType, "Pre-generated Provider implementing type incorrect");
|
|
Diagnostics.Assert(string.Equals(pair.Value.Name, other.Name, StringComparison.Ordinal), "Pre-generated Provider name incorrect");
|
|
Diagnostics.Assert(pair.Value.PSSnapIn == other.PSSnapIn, "Pre-generated Provider snapin type incorrect");
|
|
Diagnostics.Assert(pair.Value.Module == other.Module, "Pre-generated Provider module incorrect");
|
|
Diagnostics.Assert(pair.Value.Visibility == other.Visibility, "Pre-generated Provider visibility incorrect");
|
|
}
|
|
else
|
|
{
|
|
Diagnostics.Assert(false, "Pre-generated Cmdlet missing: " + pair.Key);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
AnalyzeModuleAssemblyWithReflection(assembly, name, psSnapInInfo, moduleInfo, helpFile, ref cmdlets, ref aliases, ref providers);
|
|
}
|
|
|
|
// Cache the cmdlet and provider info for this assembly...
|
|
// We need to cache a clone of this data *before* the
|
|
// module info is set on it since module info can't be shared
|
|
// across runspaces. When these entries are hit in the cache,
|
|
// copies will be returned to ensure that the cache is never tied to a runspace.
|
|
|
|
if (cmdlets != null)
|
|
{
|
|
var clone = new Dictionary<string, Tuple<SessionStateCmdletEntry, List<SessionStateAliasEntry>>>(cmdlets.Count, StringComparer.OrdinalIgnoreCase);
|
|
List<SessionStateAliasEntry> aliasesCloneList = null;
|
|
|
|
foreach (var entry in cmdlets)
|
|
{
|
|
List<SessionStateAliasEntry> aliasEntries;
|
|
if (aliases != null && aliases.TryGetValue(entry.Key, out aliasEntries))
|
|
{
|
|
aliasesCloneList = new List<SessionStateAliasEntry>(aliases.Count);
|
|
foreach (var aliasEntry in aliasEntries)
|
|
{
|
|
aliasesCloneList.Add((SessionStateAliasEntry)aliasEntry.Clone());
|
|
}
|
|
}
|
|
|
|
clone[entry.Key] = new Tuple<SessionStateCmdletEntry, List<SessionStateAliasEntry>>((SessionStateCmdletEntry)entry.Value.Clone(), aliasesCloneList);
|
|
}
|
|
|
|
s_cmdletCache.Value[assembly] = clone;
|
|
}
|
|
|
|
if (providers != null)
|
|
{
|
|
var clone = new Dictionary<string, SessionStateProviderEntry>(providers.Count, StringComparer.OrdinalIgnoreCase);
|
|
foreach (var entry in providers)
|
|
{
|
|
clone[entry.Key] = (SessionStateProviderEntry)entry.Value.Clone();
|
|
}
|
|
|
|
s_providerCache.Value[assembly] = clone;
|
|
}
|
|
}
|
|
|
|
private static void AnalyzeModuleAssemblyWithReflection(
|
|
Assembly assembly,
|
|
string name,
|
|
PSSnapInInfo psSnapInInfo,
|
|
PSModuleInfo moduleInfo,
|
|
string helpFile,
|
|
ref Dictionary<string, SessionStateCmdletEntry> cmdlets,
|
|
ref Dictionary<string, List<SessionStateAliasEntry>> aliases,
|
|
ref Dictionary<string, SessionStateProviderEntry> providers)
|
|
{
|
|
var assemblyTypes = GetAssemblyTypes(assembly, name);
|
|
ExecuteModuleInitializer(assembly, assemblyTypes);
|
|
|
|
foreach (Type type in assemblyTypes)
|
|
{
|
|
if (!HasDefaultConstructor(type)) { continue; }
|
|
|
|
// Check for cmdlets
|
|
if (IsCmdletClass(type) && TryGetCustomAttribute(type, out CmdletAttribute cmdletAttribute))
|
|
{
|
|
if (TryGetCustomAttribute(type, out ExperimentalAttribute expAttribute) && expAttribute.ToHide)
|
|
{
|
|
// If 'ExperimentalAttribute' is specified on the cmdlet type and the
|
|
// effective action at run time is 'Hide', then we ignore the type.
|
|
continue;
|
|
}
|
|
|
|
string cmdletName = cmdletAttribute.VerbName + "-" + cmdletAttribute.NounName;
|
|
if (cmdlets != null && cmdlets.ContainsKey(cmdletName))
|
|
{
|
|
string message = StringUtil.Format(ConsoleInfoErrorStrings.PSSnapInDuplicateCmdlets, cmdletName, name);
|
|
s_PSSnapInTracer.TraceError(message);
|
|
throw new PSSnapInException(name, message);
|
|
}
|
|
|
|
SessionStateCmdletEntry cmdlet = new SessionStateCmdletEntry(cmdletName, type, helpFile);
|
|
if (psSnapInInfo != null) { cmdlet.SetPSSnapIn(psSnapInInfo); }
|
|
|
|
if (moduleInfo != null) { cmdlet.SetModule(moduleInfo); }
|
|
|
|
cmdlets = cmdlets ?? new Dictionary<string, SessionStateCmdletEntry>(StringComparer.OrdinalIgnoreCase);
|
|
cmdlets.Add(cmdletName, cmdlet);
|
|
|
|
if (TryGetCustomAttribute(type, out AliasAttribute aliasAttribute))
|
|
{
|
|
aliases = aliases ?? new Dictionary<string, List<SessionStateAliasEntry>>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
var aliasList = new List<SessionStateAliasEntry>();
|
|
foreach (var alias in aliasAttribute.AliasNames)
|
|
{
|
|
// Alias declared by 'AliasAttribute' is set with the option 'ScopedItemOptions.None', because we believe
|
|
// the users of the cmdlet, instead of the author, should have control of what options applied to an alias
|
|
// ('ScopedItemOptions.ReadOnly' and/or 'ScopedItemOptions.AllScopes').
|
|
var aliasEntry = new SessionStateAliasEntry(alias, cmdletName, description: string.Empty, ScopedItemOptions.None);
|
|
if (psSnapInInfo != null) { aliasEntry.SetPSSnapIn(psSnapInInfo); }
|
|
|
|
if (moduleInfo != null) { aliasEntry.SetModule(moduleInfo); }
|
|
|
|
aliasList.Add(aliasEntry);
|
|
}
|
|
|
|
aliases.Add(cmdletName, aliasList);
|
|
}
|
|
|
|
s_PSSnapInTracer.WriteLine("{0} from type {1} is added as a cmdlet. ", cmdletName, type.FullName);
|
|
}
|
|
// Check for providers
|
|
else if (IsProviderClass(type) && TryGetCustomAttribute(type, out CmdletProviderAttribute providerAttribute))
|
|
{
|
|
if (TryGetCustomAttribute(type, out ExperimentalAttribute expAttribute) && expAttribute.ToHide)
|
|
{
|
|
// If 'ExperimentalAttribute' is specified on the provider type and
|
|
// the effective action at run time is 'Hide', then we ignore the type.
|
|
continue;
|
|
}
|
|
|
|
string providerName = providerAttribute.ProviderName;
|
|
if (providers != null && providers.ContainsKey(providerName))
|
|
{
|
|
string message = StringUtil.Format(ConsoleInfoErrorStrings.PSSnapInDuplicateProviders, providerName, psSnapInInfo.Name);
|
|
s_PSSnapInTracer.TraceError(message);
|
|
throw new PSSnapInException(psSnapInInfo.Name, message);
|
|
}
|
|
|
|
SessionStateProviderEntry provider = new SessionStateProviderEntry(providerName, type, helpFile);
|
|
if (psSnapInInfo != null) { provider.SetPSSnapIn(psSnapInInfo); }
|
|
|
|
if (moduleInfo != null) { provider.SetModule(moduleInfo); }
|
|
|
|
providers = providers ?? new Dictionary<string, SessionStateProviderEntry>(StringComparer.OrdinalIgnoreCase);
|
|
providers.Add(providerName, provider);
|
|
|
|
s_PSSnapInTracer.WriteLine("{0} from type {1} is added as a provider. ", providerName, type.FullName);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void InitializeCoreCmdletsAndProviders(
|
|
PSSnapInInfo psSnapInInfo,
|
|
out Dictionary<string, SessionStateCmdletEntry> cmdlets,
|
|
out Dictionary<string, SessionStateProviderEntry> providers,
|
|
string helpFile)
|
|
{
|
|
cmdlets = new Dictionary<string, SessionStateCmdletEntry>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
{ "Add-History", new SessionStateCmdletEntry("Add-History", typeof(AddHistoryCommand), helpFile) },
|
|
{ "Clear-History", new SessionStateCmdletEntry("Clear-History", typeof(ClearHistoryCommand), helpFile) },
|
|
{ "Debug-Job", new SessionStateCmdletEntry("Debug-Job", typeof(DebugJobCommand), helpFile) },
|
|
#if !UNIX
|
|
{ "Disable-PSRemoting", new SessionStateCmdletEntry("Disable-PSRemoting", typeof(DisablePSRemotingCommand), helpFile) },
|
|
{ "Enable-PSRemoting", new SessionStateCmdletEntry("Enable-PSRemoting", typeof(EnablePSRemotingCommand), helpFile) },
|
|
{ "Disable-PSSessionConfiguration", new SessionStateCmdletEntry("Disable-PSSessionConfiguration", typeof(DisablePSSessionConfigurationCommand), helpFile) },
|
|
{ "Enable-PSSessionConfiguration", new SessionStateCmdletEntry("Enable-PSSessionConfiguration", typeof(EnablePSSessionConfigurationCommand), helpFile) },
|
|
{ "Get-PSSessionCapability", new SessionStateCmdletEntry("Get-PSSessionCapability", typeof(GetPSSessionCapabilityCommand), helpFile) },
|
|
{ "Get-PSSessionConfiguration", new SessionStateCmdletEntry("Get-PSSessionConfiguration", typeof(GetPSSessionConfigurationCommand), helpFile) },
|
|
{ "New-PSSessionConfigurationFile", new SessionStateCmdletEntry("New-PSSessionConfigurationFile", typeof(NewPSSessionConfigurationFileCommand), helpFile) },
|
|
{ "Receive-PSSession", new SessionStateCmdletEntry("Receive-PSSession", typeof(ReceivePSSessionCommand), helpFile) },
|
|
{ "Register-PSSessionConfiguration", new SessionStateCmdletEntry("Register-PSSessionConfiguration", typeof(RegisterPSSessionConfigurationCommand), helpFile) },
|
|
{ "Unregister-PSSessionConfiguration", new SessionStateCmdletEntry("Unregister-PSSessionConfiguration", typeof(UnregisterPSSessionConfigurationCommand), helpFile) },
|
|
{ "Set-PSSessionConfiguration", new SessionStateCmdletEntry("Set-PSSessionConfiguration", typeof(SetPSSessionConfigurationCommand), helpFile) },
|
|
{ "Test-PSSessionConfigurationFile", new SessionStateCmdletEntry("Test-PSSessionConfigurationFile", typeof(TestPSSessionConfigurationFileCommand), helpFile) },
|
|
{ "Connect-PSSession", new SessionStateCmdletEntry("Connect-PSSession", typeof(ConnectPSSessionCommand), helpFile) },
|
|
{ "Disconnect-PSSession", new SessionStateCmdletEntry("Disconnect-PSSession", typeof(DisconnectPSSessionCommand), helpFile) },
|
|
#endif
|
|
{ "Disable-ExperimentalFeature", new SessionStateCmdletEntry("Disable-ExperimentalFeature", typeof(DisableExperimentalFeatureCommand), helpFile) },
|
|
{ "Enable-ExperimentalFeature", new SessionStateCmdletEntry("Enable-ExperimentalFeature", typeof(EnableExperimentalFeatureCommand), helpFile) },
|
|
{ "Enter-PSHostProcess", new SessionStateCmdletEntry("Enter-PSHostProcess", typeof(EnterPSHostProcessCommand), helpFile) },
|
|
{ "Enter-PSSession", new SessionStateCmdletEntry("Enter-PSSession", typeof(EnterPSSessionCommand), helpFile) },
|
|
{ "Exit-PSHostProcess", new SessionStateCmdletEntry("Exit-PSHostProcess", typeof(ExitPSHostProcessCommand), helpFile) },
|
|
{ "Exit-PSSession", new SessionStateCmdletEntry("Exit-PSSession", typeof(ExitPSSessionCommand), helpFile) },
|
|
{ "Export-ModuleMember", new SessionStateCmdletEntry("Export-ModuleMember", typeof(ExportModuleMemberCommand), helpFile) },
|
|
{ "ForEach-Object", new SessionStateCmdletEntry("ForEach-Object", typeof(ForEachObjectCommand), helpFile) },
|
|
{ "Get-Command", new SessionStateCmdletEntry("Get-Command", typeof(GetCommandCommand), helpFile) },
|
|
{ "Get-ExperimentalFeature", new SessionStateCmdletEntry("Get-ExperimentalFeature", typeof(GetExperimentalFeatureCommand), helpFile) },
|
|
{ "Get-Help", new SessionStateCmdletEntry("Get-Help", typeof(GetHelpCommand), helpFile) },
|
|
{ "Get-History", new SessionStateCmdletEntry("Get-History", typeof(GetHistoryCommand), helpFile) },
|
|
{ "Get-Job", new SessionStateCmdletEntry("Get-Job", typeof(GetJobCommand), helpFile) },
|
|
{ "Get-Module", new SessionStateCmdletEntry("Get-Module", typeof(GetModuleCommand), helpFile) },
|
|
{ "Get-PSHostProcessInfo", new SessionStateCmdletEntry("Get-PSHostProcessInfo", typeof(GetPSHostProcessInfoCommand), helpFile) },
|
|
{ "Get-PSSession", new SessionStateCmdletEntry("Get-PSSession", typeof(GetPSSessionCommand), helpFile) },
|
|
{ "Import-Module", new SessionStateCmdletEntry("Import-Module", typeof(ImportModuleCommand), helpFile) },
|
|
{ "Invoke-Command", new SessionStateCmdletEntry("Invoke-Command", typeof(InvokeCommandCommand), helpFile) },
|
|
{ "Invoke-History", new SessionStateCmdletEntry("Invoke-History", typeof(InvokeHistoryCommand), helpFile) },
|
|
{ "New-Module", new SessionStateCmdletEntry("New-Module", typeof(NewModuleCommand), helpFile) },
|
|
{ "New-ModuleManifest", new SessionStateCmdletEntry("New-ModuleManifest", typeof(NewModuleManifestCommand), helpFile) },
|
|
{ "New-PSRoleCapabilityFile", new SessionStateCmdletEntry("New-PSRoleCapabilityFile", typeof(NewPSRoleCapabilityFileCommand), helpFile) },
|
|
{ "New-PSSession", new SessionStateCmdletEntry("New-PSSession", typeof(NewPSSessionCommand), helpFile) },
|
|
{ "New-PSSessionOption", new SessionStateCmdletEntry("New-PSSessionOption", typeof(NewPSSessionOptionCommand), helpFile) },
|
|
{ "New-PSTransportOption", new SessionStateCmdletEntry("New-PSTransportOption", typeof(NewPSTransportOptionCommand), helpFile) },
|
|
{ "Out-Default", new SessionStateCmdletEntry("Out-Default", typeof(OutDefaultCommand), helpFile) },
|
|
{ "Out-Host", new SessionStateCmdletEntry("Out-Host", typeof(OutHostCommand), helpFile) },
|
|
{ "Out-Null", new SessionStateCmdletEntry("Out-Null", typeof(OutNullCommand), helpFile) },
|
|
{ "Receive-Job", new SessionStateCmdletEntry("Receive-Job", typeof(ReceiveJobCommand), helpFile) },
|
|
{ "Register-ArgumentCompleter", new SessionStateCmdletEntry("Register-ArgumentCompleter", typeof(RegisterArgumentCompleterCommand), helpFile) },
|
|
{ "Remove-Job", new SessionStateCmdletEntry("Remove-Job", typeof(RemoveJobCommand), helpFile) },
|
|
{ "Remove-Module", new SessionStateCmdletEntry("Remove-Module", typeof(RemoveModuleCommand), helpFile) },
|
|
{ "Remove-PSSession", new SessionStateCmdletEntry("Remove-PSSession", typeof(RemovePSSessionCommand), helpFile) },
|
|
{ "Save-Help", new SessionStateCmdletEntry("Save-Help", typeof(SaveHelpCommand), helpFile) },
|
|
{ "Set-PSDebug", new SessionStateCmdletEntry("Set-PSDebug", typeof(SetPSDebugCommand), helpFile) },
|
|
{ "Set-StrictMode", new SessionStateCmdletEntry("Set-StrictMode", typeof(SetStrictModeCommand), helpFile) },
|
|
{ "Start-Job", new SessionStateCmdletEntry("Start-Job", typeof(StartJobCommand), helpFile) },
|
|
{ "Stop-Job", new SessionStateCmdletEntry("Stop-Job", typeof(StopJobCommand), helpFile) },
|
|
{ "Test-ModuleManifest", new SessionStateCmdletEntry("Test-ModuleManifest", typeof(TestModuleManifestCommand), helpFile) },
|
|
{ "Update-Help", new SessionStateCmdletEntry("Update-Help", typeof(UpdateHelpCommand), helpFile) },
|
|
{ "Wait-Job", new SessionStateCmdletEntry("Wait-Job", typeof(WaitJobCommand), helpFile) },
|
|
{ "Where-Object", new SessionStateCmdletEntry("Where-Object", typeof(WhereObjectCommand), helpFile) },
|
|
#if !CORECLR
|
|
{ "Add-PSSnapin", new SessionStateCmdletEntry("Add-PSSnapin", typeof(AddPSSnapinCommand), helpFile) },
|
|
{ "Export-Console", new SessionStateCmdletEntry("Export-Console", typeof(ExportConsoleCommand), helpFile) },
|
|
{ "Get-PSSnapin", new SessionStateCmdletEntry("Get-PSSnapin", typeof(GetPSSnapinCommand), helpFile) },
|
|
{ "Remove-PSSnapin", new SessionStateCmdletEntry("Remove-PSSnapin", typeof(RemovePSSnapinCommand), helpFile) },
|
|
{ "Resume-Job", new SessionStateCmdletEntry("Resume-Job", typeof(ResumeJobCommand), helpFile) },
|
|
{ "Suspend-Job", new SessionStateCmdletEntry("Suspend-Job", typeof(SuspendJobCommand), helpFile) },
|
|
#endif
|
|
// Not exported, but are added via reflection so added here as well, though maybe they shouldn't be
|
|
{ "Out-LineOutput", new SessionStateCmdletEntry("Out-LineOutput", typeof(OutLineOutputCommand), helpFile) },
|
|
{ "Format-Default", new SessionStateCmdletEntry("Format-Default", typeof(FormatDefaultCommand), helpFile) },
|
|
};
|
|
foreach (var val in cmdlets.Values)
|
|
{
|
|
val.SetPSSnapIn(psSnapInInfo);
|
|
}
|
|
|
|
providers = new Dictionary<string, SessionStateProviderEntry>(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
#if !UNIX
|
|
{ "Registry", new SessionStateProviderEntry("Registry", typeof(RegistryProvider), helpFile) },
|
|
#endif
|
|
{ "Alias", new SessionStateProviderEntry("Alias", typeof(AliasProvider), helpFile) },
|
|
{ "Environment", new SessionStateProviderEntry("Environment", typeof(EnvironmentProvider), helpFile) },
|
|
{ "FileSystem" , new SessionStateProviderEntry("FileSystem", typeof(FileSystemProvider), helpFile) },
|
|
{ "Function", new SessionStateProviderEntry("Function", typeof(FunctionProvider), helpFile) },
|
|
{ "Variable", new SessionStateProviderEntry("Variable", typeof(VariableProvider), helpFile) },
|
|
};
|
|
foreach (var val in providers.Values)
|
|
{
|
|
val.SetPSSnapIn(psSnapInInfo);
|
|
}
|
|
}
|
|
|
|
private static void ExecuteModuleInitializer(Assembly assembly, IEnumerable<Type> assemblyTypes)
|
|
{
|
|
foreach (Type type in assemblyTypes)
|
|
{
|
|
if (typeof(IModuleAssemblyInitializer).IsAssignableFrom(type))
|
|
{
|
|
s_assembliesWithModuleInitializerCache.Value[assembly] = true;
|
|
var moduleInitializer = (IModuleAssemblyInitializer)Activator.CreateInstance(type, true);
|
|
moduleInitializer.OnImport();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static IEnumerable<Type> GetAssemblyTypes(Assembly assembly, string name)
|
|
{
|
|
try
|
|
{
|
|
// Return types that are public, non-abstract, non-interface and non-valueType.
|
|
return assembly.ExportedTypes.Where(t => !t.IsAbstract && !t.IsInterface && !t.IsValueType);
|
|
}
|
|
catch (ReflectionTypeLoadException e)
|
|
{
|
|
string message = e.Message + "\nLoader Exceptions: \n";
|
|
if (e.LoaderExceptions != null)
|
|
{
|
|
foreach (Exception exception in e.LoaderExceptions)
|
|
{
|
|
message += "\n" + exception.Message;
|
|
}
|
|
}
|
|
|
|
s_PSSnapInTracer.TraceError(message);
|
|
throw new PSSnapInException(name, message);
|
|
}
|
|
}
|
|
|
|
// cmdletCache holds the list of cmdlets along with its aliases per each assembly.
|
|
private static Lazy<ConcurrentDictionary<Assembly, Dictionary<string, Tuple<SessionStateCmdletEntry, List<SessionStateAliasEntry>>>>> s_cmdletCache =
|
|
new Lazy<ConcurrentDictionary<Assembly, Dictionary<string, Tuple<SessionStateCmdletEntry, List<SessionStateAliasEntry>>>>>();
|
|
private static Lazy<ConcurrentDictionary<Assembly, Dictionary<string, SessionStateProviderEntry>>> s_providerCache =
|
|
new Lazy<ConcurrentDictionary<Assembly, Dictionary<string, SessionStateProviderEntry>>>();
|
|
// Using a ConcurrentDictionary for this so that we can avoid having a private lock variable. We use only the keys for checking.
|
|
private static Lazy<ConcurrentDictionary<Assembly, bool>> s_assembliesWithModuleInitializerCache = new Lazy<ConcurrentDictionary<Assembly, bool>>();
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static bool IsCmdletClass(Type type)
|
|
{
|
|
return type.IsSubclassOf(typeof(System.Management.Automation.Cmdlet));
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static bool IsProviderClass(Type type)
|
|
{
|
|
return type.IsSubclassOf(typeof(System.Management.Automation.Provider.CmdletProvider));
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static bool HasDefaultConstructor(Type type)
|
|
{
|
|
return !(type.GetConstructor(Type.EmptyTypes) == null);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static string GetHelpFile(string assemblyPath)
|
|
{
|
|
// Help files exist only for original module assemblies, not for generated Ngen binaries
|
|
return Path.GetFileName(assemblyPath).Replace(".ni.dll", ".dll") + StringLiterals.HelpFileExtension;
|
|
}
|
|
|
|
private static PSTraceSource s_PSSnapInTracer = PSTraceSource.GetTracer("PSSnapInLoadUnload", "Loading and unloading mshsnapins", false);
|
|
}
|
|
|
|
// Guid is {15d4c170-2f29-5689-a0e2-d95b0c7b4ea0}
|
|
|
|
[EventSource(Name = "Microsoft-PowerShell-Runspaces")]
|
|
internal class RunspaceEventSource : EventSource
|
|
{
|
|
internal static RunspaceEventSource Log = new RunspaceEventSource();
|
|
|
|
public void OpenRunspaceStart() { WriteEvent(1); }
|
|
|
|
public void OpenRunspaceStop() { WriteEvent(2); }
|
|
|
|
public void LoadAssembliesStart() { WriteEvent(3); }
|
|
|
|
public void LoadAssembliesStop() { WriteEvent(4); }
|
|
|
|
public void UpdateFormatTableStart() { WriteEvent(5); }
|
|
|
|
public void UpdateFormatTableStop() { WriteEvent(6); }
|
|
|
|
public void UpdateTypeTableStart() { WriteEvent(7); }
|
|
|
|
public void UpdateTypeTableStop() { WriteEvent(8); }
|
|
|
|
public void LoadProvidersStart() { WriteEvent(9); }
|
|
|
|
public void LoadProvidersStop() { WriteEvent(10); }
|
|
|
|
public void LoadCommandsStart() { WriteEvent(11); }
|
|
|
|
public void LoadCommandsStop() { WriteEvent(12); }
|
|
|
|
public void LoadVariablesStart() { WriteEvent(13); }
|
|
|
|
public void LoadVariablesStop() { WriteEvent(14); }
|
|
|
|
public void LoadEnvironmentVariablesStart() { WriteEvent(15); }
|
|
|
|
public void LoadEnvironmentVariablesStop() { WriteEvent(16); }
|
|
|
|
public void LoadAssemblyStart(string Name, string FileName) { WriteEvent(17, Name, FileName); }
|
|
|
|
public void LoadAssemblyStop(string Name, string FileName) { WriteEvent(18, Name, FileName); }
|
|
|
|
public void ProcessFormatFileStart(string FileName) { WriteEvent(19, FileName); }
|
|
|
|
public void ProcessFormatFileStop(string FileName) { WriteEvent(20, FileName); }
|
|
|
|
public void ProcessTypeFileStart(string FileName) { WriteEvent(21, FileName); }
|
|
|
|
public void ProcessTypeFileStop(string FileName) { WriteEvent(22, FileName); }
|
|
|
|
public void LoadProviderStart(string Name) { WriteEvent(23, Name); }
|
|
|
|
public void LoadProviderStop(string Name) { WriteEvent(24, Name); }
|
|
|
|
public void LoadCommandStart(string Name) { WriteEvent(25, Name); }
|
|
|
|
public void LoadCommandStop(string Name) { WriteEvent(26, Name); }
|
|
}
|
|
}
|