Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes308downloads
NativeCommandProcessor.cs2669 linesDownload Raw Back to engine
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4#pragma warning disable 1634, 16915 6using System.Collections;7using System.Collections.Concurrent;8using System.Collections.Generic;9using System.ComponentModel;10using System.Diagnostics;11using System.Diagnostics.CodeAnalysis;12using System.Globalization;13using System.IO;14using System.Linq;15using System.Management.Automation.Internal;16using System.Runtime.CompilerServices;17using System.Runtime.Serialization;18using System.Text;19using System.Threading;20using System.Threading.Tasks;21using System.Xml;22using Microsoft.PowerShell.Commands;23using Microsoft.PowerShell.Telemetry;24using Microsoft.Win32;25using Dbg = System.Management.Automation.Diagnostics;26 27namespace System.Management.Automation28{29    /// <summary>30    /// Various types of input/output supported by native commands.31    /// </summary>32    /// <remarks>33    /// Most native commands only support text. Other formats34    /// are supported by minishell35    /// </remarks>36    internal enum NativeCommandIOFormat37    {38        Text,39        Xml40    }41 42    /// <summary>43    /// Different streams produced by minishell output.44    /// </summary>45    internal enum MinishellStream46    {47        Output,48        Error,49        Verbose,50        Warning,51        Debug,52        Progress,53        Information,54        Unknown55    }56 57    /// <summary>58    /// Helper class which holds stream names and also provide conversion59    /// method.60    /// </summary>61    internal static class StringToMinishellStreamConverter62    {63        internal const string OutputStream = "output";64        internal const string ErrorStream = "error";65        internal const string DebugStream = "debug";66        internal const string VerboseStream = "verbose";67        internal const string WarningStream = "warning";68        internal const string ProgressStream = "progress";69        internal const string InformationStream = "information";70 71        internal static MinishellStream ToMinishellStream(string stream)72        {73            Dbg.Assert(stream != null, "caller should validate the parameter");74 75            MinishellStream ms = MinishellStream.Unknown;76            if (OutputStream.Equals(stream, StringComparison.OrdinalIgnoreCase))77            {78                ms = MinishellStream.Output;79            }80            else if (ErrorStream.Equals(stream, StringComparison.OrdinalIgnoreCase))81            {82                ms = MinishellStream.Error;83            }84            else if (DebugStream.Equals(stream, StringComparison.OrdinalIgnoreCase))85            {86                ms = MinishellStream.Debug;87            }88            else if (VerboseStream.Equals(stream, StringComparison.OrdinalIgnoreCase))89            {90                ms = MinishellStream.Verbose;91            }92            else if (WarningStream.Equals(stream, StringComparison.OrdinalIgnoreCase))93            {94                ms = MinishellStream.Warning;95            }96            else if (ProgressStream.Equals(stream, StringComparison.OrdinalIgnoreCase))97            {98                ms = MinishellStream.Progress;99            }100            else if (InformationStream.Equals(stream, StringComparison.OrdinalIgnoreCase))101            {102                ms = MinishellStream.Information;103            }104 105            return ms;106        }107    }108 109    /// <summary>110    /// An output object from the child process.111    /// If it's from the error stream isError will be true.112    /// </summary>113    internal class ProcessOutputObject114    {115        /// <summary>116        /// Get the data from this object.117        /// </summary>118        /// <value>The data</value>119        internal object Data { get; }120 121        /// <summary>122        /// Stream to which data belongs.123        /// </summary>124        internal MinishellStream Stream { get; }125 126        /// <summary>127        /// Build an output object.128        /// </summary>129        /// <param name="data">The data to output.</param>130        /// <param name="stream">Stream to which data belongs.</param>131        internal ProcessOutputObject(object data, MinishellStream stream)132        {133            Data = data;134            Stream = stream;135        }136    }137 138#nullable enable139    /// <summary>140    /// This exception is used by the NativeCommandProcessor to indicate an error141    /// when a native command returns a non-zero exit code.142    /// </summary>143    public sealed class NativeCommandExitException : RuntimeException144    {145        // NOTE:146        // When implementing the native error action preference integration,147        // reusing ApplicationFailedException was rejected.148        // Instead of reusing a type already used in another scenario149        // it was decided instead to use a fresh type to avoid conflating the two scenarios:150        // * ApplicationFailedException: PowerShell was not able to complete execution of the application.151        // * NativeCommandExitException: the application completed execution but returned a non-zero exit code.152 153        #region Constructors154 155        /// <summary>156        /// Initializes a new instance of the <see cref="NativeCommandExitException"/> class with information on the native157        /// command, a specified error message and a specified error ID.158        /// </summary>159        /// <param name="path">The full path of the native command.</param>160        /// <param name="exitCode">The exit code returned by the native command.</param>161        /// <param name="processId">The process ID of the process before it ended.</param>162        /// <param name="message">The error message.</param>163        /// <param name="errorId">The PowerShell runtime error ID.</param>164        internal NativeCommandExitException(string path, int exitCode, int processId, string message, string errorId)165            : base(message)166        {167            SetErrorId(errorId);168            SetErrorCategory(ErrorCategory.NotSpecified);169            Path = path;170            ExitCode = exitCode;171            ProcessId = processId;172        }173 174        #endregion Constructors175 176        /// <summary>177        /// Gets the path of the native command.178        /// </summary>179        public string? Path { get; }180 181        /// <summary>182        /// Gets the exit code returned by the native command.183        /// </summary>184        public int ExitCode { get; }185 186        /// <summary>187        /// Gets the native command's process ID.188        /// </summary>189        public int ProcessId { get; }190 191    }192#nullable restore193 194    /// <summary>195    /// Provides way to create and execute native commands.196    /// </summary>197    internal class NativeCommandProcessor : CommandProcessorBase198    {199        /// <summary>200        /// This is the list of files which will trigger Legacy behavior if 'PSNativeCommandArgumentPassing' is set to "Windows".201        /// </summary>202        private static readonly HashSet<string> s_legacyFileExtensions = new(StringComparer.OrdinalIgnoreCase)203            {204                ".js",205                ".wsf",206                ".cmd",207                ".bat",208                ".vbs",209            };210 211        /// <summary>212        /// This is the list of native commands that have non-standard behavior with regard to argument passing.213        /// We use Legacy argument parsing for them when 'PSNativeCommandArgumentPassing' is set to "Windows".214        /// </summary>215        private static readonly HashSet<string> s_legacyCommands = new(StringComparer.OrdinalIgnoreCase)216            {217                "cmd",218                "cscript",219                "find",220                "sqlcmd",221                "wscript",222            };223 224#if !UNIX225        /// <summary>226        /// List of known package managers pulled from the registry.227        /// </summary>228        private static readonly HashSet<string> s_knownPackageManagers = GetPackageManagerListFromRegistry();229 230        /// <summary>231        /// Indicates whether the Path Update feature is enabled in a given session.232        /// PowerShell sessions could reuse the same thread, so we cannot cache the value with a thread static variable.233        /// </summary>234        private static readonly ConditionalWeakTable<ExecutionContext, string> s_pathUpdateFeatureEnabled = new();235 236        private readonly bool _isPackageManager;237        private string _originalUserEnvPath;238        private string _originalSystemEnvPath;239 240        /// <summary>241        /// Gets the known package managers from the registry.242        /// </summary>243        private static HashSet<string> GetPackageManagerListFromRegistry()244        {245            // We only account for the first 8 package managers. This is the same behavior as in CMD.246            const int MaxPackageManagerCount = 8;247            const string RegKeyPath = @"Software\Microsoft\Command Processor\KnownPackageManagers";248 249            string[] subKeyNames = null;250            HashSet<string> retSet = null;251 252            try253            {254                using RegistryKey key = Registry.LocalMachine.OpenSubKey(RegKeyPath);255                subKeyNames = key?.GetSubKeyNames();256            }257            catch258            {259                return null;260            }261 262            if (subKeyNames is { Length: > 0 })263            {264                IEnumerable<string> names = subKeyNames.Length <= MaxPackageManagerCount265                    ? subKeyNames266                    : subKeyNames.Take(MaxPackageManagerCount);267 268                retSet = new(names, StringComparer.OrdinalIgnoreCase);269            }270 271            return retSet;272        }273 274        /// <summary>275        /// Check if the given name is a known package manager from the registry list.276        /// </summary>277        private static bool IsKnownPackageManager(string name)278        {279            if (s_knownPackageManagers is null)280            {281                return false;282            }283 284            if (s_knownPackageManagers.Contains(name))285            {286                return true;287            }288 289            int lastDotIndex = name.LastIndexOf('.');290            if (lastDotIndex > 0)291            {292                string nameWithoutExt = name[..lastDotIndex];293                if (s_knownPackageManagers.Contains(nameWithoutExt))294                {295                    return true;296                }297            }298 299            return false;300        }301 302        /// <summary>303        /// Check if the Path Update feature is enabled for the given session.304        /// </summary>305        private static bool IsPathUpdateFeatureEnabled(ExecutionContext context)306        {307            // We check only once per session.308            if (s_pathUpdateFeatureEnabled.TryGetValue(context, out string value))309            {310                // The feature is enabled if the value is not null.311                return value is { };312            }313 314            // Disable Path Update if 'EnvironmentProvider' is disabled in the current session, or the current session is restricted.315            bool enabled = context.EngineSessionState.Providers.ContainsKey(EnvironmentProvider.ProviderName)316                && !Utils.IsSessionRestricted(context);317 318            // - Use the static empty string instance to indicate that the feature is enabled.319            // - Use the null value to indicate that the feature is disabled.320            s_pathUpdateFeatureEnabled.TryAdd(context, enabled ? string.Empty : null);321            return enabled;322        }323 324        /// <summary>325        /// Gets the added part of the new string compared to the old string.326        /// </summary>327        private static ReadOnlySpan<char> GetAddedPartOfString(string oldString, string newString)328        {329            if (oldString.Length >= newString.Length)330            {331                // Nothing added or something removed.332                return ReadOnlySpan<char>.Empty;333            }334 335            int index = newString.IndexOf(oldString);336            if (index is -1)337            {338                // The new and old strings are drastically different. Stop trying in this case.339                return ReadOnlySpan<char>.Empty;340            }341 342            if (index > 0)343            {344                // Found the old string at non-zero offset, so something was prepended to the old string.345                return newString.AsSpan(0, index);346            }347            else348            {349                // Found the old string at the beginning of the new string, so something was appended to the old string.350                return newString.AsSpan(oldString.Length);351            }352        }353 354        /// <summary>355        /// Update the process-scope environment variable Path based on the changes in the user-scope and system-scope Path.356        /// </summary>357        /// <param name="oldUserPath">The old value of the user-scope Path retrieved from registry.</param>358        /// <param name="oldSystemPath">The old value of the system-scope Path retrieved from registry.</param>359        private static void UpdateProcessEnvPath(string oldUserPath, string oldSystemPath)360        {361            string newUserEnvPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.User);362            string newSystemEnvPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);363            string procEnvPath = Environment.GetEnvironmentVariable("Path");364 365            ReadOnlySpan<char> userPathChange = GetAddedPartOfString(oldUserPath, newUserEnvPath).Trim(';');366            ReadOnlySpan<char> systemPathChange = GetAddedPartOfString(oldSystemPath, newSystemEnvPath).Trim(';');367 368            // Add 2 to account for the path separators we may need to add.369            int maxLength = procEnvPath.Length + userPathChange.Length + systemPathChange.Length + 2;370            StringBuilder newPath = null;371 372            if (userPathChange.Length > 0)373            {374                CreateNewProcEnvPath(userPathChange);375            }376 377            if (systemPathChange.Length > 0)378            {379                CreateNewProcEnvPath(systemPathChange);380            }381 382            if (newPath is { Length: > 0 })383            {384                // Update the process env Path.385                Environment.SetEnvironmentVariable("Path", newPath.ToString());386            }387 388            // Helper method to create a new env Path string.389            void CreateNewProcEnvPath(ReadOnlySpan<char> newChange)390            {391                newPath ??= new StringBuilder(procEnvPath, capacity: maxLength);392 393                if (newPath.Length is 0 || newPath[^1] is ';')394                {395                    newPath.Append(newChange);396                }397                else398                {399                    newPath.Append(';').Append(newChange);400                }401            }402        }403#endif404 405        #region ctor/native command properties406 407        /// <summary>408        /// Information about application which is invoked by this instance of409        /// NativeCommandProcessor.410        /// </summary>411        private readonly ApplicationInfo _applicationInfo;412 413        /// <summary>414        /// Initializes the new instance of NativeCommandProcessor class.415        /// </summary>416        /// <param name="applicationInfo">417        /// The information about the application to run.418        /// </param>419        /// <param name="context">420        /// The execution context for this command.421        /// </param>422        /// <exception cref="ArgumentNullException">423        /// <paramref name="applicationInfo"/> or <paramref name="context"/> is null424        /// </exception>425        internal NativeCommandProcessor(ApplicationInfo applicationInfo, ExecutionContext context)426            : base(applicationInfo)427        {428            if (applicationInfo == null)429            {430                throw PSTraceSource.NewArgumentNullException(nameof(applicationInfo));431            }432 433            _applicationInfo = applicationInfo;434            this._context = context;435 436            this.Command = new NativeCommand();437            this.Command.CommandInfo = applicationInfo;438            this.Command.Context = context;439            this.Command.commandRuntime = this.commandRuntime = new MshCommandRuntime(context, applicationInfo, this.Command);440 441            this.CommandScope = context.EngineSessionState.CurrentScope;442 443            // provide native command a backpointer to this object.444            // When kill is called on the command object,445            // it calls this NCP back to kill the process...446            ((NativeCommand)Command).MyCommandProcessor = this;447 448            // Create input writer for providing input to the process.449            _inputWriter = new ProcessInputWriter(Command);450 451            _isTranscribing = context.EngineHostInterface.UI.IsTranscribing;452 453#if !UNIX454            _isPackageManager = IsKnownPackageManager(_applicationInfo.Name) && IsPathUpdateFeatureEnabled(context);455#endif456        }457 458        /// <summary>459        /// Gets the NativeCommand associated with this command processor.460        /// </summary>461        private NativeCommand nativeCommand462        {463            get464            {465                NativeCommand command = this.Command as NativeCommand;466                Diagnostics.Assert(command != null, "this.Command is created in the constructor.");467                return command;468            }469        }470 471        /// <summary>472        /// Gets or sets the name of the native command.473        /// </summary>474        private string NativeCommandName475        {476            get477            {478                string name = _applicationInfo.Name;479                return name;480            }481        }482 483        /// <summary>484        /// Gets or sets path to the native command.485        /// </summary>486        private string Path487        {488            get489            {490                string path = _applicationInfo.Path;491                return path;492            }493        }494 495        internal NativeCommandProcessor DownStreamNativeCommand { get; set; }496 497        internal bool UpstreamIsNativeCommand { get; set; }498 499        internal BytePipe StdOutDestination { get; set; }500 501        #endregion ctor/native command properties502 503        #region parameter binder504 505        /// <summary>506        /// Parameter binder used by this command processor.507        /// </summary>508        private NativeCommandParameterBinderController _nativeParameterBinderController;509 510        /// <summary>511        /// Gets a new instance of a ParameterBinderController using a NativeCommandParameterBinder.512        /// </summary>513        /// <param name="command">514        /// The native command to be run.515        /// </param>516        /// <returns>517        /// A new parameter binder controller for the specified command.518        /// </returns>519        internal ParameterBinderController NewParameterBinderController(InternalCommand command)520        {521            if (_isMiniShell)522            {523                _nativeParameterBinderController =524                    new MinishellParameterBinderController(525                        this.nativeCommand);526            }527            else528            {529                _nativeParameterBinderController =530                    new NativeCommandParameterBinderController(531                        this.nativeCommand);532            }533 534            return _nativeParameterBinderController;535        }536 537        internal NativeCommandParameterBinderController NativeParameterBinderController538        {539            get540            {541                if (_nativeParameterBinderController == null)542                {543                    NewParameterBinderController(this.Command);544                }545 546                return _nativeParameterBinderController;547            }548        }549 550        #endregion parameter binder551 552        #region internal overrides553 554        /// <summary>555        /// Prepares the command for execution with the specified CommandParameterInternal.556        /// </summary>557        internal override void Prepare(IDictionary psDefaultParameterValues)558        {559            // Check if the application is minishell560            _isMiniShell = IsMiniShell();561 562            // For minishell parameter binding is done in Complete method because we need563            // to know if output is redirected before we can bind parameters.564            if (!_isMiniShell)565            {566                this.NativeParameterBinderController.BindParameters(arguments);567            }568 569            try570            {571                InitNativeProcess();572            }573            catch (Exception)574            {575                // Do cleanup in case of exception576                CleanUp(killBackgroundProcess: true);577                throw;578            }579        }580 581        /// <summary>582        /// Executes the command. This method assumes that Prepare is already called.583        /// </summary>584        internal override void ProcessRecord()585        {586            try587            {588                // If upstream is a native command it'll be writing directly to our stdin stream589                // so we can skip reading here.590                if (!UpstreamIsNativeCommand)591                {592                    while (Read())593                    {594                        _inputWriter.Add(Command.CurrentPipelineObject);595                    }596                }597 598                ConsumeAvailableNativeProcessOutput(blocking: false);599            }600            catch (Exception)601            {602                // Do cleanup in case of exception603                CleanUp(killBackgroundProcess: true);604                throw;605            }606        }607 608        /// <summary>609        /// Process object for the invoked application.610        /// </summary>611        private Process _nativeProcess;612 613        /// <summary>614        /// This is used for writing input to the process.615        /// </summary>616        private readonly ProcessInputWriter _inputWriter = null;617 618        /// <summary>619        /// Is true if this command is to be run "standalone" - that is, with620        /// no redirection.621        /// </summary>622        private bool _runStandAlone;623 624        /// <summary>625        /// Indicate whether we need to consider redirecting the output/error of the current native command.626        /// Usually a windows program which is the last command in a pipeline can be executed as 'background' -- we don't need to capture its output/error streams.627        /// </summary>628        private bool _isRunningInBackground;629 630        /// <summary>631        /// Indicate if we have called 'NotifyBeginApplication()' on the host, so that632        /// we can call the counterpart 'NotifyEndApplication' as appropriate.633        /// </summary>634        private bool _hasNotifiedBeginApplication;635 636        /// <summary>637        /// This output queue helps us keep the output and error (if redirected) order correct.638        /// We could do a blocking read in the Complete block instead,639        /// but then we would not be able to restore the order reasonable.640        /// </summary>641        private BlockingCollection<ProcessOutputObject> _nativeProcessOutputQueue;642 643        private static bool? s_supportScreenScrape = null;644        private readonly bool _isTranscribing;645        private Host.Coordinates _startPosition;646 647        /// <summary>648        /// Object used for synchronization between StopProcessing thread and649        /// Pipeline thread.650        /// </summary>651        private readonly object _sync = new object();652 653        private SemaphoreSlim _processInitialized;654 655        internal async Task WaitForProcessInitializationAsync(CancellationToken cancellationToken)656        {657            SemaphoreSlim processInitialized = _processInitialized;658            if (processInitialized is null)659            {660                lock (_sync)661                {662                    processInitialized = _processInitialized ??= new SemaphoreSlim(0, 1);663                }664            }665 666            try667            {668                await processInitialized.WaitAsync(cancellationToken);669            }670            finally671            {672                processInitialized.Release();673            }674        }675 676        /// <summary>677        /// Creates a pipe representing the streaming of unprocessed bytes.678        /// </summary>679        /// <param name="stdout">680        /// The stream that the pipe should represent. <see langword="true" />681        /// for stdout, <see langword="false" /> for stdin.682        /// </param>683        /// <returns>A new byte pipe representing the specified stream.</returns>684        internal BytePipe CreateBytePipe(bool stdout) => new NativeCommandProcessorBytePipe(this, stdout);685 686        /// <summary>687        /// Gets the specified base <see cref="Stream" /> for the underlying688        /// <see cref="Process" />.689        /// </summary>690        /// <param name="stdout">691        /// The stream that should be retrieved. <see langword="true" /> for692        /// stdout, <see langword="false" /> for stdin.693        /// </param>694        /// <returns>The specified <see cref="Stream" />.</returns>695        internal Stream GetStream(bool stdout)696        {697            Debug.Assert(698                _nativeProcess is not null,699                "Caller should verify that initialization has completed before attempting to get the underlying stream.");700 701            return stdout702                ? _nativeProcess.StandardOutput.BaseStream703                : _nativeProcess.StandardInput.BaseStream;704        }705 706        /// <summary>707        /// Executes the native command once all of the input has been gathered.708        /// </summary>709        /// <exception cref="PipelineStoppedException">710        /// The pipeline is stopping711        /// </exception>712        /// <exception cref="ApplicationFailedException">713        /// The native command could not be run714        /// </exception>715        private void InitNativeProcess()716        {717            // Figure out if we're going to run this process "standalone" i.e. without718            // redirecting anything. This is a bit tricky as we always run redirected so719            // we have to see if the redirection is actually being done at the topmost level or not.720 721            // Calculate if input and output are redirected.722            bool redirectOutput;723            bool redirectError;724            bool redirectInput;725 726            _startPosition = new Host.Coordinates();727 728            bool isWindowsApplication = IsWindowsApplication(this.Path);729            CalculateIORedirection(isWindowsApplication, out redirectOutput, out redirectError, out redirectInput);730 731            // Find out if it's the only command in the pipeline.732            bool soloCommand = this.Command.MyInvocation.PipelineLength == 1;733 734            // Get the start info for the process.735            ProcessStartInfo startInfo = GetProcessStartInfo(redirectOutput, redirectError, redirectInput, soloCommand);736 737            // Send Telemetry indicating what argument passing mode we are in.738            ApplicationInsightsTelemetry.SendExperimentalUseData(739                "PSWindowsNativeCommandArgPassing",740                NativeParameterBinderController.ArgumentPassingStyle.ToString());741 742#if !UNIX743            string commandPath = this.Path.ToLowerInvariant();744            if (commandPath.EndsWith("powershell.exe") || commandPath.EndsWith("powershell_ise.exe"))745            {746                // if starting Windows PowerShell, need to remove PowerShell specific segments of PSModulePath747                string psmodulepath = ModuleIntrinsics.GetWindowsPowerShellModulePath();748                startInfo.Environment["PSModulePath"] = psmodulepath;749 750                // must set UseShellExecute to false if we modify the environment block751                startInfo.UseShellExecute = false;752            }753 754            if (_isPackageManager)755            {756                _originalUserEnvPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.User);757                _originalSystemEnvPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);758            }759#endif760 761            if (this.Command.Context.CurrentPipelineStopping)762            {763                throw new PipelineStoppedException();764            }765 766            Exception exceptionToRethrow = null;767            try768            {769                // Before start the executable, tell the host, which might want to save off the770                // window title or other such state as might be tweaked by the native process.771                Command.Context.EngineHostInterface.NotifyBeginApplication();772                _hasNotifiedBeginApplication = true;773 774                if (_runStandAlone)775                {776                    // Store the Raw UI coordinates so that we can scrape the screen after777                    // if we are transcribing.778                    if (_isTranscribing && (s_supportScreenScrape == true))779                    {780                        _startPosition = this.Command.Context.EngineHostInterface.UI.RawUI.CursorPosition;781                        _startPosition.X = 0;782                    }783                }784 785                // Start the process. If stop has been called, throw exception.786                // Note: if StopProcessing is called which this method has the lock,787                // Stop thread will wait for nativeProcess to start.788                // If StopProcessing gets the lock first, then it will set the stopped789                // flag and this method will throw PipelineStoppedException when it gets790                // the lock.791                lock (_sync)792                {793                    if (_stopped)794                    {795                        throw new PipelineStoppedException();796                    }797 798                    try799                    {800                        _nativeProcess = new Process() { StartInfo = startInfo };801                        _nativeProcess.Start();802                    }803                    catch (Win32Exception)804                    {805#if UNIX806                        // On Unix platforms, nothing can be further done, so just throw.807                        throw;808#else809                        // On headless Windows SKUs, there is no shell to fall back to, so just throw810                        if (!Platform.IsWindowsDesktop)811                        {812                            throw;813                        }814 815                        // on Windows desktops, see if there is a file association for this command. If so then we'll use that.816                        string executable = Interop.Windows.FindExecutable(startInfo.FileName);817                        bool notDone = true;818                        // check to see what mode we should be in for argument passing819                        if (!string.IsNullOrEmpty(executable))820                        {821                            isWindowsApplication = IsWindowsApplication(executable);822                            if (!isWindowsApplication)823                            {824                                // Allocate a console if there isn't one attached already...825                                ConsoleVisibility.AllocateHiddenConsole();826                            }827 828                            string oldArguments = startInfo.Arguments;829                            string oldFileName = startInfo.FileName;830                            // Check to see whether this executable should be using Legacy mode argument parsing831                            bool useSpecialArgumentPassing = UseSpecialArgumentPassing(oldFileName);832                            if (useSpecialArgumentPassing)833                            {834                                // codeql[cs/microsoft/command-line-injection] - This is expected PowerShell behavior where user inputted paths are supported for the context of this method and the path portion of the argument is escaped. The user assumes trust for the file path specified on the user's system to start process for, and in the case of remoting, restricted remoting security guidelines should be used.835                                startInfo.Arguments = "\"" + oldFileName + "\" " + startInfo.Arguments;836                            }837                            else838                            {839                                startInfo.ArgumentList.Insert(0, oldFileName);840                            }841                            startInfo.FileName = executable;842                            try843                            {844                                _nativeProcess.Start();845                                notDone = false;846                            }847                            catch (Win32Exception)848                            {849                                // Restore the old filename and arguments to try shell execute last...850                                if (useSpecialArgumentPassing)851                                {852                                    startInfo.Arguments = oldArguments;853                                }854                                else855                                {856                                    startInfo.ArgumentList.RemoveAt(0);857                                }858 859                                // codeql[cs/microsoft/command-line-injection-shell-execution] - This is expected PowerShell behavior where user inputted paths are supported for the context of this method. The user assumes trust for the file path specified on the user's system to retrieve process info for, and in the case of remoting, restricted remoting security guidelines should be used.860                                startInfo.FileName = oldFileName;861                            }862                        }863                        // We got here because there was either no executable found for this864                        // file or we tried to launch the exe and it failed. In either case865                        // we will try launching one last time using ShellExecute...866                        if (notDone)867                        {868                            if (soloCommand && !startInfo.UseShellExecute)869                            {870                                startInfo.UseShellExecute = true;871                                startInfo.RedirectStandardInput = false;872                                startInfo.RedirectStandardOutput = false;873                                startInfo.RedirectStandardError = false;874                                _nativeProcess.Start();875                            }876                            else877                            {878                                throw;879                            }880                        }881#endif882                    }883 884                    if (UpstreamIsNativeCommand)885                    {886                        _processInitialized ??= new SemaphoreSlim(0, 1);887                        _processInitialized.Release();888                    }889                }890 891                if (this.Command.MyInvocation.PipelinePosition < this.Command.MyInvocation.PipelineLength)892                {893                    // Never background unless you're at the end of a pipe.894                    // Something like895                    //    ls | notepad | sort.exe896                    // should block until the notepad process is terminated.897                    _isRunningInBackground = false;898                }899                else900                {901                    _isRunningInBackground = true;902                    if (!startInfo.UseShellExecute)903                    {904                        _isRunningInBackground = isWindowsApplication;905                    }906                }907 908                try909                {910                    // If input is redirected, start input to process.911                    if (startInfo.RedirectStandardInput)912                    {913                        NativeCommandIOFormat inputFormat = NativeCommandIOFormat.Text;914                        if (_isMiniShell)915                        {916                            inputFormat = ((MinishellParameterBinderController)NativeParameterBinderController).InputFormat;917                        }918 919                        lock (_sync)920                        {921                            if (!_stopped && !UpstreamIsNativeCommand)922                            {923                                _inputWriter.Start(_nativeProcess, inputFormat);924                            }925                        }926                    }927                }928                catch (Exception)929                {930                    StopProcessing();931                    throw;932                }933 934                if (!_isRunningInBackground)935                {936                    InitOutputQueue();937                }938            }939            catch (Win32Exception e)940            {941                exceptionToRethrow = e;942            }943            catch (PipelineStoppedException)944            {945                // If we're stopping the process, just rethrow this exception...946                throw;947            }948            catch (Exception e)949            {950                exceptionToRethrow = e;951            }952 953            // An exception was thrown while attempting to run the program954            // so wrap and rethrow it here...955            if (exceptionToRethrow != null)956            {957                // It's a system exception so wrap it in one of ours and re-throw.958                string message = StringUtil.Format(ParserStrings.ProgramFailedToExecute,959                    this.NativeCommandName, exceptionToRethrow.Message,960                    this.Command.MyInvocation.PositionMessage);961                ApplicationFailedException appFailedException = new ApplicationFailedException(message, exceptionToRethrow);962 963                // There is no need to set this exception here since this exception will eventually be caught by pipeline processor.964                // this.commandRuntime.PipelineProcessor.ExecutionFailed = true;965 966                throw appFailedException;967            }968        }969 970        private AsyncByteStreamTransfer _stdOutByteTransfer;971 972        private void InitOutputQueue()973        {974            // if output is redirected, start reading output of process in queue.975            if (_nativeProcess.StartInfo.RedirectStandardOutput || _nativeProcess.StartInfo.RedirectStandardError)976            {977                lock (_sync)978                {979                    if (!_stopped)980                    {981                        if (CommandRuntime.ErrorMergeTo is MshCommandRuntime.MergeDataStream.Output)982                        {983                            StdOutDestination = null;984                            if (DownStreamNativeCommand is not null)985                            {986                                DownStreamNativeCommand.UpstreamIsNativeCommand = false;987                                DownStreamNativeCommand = null;988                            }989                        }990 991                        _nativeProcessOutputQueue = new BlockingCollection<ProcessOutputObject>();992                        // we don't assign the handler to anything, because it's used only for objects marshaling993                        BytePipe stdOutDestination = StdOutDestination ?? DownStreamNativeCommand?.CreateBytePipe(stdout: false);994 995                        BytePipe stdOutSource = null;996                        if (stdOutDestination is not null)997                        {998                            stdOutSource = CreateBytePipe(stdout: true);999                        }1000 1001                        _ = new ProcessOutputHandler(1002                            _nativeProcess,1003                            _nativeProcessOutputQueue,1004                            stdOutDestination,1005                            stdOutSource,1006                            out _stdOutByteTransfer);1007                    }1008                }1009            }1010        }1011 1012        private ProcessOutputObject DequeueProcessOutput(bool blocking)1013        {1014            if (blocking)1015            {1016                // If adding was completed and collection is empty (IsCompleted == true)1017                // there is no need to do a blocking Take(), we should just return.1018                if (!_nativeProcessOutputQueue.IsCompleted)1019                {1020                    try1021                    {1022                        // If adding is not complete we need a try {} catch {}1023                        // to mitigate a concurrent call to CompleteAdding().1024                        return _nativeProcessOutputQueue.Take();1025                    }1026                    catch (InvalidOperationException)1027                    {1028                        // It's a normal situation: another thread can mark collection as CompleteAdding1029                        // in a concurrent way and we will rise an exception in Take().1030                        // Although it's a normal situation it's not the most common path1031                        // and will be executed only on the race condtion case.1032                    }1033                }1034 1035                return null;1036            }1037 1038            _nativeProcessOutputQueue.TryTake(out ProcessOutputObject record);1039            return record;1040        }1041 1042        /// <summary>1043        /// Read the output from the native process and send it down the line.1044        /// </summary>1045        private void ConsumeAvailableNativeProcessOutput(bool blocking)1046        {1047            if (_isRunningInBackground)1048            {1049                return;1050            }1051 1052            bool stdOutRedirected = _nativeProcess.StartInfo.RedirectStandardOutput;1053            bool stdErrRedirected = _nativeProcess.StartInfo.RedirectStandardError;1054            if (stdOutRedirected && _stdOutByteTransfer is not null)1055            {1056                if (blocking)1057                {1058                    _stdOutByteTransfer.EOF.GetAwaiter().GetResult();1059                }1060 1061                if (!stdErrRedirected)1062                {1063                    return;1064                }1065            }1066 1067            if (stdOutRedirected || stdErrRedirected)1068            {1069                ProcessOutputObject record;1070                while ((record = DequeueProcessOutput(blocking)) != null)1071                {1072                    if (this.Command.Context.CurrentPipelineStopping)1073                    {1074                        this.StopProcessing();1075                        return;1076                    }1077 1078                    ProcessOutputRecord(record);1079                }1080            }1081        }1082 1083        internal override void Complete()1084        {1085            Exception exceptionToRethrow = null;1086            try1087            {1088                if (!_isRunningInBackground)1089                {1090                    // Wait for input writer to finish.1091                    if (!UpstreamIsNativeCommand || _nativeProcess.StartInfo.RedirectStandardError)1092                    {1093                        _inputWriter.Done();1094                    }1095 1096                    // read all the available output in the blocking way1097                    ConsumeAvailableNativeProcessOutput(blocking: true);1098                    _nativeProcess.WaitForExit();1099 1100#if !UNIX1101                    if (_isPackageManager)1102                    {1103                        UpdateProcessEnvPath(_originalUserEnvPath, _originalSystemEnvPath);1104                    }1105#endif1106 1107                    // Capture screen output if we are transcribing and running stand alone1108                    if (_isTranscribing && (s_supportScreenScrape == true) && _runStandAlone)1109                    {1110                        Host.Coordinates endPosition = this.Command.Context.EngineHostInterface.UI.RawUI.CursorPosition;1111                        endPosition.X = this.Command.Context.EngineHostInterface.UI.RawUI.BufferSize.Width - 1;1112 1113                        // If the end position is before the start position, then capture the entire buffer.1114                        if (endPosition.Y < _startPosition.Y)1115                        {1116                            _startPosition.Y = 0;1117                        }1118 1119                        Host.BufferCell[,] bufferContents = this.Command.Context.EngineHostInterface.UI.RawUI.GetBufferContents(1120                            new Host.Rectangle(_startPosition, endPosition));1121 1122                        StringBuilder lineContents = new StringBuilder();1123                        StringBuilder bufferText = new StringBuilder();1124 1125                        for (int row = 0; row < bufferContents.GetLength(0); row++)1126                        {1127                            if (row > 0)1128                            {1129                                bufferText.Append(Environment.NewLine);1130                            }1131 1132                            lineContents.Clear();1133                            for (int column = 0; column < bufferContents.GetLength(1); column++)1134                            {1135                                lineContents.Append(bufferContents[row, column].Character);1136                            }1137 1138                            bufferText.Append(lineContents.ToString().TrimEnd(Utils.Separators.SpaceOrTab));1139                        }1140 1141                        this.Command.Context.InternalHost.UI.TranscribeResult(bufferText.ToString());1142                    }1143 1144                    this.Command.Context.SetVariable(SpecialVariables.LastExitCodeVarPath, _nativeProcess.ExitCode);1145                    if (_nativeProcess.ExitCode == 0)1146                    {1147                        return;1148                    }1149 1150                    this.commandRuntime.PipelineProcessor.ExecutionFailed = true;1151 1152                    // We send telemetry information only if the feature is enabled.1153                    // This shouldn't be done once, because it's a run-time check we should send telemetry every time.1154                    // Report on the following conditions:1155                    // - The variable is not present1156                    // - The value is not set (variable is null)1157                    // - The value is set to true or false1158                    bool useDefaultSetting;1159                    bool nativeErrorActionPreferenceSetting = Command.Context.GetBooleanPreference(1160                        SpecialVariables.PSNativeCommandUseErrorActionPreferenceVarPath,1161                        defaultPref: false,1162                        out useDefaultSetting);1163 1164                    // The variable is unset1165                    if (useDefaultSetting)1166                    {1167                        ApplicationInsightsTelemetry.SendExperimentalUseData("PSNativeCommandErrorActionPreference", "unset");1168                        return;1169                    }1170 1171                    // Send the value that was set.1172                    ApplicationInsightsTelemetry.SendExperimentalUseData("PSNativeCommandErrorActionPreference", nativeErrorActionPreferenceSetting.ToString());1173 1174                    // if it was explicitly set to false, return1175                    if (!nativeErrorActionPreferenceSetting)1176                    {1177                        return;1178                    }1179 1180                    const string errorId = nameof(CommandBaseStrings.ProgramExitedWithNonZeroCode);1181#if UNIX1182                    string hexFormatStr = "0x{0:X2}";1183#else1184                    string hexFormatStr = "0x{0:X8}";1185#endif1186 1187                    string errorMsg = StringUtil.Format(1188                        CommandBaseStrings.ProgramExitedWithNonZeroCode,1189                        NativeCommandName,1190                        _nativeProcess.ExitCode,1191                        string.Format(CultureInfo.InvariantCulture, hexFormatStr, _nativeProcess.ExitCode));1192 1193                    var exception = new NativeCommandExitException(1194                        Path,1195                        _nativeProcess.ExitCode,1196                        _nativeProcess.Id,1197                        errorMsg,1198                        errorId);1199 1200                    var errorRecord = new ErrorRecord(exception, errorId, ErrorCategory.NotSpecified, targetObject: Path);

Showing the first 1,200 of 2669 lines. Download the file for the rest.