MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Generic;6using System.Collections.ObjectModel;7using System.Diagnostics;8using System.Diagnostics.CodeAnalysis;9using System.Management.Automation.Internal;10using System.Management.Automation.Language;11using System.Management.Automation.Runspaces;12using System.Security;13 14using Dbg = System.Management.Automation;15 16namespace System.Management.Automation17{18 /// <summary>19 /// Holds the state of a Monad Shell session.20 /// </summary>21 [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "This is a bridge class between internal classes and a public interface. It requires this much coupling.")]22 internal sealed partial class SessionStateInternal23 {24 #region tracer25 26 /// <summary>27 /// An instance of the PSTraceSource class used for trace output28 /// using "SessionState" as the category.29 /// </summary>30 [Dbg.TraceSource(31 "SessionState",32 "SessionState Class")]33 private static readonly Dbg.PSTraceSource s_tracer =34 Dbg.PSTraceSource.GetTracer("SessionState",35 "SessionState Class");36 37 #endregion tracer38 39 #region Constructor40 41 /// <summary>42 /// Constructor for session state object.43 /// </summary>44 /// <param name="context">45 /// The context for the runspace to which this session state object belongs.46 /// </param>47 /// <exception cref="ArgumentNullException">48 /// if <paramref name="context"/> is null.49 /// </exception>50 internal SessionStateInternal(ExecutionContext context) : this(null, false, context)51 {52 }53 54 internal SessionStateInternal(SessionStateInternal parent, bool linkToGlobal, ExecutionContext context)55 {56 if (context == null)57 {58 throw PSTraceSource.NewArgumentNullException(nameof(context));59 }60 61 ExecutionContext = context;62 63 // Create the working directory stack. This64 // is used for the pushd and popd commands65 66 _workingLocationStack = new Dictionary<string, Stack<PathInfo>>(StringComparer.OrdinalIgnoreCase);67 68 // Conservative choice to limit the Set-Location history in order to limit memory impact in case of a regression.69 const int locationHistoryLimit = 20;70 _setLocationHistory = new HistoryStack<PathInfo>(locationHistoryLimit);71 72 GlobalScope = new SessionStateScope(null);73 ModuleScope = GlobalScope;74 _currentScope = GlobalScope;75 76 InitializeSessionStateInternalSpecialVariables(false);77 78 // Create the push the global scope on as79 // the starting script scope. That way, if you dot-source a script80 // that uses variables qualified by script: it works.81 GlobalScope.ScriptScope = GlobalScope;82 83 if (parent != null)84 {85 GlobalScope.Parent = parent.GlobalScope;86 87 // Copy the drives and providers from the parent...88 CopyProviders(parent);89 // During loading of core modules, providers are not populated.90 // We set the drive information later91 if (Providers != null && Providers.Count > 0)92 {93 CurrentDrive = parent.CurrentDrive;94 }95 96 // Link it to the global scope...97 if (linkToGlobal)98 {99 GlobalScope = parent.GlobalScope;100 }101 }102 else103 {104 _currentScope.LocalsTuple = MutableTuple.MakeTuple(Compiler.DottedLocalsTupleType, Compiler.DottedLocalsNameIndexMap);105 }106 }107 108 /// <summary>109 /// Add any special variables to the session state variable table. This routine110 /// must be called at construction time or if the variable table is reset.111 /// </summary>112 internal void InitializeSessionStateInternalSpecialVariables(bool clearVariablesTable)113 {114 if (clearVariablesTable)115 {116 // Clear the variable table117 GlobalScope.Variables.Clear();118 119 // Add in the per-scope default variables.120 GlobalScope.AddSessionStateScopeDefaultVariables();121 }122 123 // Set variable $Error124 PSVariable errorvariable = new PSVariable("Error", new ArrayList(), ScopedItemOptions.Constant);125 GlobalScope.SetVariable(errorvariable.Name, errorvariable, false, false, this, fastPath: true);126 127 // Set variable $PSDefaultParameterValues128 Collection<Attribute> attributes = new Collection<Attribute>();129 attributes.Add(new ArgumentTypeConverterAttribute(typeof(System.Management.Automation.DefaultParameterDictionary)));130 PSVariable psDefaultParameterValuesVariable = new PSVariable(SpecialVariables.PSDefaultParameterValues,131 new DefaultParameterDictionary(),132 ScopedItemOptions.None, attributes,133 RunspaceInit.PSDefaultParameterValuesDescription);134 GlobalScope.SetVariable(psDefaultParameterValuesVariable.Name, psDefaultParameterValuesVariable, false, false, this, fastPath: true);135 }136 137 #endregion Constructor138 139 #region Private data140 141 /// <summary>142 /// Provides all the path manipulation and globbing for Monad paths.143 /// </summary>144 internal LocationGlobber Globber145 {146 get { return _globberPrivate ??= ExecutionContext.LocationGlobber; }147 }148 149 private LocationGlobber _globberPrivate;150 151 /// <summary>152 /// The context of the runspace to which this session state object belongs.153 /// </summary>154 internal ExecutionContext ExecutionContext { get; }155 156 /// <summary>157 /// Returns the public session state facade object for this session state instance.158 /// </summary>159 internal SessionState PublicSessionState160 {161 get { return _publicSessionState ??= new SessionState(this); }162 163 set { _publicSessionState = value; }164 }165 166 private SessionState _publicSessionState;167 168 /// <summary>169 /// Gets the engine APIs to access providers.170 /// </summary>171 internal ProviderIntrinsics InvokeProvider172 {173 get { return _invokeProvider ??= new ProviderIntrinsics(this); }174 }175 176 private ProviderIntrinsics _invokeProvider;177 178 /// <summary>179 /// The module info object associated with this session state.180 /// </summary>181 internal PSModuleInfo Module { get; set; } = null;182 183 // This is used to maintain the order in which modules were imported.184 // This is used by Get-Command -All to order by last imported185 internal List<string> ModuleTableKeys = new List<string>();186 187 /// <summary>188 /// The private module table for this session state object...189 /// </summary>190 internal Dictionary<string, PSModuleInfo> ModuleTable { get; } = new Dictionary<string, PSModuleInfo>(StringComparer.OrdinalIgnoreCase);191 192 /// <summary>193 /// Get/set constraints for this execution environment.194 /// </summary>195 internal PSLanguageMode LanguageMode196 {197 get198 {199 return ExecutionContext.LanguageMode;200 }201 202 set203 {204 ExecutionContext.LanguageMode = value;205 }206 }207 208 /// <summary>209 /// If true the PowerShell debugger will use FullLanguage mode, otherwise it will use the current language mode.210 /// </summary>211 internal bool UseFullLanguageModeInDebugger212 {213 get214 {215 return ExecutionContext.UseFullLanguageModeInDebugger;216 }217 }218 219 /// <summary>220 /// The list of scripts that are allowed to be run. If the name "*"221 /// is in the list, then all scripts can be run. (This is the default.)222 /// </summary>223 public List<string> Scripts { get; } = new List<string>(new string[] { "*" });224 225 /// <summary>226 /// See if a script is allowed to be run.227 /// </summary>228 /// <param name="scriptPath">Path to check.</param>229 /// <returns>True if script is allowed.</returns>230 internal SessionStateEntryVisibility CheckScriptVisibility(string scriptPath)231 {232 return checkPathVisibility(Scripts, scriptPath);233 }234 235 /// <summary>236 /// The list of applications that are allowed to be run. If the name "*"237 /// is in the list, then all applications can be run. (This is the default.)238 /// </summary>239 public List<string> Applications { get; } = new List<string>(new string[] { "*" });240 241 /// <summary>242 /// List of functions/filters to export from this session state object...243 /// </summary>244 internal List<CmdletInfo> ExportedCmdlets { get; } = new List<CmdletInfo>();245 246 /// <summary>247 /// Defines the default command visibility for this session state. Binding an InitialSessionState instance248 /// with private members will set this to Private.249 /// </summary>250 internal SessionStateEntryVisibility DefaultCommandVisibility = SessionStateEntryVisibility.Public;251 252 /// <summary>253 /// Add an new SessionState cmdlet entry to this session state object...254 /// </summary>255 /// <param name="entry">The entry to add.</param>256 internal void AddSessionStateEntry(SessionStateCmdletEntry entry)257 {258 AddSessionStateEntry(entry, /*local*/false);259 }260 261 /// <summary>262 /// Add an new SessionState cmdlet entry to this session state object...263 /// </summary>264 /// <param name="entry">The entry to add.</param>265 /// <param name="local">If local, add cmdlet to current scope. Else, add to module scope.</param>266 internal void AddSessionStateEntry(SessionStateCmdletEntry entry, bool local)267 {268 ExecutionContext.CommandDiscovery.AddSessionStateCmdletEntryToCache(entry, local);269 }270 271 /// <summary>272 /// Add an new SessionState cmdlet entry to this session state object...273 /// </summary>274 /// <param name="entry">The entry to add.</param>275 internal void AddSessionStateEntry(SessionStateApplicationEntry entry)276 {277 this.Applications.Add(entry.Path);278 }279 280 /// <summary>281 /// Add an new SessionState cmdlet entry to this session state object...282 /// </summary>283 /// <param name="entry">The entry to add.</param>284 internal void AddSessionStateEntry(SessionStateScriptEntry entry)285 {286 this.Scripts.Add(entry.Path);287 }288 289 /// <summary>290 /// Add the variables that must always be present in a SessionState instance...291 /// </summary>292 internal void InitializeFixedVariables()293 {294 //295 // BUGBUG296 //297 // String resources for aliases are currently associated with Runspace init298 //299 300 // $Host301 PSVariable v = new PSVariable(302 SpecialVariables.Host,303 ExecutionContext.EngineHostInterface,304 ScopedItemOptions.Constant | ScopedItemOptions.AllScope,305 RunspaceInit.PSHostDescription);306 this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);307 308 // $HOME - indicate where a user's home directory is located in the file system.309 // -- %USERPROFILE% on windows310 // -- %HOME% on unix311 string home = Environment.GetEnvironmentVariable(Platform.CommonEnvVariableNames.Home) ?? string.Empty;312 v = new PSVariable(SpecialVariables.Home,313 home,314 ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope,315 RunspaceInit.HOMEDescription);316 this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);317 318 // $ExecutionContext319 v = new PSVariable(SpecialVariables.ExecutionContext,320 ExecutionContext.EngineIntrinsics,321 ScopedItemOptions.Constant | ScopedItemOptions.AllScope,322 RunspaceInit.ExecutionContextDescription);323 this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);324 325 // $PSVersionTable326 v = new PSVariable(SpecialVariables.PSVersionTable,327 PSVersionInfo.GetPSVersionTable(),328 ScopedItemOptions.Constant | ScopedItemOptions.AllScope,329 RunspaceInit.PSVersionTableDescription);330 this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);331 332 // $PSEdition333 v = new PSVariable(SpecialVariables.PSEdition,334 PSVersionInfo.PSEditionValue,335 ScopedItemOptions.Constant | ScopedItemOptions.AllScope,336 RunspaceInit.PSEditionDescription);337 this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);338 339 // $PID340 v = new PSVariable(341 SpecialVariables.PID,342 Environment.ProcessId,343 ScopedItemOptions.Constant | ScopedItemOptions.AllScope,344 RunspaceInit.PIDDescription);345 this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);346 347 // $PSCulture348 v = new PSCultureVariable();349 this.GlobalScope.SetVariableForce(v, this);350 351 // $PSUICulture352 v = new PSUICultureVariable();353 this.GlobalScope.SetVariableForce(v, this);354 355 // $?356 v = new QuestionMarkVariable(this.ExecutionContext);357 this.GlobalScope.SetVariableForce(v, this);358 359 // $ShellId - if there is no runspace config, use the default string360 string shellId = ExecutionContext.ShellID;361 v = new PSVariable(SpecialVariables.ShellId, shellId,362 ScopedItemOptions.Constant | ScopedItemOptions.AllScope,363 RunspaceInit.MshShellIdDescription);364 this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);365 366 // $PSHOME367 string applicationBase = Utils.DefaultPowerShellAppBase;368 v = new PSVariable(SpecialVariables.PSHome, applicationBase,369 ScopedItemOptions.Constant | ScopedItemOptions.AllScope,370 RunspaceInit.PSHOMEDescription);371 this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);372 373 // $EnabledExperimentalFeatures374 v = new PSVariable(SpecialVariables.EnabledExperimentalFeatures,375 ExperimentalFeature.EnabledExperimentalFeatureNames,376 ScopedItemOptions.Constant | ScopedItemOptions.AllScope,377 RunspaceInit.EnabledExperimentalFeatures);378 this.GlobalScope.SetVariable(v.Name, v, asValue: false, force: true, this, CommandOrigin.Internal, fastPath: true);379 }380 381 /// <summary>382 /// Check to see if an application is allowed to be run.383 /// </summary>384 /// <param name="applicationPath">The path to the application to check.</param>385 /// <returns>True if application is permitted.</returns>386 internal SessionStateEntryVisibility CheckApplicationVisibility(string applicationPath)387 {388 return checkPathVisibility(Applications, applicationPath);389 }390 391 private static SessionStateEntryVisibility checkPathVisibility(List<string> list, string path)392 {393 if (list == null || list.Count == 0)394 {395 return SessionStateEntryVisibility.Private;396 }397 398 if (string.IsNullOrEmpty(path))399 {400 return SessionStateEntryVisibility.Private;401 }402 403 if (list.Contains("*"))404 {405 return SessionStateEntryVisibility.Public;406 }407 408 foreach (string p in list)409 {410 if (string.Equals(p, path, StringComparison.OrdinalIgnoreCase))411 {412 return SessionStateEntryVisibility.Public;413 }414 415 if (WildcardPattern.ContainsWildcardCharacters(p))416 {417 WildcardPattern pattern = WildcardPattern.Get(p, WildcardOptions.IgnoreCase);418 if (pattern.IsMatch(path))419 {420 return SessionStateEntryVisibility.Public;421 }422 }423 }424 425 return SessionStateEntryVisibility.Private;426 }427 428 #endregion Private data429 430 /// <summary>431 /// Notification for SessionState to do cleanup432 /// before runspace is closed.433 /// </summary>434 internal void RunspaceClosingNotification()435 {436 if (this != ExecutionContext.TopLevelSessionState && Providers.Count > 0)437 {438 // Remove all providers at the top level...439 440 CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext);441 442 foreach (string providerName in Providers.Keys)443 {444 // All errors are ignored.445 446 RemoveProvider(providerName, true, context);447 }448 }449 }450 451 #region Errors452 453 /// <summary>454 /// Constructs a new instance of a ProviderInvocationException455 /// using the specified data.456 /// </summary>457 /// <param name="resourceId">458 /// The resource ID to use as the format message for the error.459 /// </param>460 /// <param name="resourceStr">461 /// This is the message template string.462 /// </param>463 /// <param name="provider">464 /// The provider information used when formatting the error message.465 /// </param>466 /// <param name="path">467 /// The path used when formatting the error message.468 /// </param>469 /// <param name="e">470 /// The exception that was thrown by the provider. This will be set as471 /// the ProviderInvocationException's InnerException and the message will472 /// be used when formatting the error message.473 /// </param>474 /// <returns>475 /// A new instance of a ProviderInvocationException.476 /// </returns>477 /// <exception cref="ProviderInvocationException">478 /// Wraps <paramref name="e"/> in a ProviderInvocationException479 /// and then throws it.480 /// </exception>481 internal ProviderInvocationException NewProviderInvocationException(482 string resourceId,483 string resourceStr,484 ProviderInfo provider,485 string path,486 Exception e)487 {488 return NewProviderInvocationException(resourceId, resourceStr, provider, path, e, true);489 }490 491 /// <summary>492 /// Constructs a new instance of a ProviderInvocationException493 /// using the specified data.494 /// </summary>495 /// <param name="resourceId">496 /// The resource ID to use as the format message for the error.497 /// </param>498 /// <param name="resourceStr">499 /// This is the message template string.500 /// </param>501 /// <param name="provider">502 /// The provider information used when formatting the error message.503 /// </param>504 /// <param name="path">505 /// The path used when formatting the error message.506 /// </param>507 /// <param name="e">508 /// The exception that was thrown by the provider. This will be set as509 /// the ProviderInvocationException's InnerException and the message will510 /// be used when formatting the error message.511 /// </param>512 /// <param name="useInnerExceptionErrorMessage">513 /// If true, the error record from the inner exception will be used if it contains one.514 /// If false, the error message specified by the resourceId will be used.515 /// </param>516 /// <returns>517 /// A new instance of a ProviderInvocationException.518 /// </returns>519 /// <exception cref="ProviderInvocationException">520 /// Wraps <paramref name="e"/> in a ProviderInvocationException521 /// and then throws it.522 /// </exception>523 internal ProviderInvocationException NewProviderInvocationException(524 string resourceId,525 string resourceStr,526 ProviderInfo provider,527 string path,528 Exception e,529 bool useInnerExceptionErrorMessage)530 {531 // If the innerException was itself thrown by532 // ProviderBase.ThrowTerminatingError, it is already a533 // ProviderInvocationException, and we don't want to534 // re-wrap it.535 ProviderInvocationException pie = e as ProviderInvocationException;536 if (pie != null)537 {538 pie._providerInfo = provider;539 return pie;540 }541 542 pie = new ProviderInvocationException(resourceId, resourceStr, provider, path, e, useInnerExceptionErrorMessage);543 544 // Log a provider health event545 546 MshLog.LogProviderHealthEvent(547 ExecutionContext,548 provider.Name,549 pie,550 Severity.Warning);551 552 return pie;553 }554 #endregion Errors555 }556}557 