Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using System.Management.Automation.Internal;
using System.Management.Automation.Language;
using System.Management.Automation.Runspaces;
using System.Threading.Tasks;
using System.Security;
using System.Text;

Expand Down Expand Up @@ -325,15 +326,29 @@ internal string? ConfigurationName
get
{
AssertArgumentsParsed();
return _configurationName;
}

set
{
if (!string.IsNullOrEmpty(value))
if (_configNameFromGPOTask is null)
{
_configurationName = value;
return _configurationName;
}

// Querying the configuration name from group policy (both registry and setting files) is expensive,
// so we push the work to a thread pool worker to avoid blocking the main thread as much as possible.
lock (this)
{
if (_configNameFromGPOTask is not null)
{
string nameFromGPO = _configNameFromGPOTask.GetAwaiter().GetResult();
if (!string.IsNullOrEmpty(nameFromGPO))
{
_configurationName = nameFromGPO;
}

_configNameFromGPOTask = null;
}
}

return _configurationName;
}
}

Expand Down Expand Up @@ -573,13 +588,17 @@ private bool TryParseSettingFileHelper(string[] args, int settingFileArgIndex)
return true;
}

internal static string GetConfigurationNameFromGroupPolicy()
internal void GetConfigurationNameFromGroupPolicy()
{
// Current user policy takes precedence.
var consoleSessionSetting = Utils.GetPolicySetting<ConsoleSessionConfiguration>(Utils.CurrentUserThenSystemWideConfig);
_configNameFromGPOTask = Task.Run<string>(GetFromGPOImpl);

return (consoleSessionSetting?.EnableConsoleSessionConfiguration == true && !string.IsNullOrEmpty(consoleSessionSetting?.ConsoleSessionConfigurationName)) ?
consoleSessionSetting.ConsoleSessionConfigurationName : string.Empty;
static string GetFromGPOImpl()
{
// Current user policy takes precedence.
var consoleSessionSetting = Utils.GetPolicySetting<ConsoleSessionConfiguration>(Utils.CurrentUserThenSystemWideConfig);
return (consoleSessionSetting?.EnableConsoleSessionConfiguration == true && !string.IsNullOrEmpty(consoleSessionSetting?.ConsoleSessionConfigurationName)) ?
consoleSessionSetting.ConsoleSessionConfigurationName : string.Empty;
}
}

/// <summary>
Expand Down Expand Up @@ -1333,6 +1352,7 @@ private bool CollectArgs(string[] args, ref int i)
private bool _sshServerMode;
private bool _showVersion;
private string? _configurationName;
private Task<string>? _configNameFromGPOTask;
private string? _error;
private bool _showHelp;
private bool _showExtendedHelp;
Expand Down
118 changes: 56 additions & 62 deletions src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.PowerShell.Commands;
using Microsoft.PowerShell.Telemetry;
#if LEGACYTELEMETRY
Expand Down Expand Up @@ -138,8 +139,8 @@ internal static int Start(string bannerText, string helpText)
// improve startup performance.
}

Task telemetryTask = null;
uint exitCode = ExitCodeSuccess;

Thread.CurrentThread.Name = "ConsoleHost main thread";

try
Expand Down Expand Up @@ -183,33 +184,32 @@ internal static int Start(string bannerText, string helpText)
#if !UNIX
TaskbarJumpList.CreateRunAsAdministratorJumpList();
#endif

// First check for and handle PowerShell running in a server mode.
if (s_cpp.ServerMode)
{
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("ServerMode");
telemetryTask = SendStartupTelemetry("ServerMode");
ProfileOptimization.StartProfile("StartupProfileData-ServerMode");
System.Management.Automation.Remoting.Server.OutOfProcessMediator.Run(s_cpp.InitialCommand, s_cpp.WorkingDirectory);
exitCode = 0;
}
else if (s_cpp.NamedPipeServerMode)
{
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("NamedPipe");
telemetryTask = SendStartupTelemetry("NamedPipe");
ProfileOptimization.StartProfile("StartupProfileData-NamedPipeServerMode");
System.Management.Automation.Remoting.RemoteSessionNamedPipeServer.RunServerMode(
s_cpp.ConfigurationName);
exitCode = 0;
}
else if (s_cpp.SSHServerMode)
{
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("SSHServer");
telemetryTask = SendStartupTelemetry("SSHServer");
ProfileOptimization.StartProfile("StartupProfileData-SSHServerMode");
System.Management.Automation.Remoting.Server.SSHProcessMediator.Run(s_cpp.InitialCommand);
exitCode = 0;
}
else if (s_cpp.SocketServerMode)
{
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("SocketServerMode");
telemetryTask = SendStartupTelemetry("SocketServerMode");
ProfileOptimization.StartProfile("StartupProfileData-SocketServerMode");
System.Management.Automation.Remoting.Server.HyperVSocketMediator.Run(s_cpp.InitialCommand,
s_cpp.ConfigurationName);
Expand Down Expand Up @@ -243,7 +243,7 @@ internal static int Start(string bannerText, string helpText)
PSHost.IsStdOutputRedirected = Console.IsOutputRedirected;

// Send startup telemetry for ConsoleHost startup
ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("Normal");
telemetryTask = SendStartupTelemetry("Normal");

exitCode = s_theConsoleHost.Run(s_cpp, false);
}
Expand All @@ -257,6 +257,8 @@ internal static int Start(string bannerText, string helpText)
#endif
s_theConsoleHost.Dispose();
}

telemetryTask?.Wait();
}

unchecked
Expand All @@ -265,6 +267,16 @@ internal static int Start(string bannerText, string helpText)
}
}

private static Task SendStartupTelemetry(string mode)
{
return Task.Factory.StartNew(
state => ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry((string)state),
mode,
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default);
}

internal static void ParseCommandLine(string[] args)
{
s_cpp.Parse(args);
Expand All @@ -284,7 +296,7 @@ internal static void ParseCommandLine(string[] args)
// Check registry setting for a Group Policy ConfigurationName entry and
// use it to override anything set by the user.
// It depends on setting file so 'SetSystemConfigFilePath()' should be called before.
s_cpp.ConfigurationName = CommandLineParameterParser.GetConfigurationNameFromGroupPolicy();
s_cpp.GetConfigurationNameFromGroupPolicy();
}

private static readonly CommandLineParameterParser s_cpp = new CommandLineParameterParser();
Expand Down Expand Up @@ -1477,10 +1489,16 @@ private uint DoRunspaceLoop(string initialCommand, bool skipProfiles, Collection

while (!ShouldEndSession)
{
RunspaceCreationEventArgs args = new RunspaceCreationEventArgs(initialCommand, skipProfiles, staMode, configurationName, initialCommandArgs);
CreateRunspace(args);

if (ExitCode == ExitCodeInitFailure) { break; }
try
{
DoCreateRunspace(initialCommand, skipProfiles, staMode, configurationName, initialCommandArgs);
}
catch (ConsoleHostStartupException startupException)
{
ReportExceptionFallback(startupException.InnerException, startupException.Message);
ExitCode = ExitCodeInitFailure;
break;
}

if (!_noExit)
{
Expand Down Expand Up @@ -1553,22 +1571,6 @@ private Exception InitializeRunspaceHelper(string command, Executor exec, Execut
return e;
}

private void CreateRunspace(object runspaceCreationArgs)
{
RunspaceCreationEventArgs args = null;
try
{
args = runspaceCreationArgs as RunspaceCreationEventArgs;
Dbg.Assert(args != null, "Event Arguments to CreateRunspace should not be null");
DoCreateRunspace(args.InitialCommand, args.SkipProfiles, args.StaMode, args.ConfigurationName, args.InitialCommandArgs);
}
catch (ConsoleHostStartupException startupException)
{
ReportExceptionFallback(startupException.InnerException, startupException.Message);
ExitCode = ExitCodeInitFailure;
}
}

/// <summary>
/// Check if a screen reviewer utility is running.
/// When a screen reader is running, we don't auto-load the PSReadLine module at startup,
Expand Down Expand Up @@ -2953,46 +2955,38 @@ private class ConsoleHostStartupException : Exception

private static ConsoleHost s_theConsoleHost;

internal static InitialSessionState DefaultInitialSessionState;

[TraceSource("ConsoleHost", "ConsoleHost subclass of S.M.A.PSHost")]
private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("ConsoleHost", "ConsoleHost subclass of S.M.A.PSHost");
private static InitialSessionState s_defaultISS;
private static Task<InitialSessionState> s_defaultISSTask;

[TraceSource("ConsoleHostRunspaceInit", "Initialization code for ConsoleHost's Runspace")]
private static readonly PSTraceSource s_runspaceInitTracer =
PSTraceSource.GetTracer("ConsoleHostRunspaceInit", "Initialization code for ConsoleHost's Runspace", false);
}

/// <summary>
/// Defines arguments passed to ConsoleHost.CreateRunspace.
/// </summary>
internal sealed class RunspaceCreationEventArgs : EventArgs
{
/// <summary>
/// Constructs RunspaceCreationEventArgs.
/// </summary>
internal RunspaceCreationEventArgs(
string initialCommand,
bool skipProfiles,
bool staMode,
string configurationName,
Collection<CommandParameter> initialCommandArgs)
internal static InitialSessionState DefaultInitialSessionState
{
InitialCommand = initialCommand;
SkipProfiles = skipProfiles;
StaMode = staMode;
ConfigurationName = configurationName;
InitialCommandArgs = initialCommandArgs;
}
get
{
if (s_defaultISS is not null)
{
return s_defaultISS;
}

internal string InitialCommand { get; set; }
s_defaultISS = s_defaultISSTask.GetAwaiter().GetResult();
return s_defaultISS;
}

internal bool SkipProfiles { get; set; }
set
{
s_defaultISS = value;
}
}

internal bool StaMode { get; set; }
internal static void CreateDefaultInitialSessionState()
{
s_defaultISSTask = Task.Run(InitialSessionState.CreateDefault2);
}

internal string ConfigurationName { get; set; }
[TraceSource("ConsoleHost", "ConsoleHost subclass of S.M.A.PSHost")]
private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("ConsoleHost", "ConsoleHost subclass of S.M.A.PSHost");

internal Collection<CommandParameter> InitialCommandArgs { get; set; }
[TraceSource("ConsoleHostRunspaceInit", "Initialization code for ConsoleHost's Runspace")]
private static readonly PSTraceSource s_runspaceInitTracer =
PSTraceSource.GetTracer("ConsoleHostRunspaceInit", "Initialization code for ConsoleHost's Runspace", false);
}
} // namespace
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public static int Start([MarshalAs(UnmanagedType.LPArray, ArraySubType = Unmanag
ManagedEntranceStrings.ShellBannerNonWindowsPowerShell,
PSVersionInfo.GitCommitId);

ConsoleHost.DefaultInitialSessionState = InitialSessionState.CreateDefault2();
ConsoleHost.CreateDefaultInitialSessionState();

exitCode = ConsoleHost.Start(banner, ManagedEntranceStrings.UsageHelp);
}
Expand Down
Loading