Пример #1
0
        public IChildProcessStateHolder SpawnProcess(
            ref ChildProcessStartInfoInternal startInfo,
            string resolvedPath,
            SafeHandle stdIn,
            SafeHandle stdOut,
            SafeHandle stdErr)
        {
            var arguments            = startInfo.Arguments;
            var environmentVariables = startInfo.EnvironmentVariables;
            var workingDirectory     = startInfo.WorkingDirectory;

            Span <int> fds         = stackalloc int[3];
            int        handleCount = 0;
            uint       flags       = 0;

            if (stdIn != null)
            {
                fds[handleCount++] = stdIn.DangerousGetHandle().ToInt32();
                flags |= RequestFlagsRedirectStdin;
            }
            if (stdOut != null)
            {
                fds[handleCount++] = stdOut.DangerousGetHandle().ToInt32();
                flags |= RequestFlagsRedirectStdout;
            }
            if (stdErr != null)
            {
                fds[handleCount++] = stdErr.DangerousGetHandle().ToInt32();
                flags |= RequestFlagsRedirectStderr;
            }

            if (startInfo.CreateNewConsole)
            {
                flags |= RequestFlagsCreateNewProcessGroup;
            }
            else
            {
                Debug.Assert(!startInfo.AllowSignal);
            }

            // If AttachToCurrentConsole (== !startInfo.AllowSignal), leave the process running after we (the parent) exit.
            // After being orphaned (and possibly reparented to the shell), it may continue running or may be terminated by SIGTTIN/SIGTTOU.
            if (startInfo.AllowSignal)
            {
                flags |= RequestFlagsEnableAutoTermination;
            }

            using var bw = new MyBinaryWriter(InitialBufferCapacity);
            var stateHolder = UnixChildProcessState.Create(this, startInfo.AllowSignal);

            try
            {
                bw.Write(stateHolder.State.Token);
                bw.Write(flags);
                bw.Write(workingDirectory);
                bw.Write(resolvedPath);

                bw.Write((uint)(arguments.Count + 1));
                bw.Write(resolvedPath);
                foreach (var x in arguments)
                {
                    bw.Write(x);
                }

                if (!startInfo.UseCustomEnvironmentVariables)
                {
                    // Send the environment variables of this process to the helper process.
                    //
                    // NOTE: We cannot cache or detect updates to the environment block; only the runtime can.
                    //       Concurrently invoking getenv and setenv is a racy operation; therefore the runtime
                    //       employs a process-global lock.
                    //
                    //       Fortunately, the caller can take a snapshot of environment variables theirselves.
                    var processEnvVars = Environment.GetEnvironmentVariables();
                    var envVarCount    = processEnvVars.Count;
                    bw.Write((uint)envVarCount);

                    var sortedEnvVars = ArrayPool <KeyValuePair <string, string> > .Shared.Rent(envVarCount);

                    try
                    {
                        EnvironmentVariableListUtil.ToSortedKeyValuePairs(processEnvVars, sortedEnvVars);

                        foreach (var(name, value) in sortedEnvVars.AsSpan <KeyValuePair <string, string> >().Slice(0, envVarCount))
                        {
                            bw.WriteEnvironmentVariable(name, value);
                        }
                    }
                    finally
                    {
                        ArrayPool <KeyValuePair <string, string> > .Shared.Return(sortedEnvVars);
                    }
                }
                else
                {
                    bw.Write((uint)environmentVariables.Length);
                    foreach (var(name, value) in environmentVariables.Span)
                    {
                        bw.WriteEnvironmentVariable(name, value);
                    }
                }

                // Work around https://github.com/microsoft/WSL/issues/6490
                // On WSL 1, if you call recvmsg multiple times to fully receive data sent with sendmsg,
                // the fds will be duplicated for each recvmsg call.
                // Send only fixed length of of data with the fds and receive that much data with one recvmsg call.
                // That will be safer anyway.
                Span <byte> header = stackalloc byte[sizeof(uint) * 2];
                if (!BitConverter.TryWriteBytes(header, (uint)UnixHelperProcessCommand.SpawnProcess) ||
                    !BitConverter.TryWriteBytes(header.Slice(sizeof(uint)), bw.Length))
                {
                    Debug.Fail("Should never fail.");
                }

                var subchannel = _helperProcess.RentSubchannelAsync(default).AsTask().GetAwaiter().GetResult();
        /// <summary>
        /// Initializes a new instance of the <see cref="ChildProcessCreationContext"/> class
        /// with the environment variables of the current process.
        /// </summary>
        /// <remarks>
        /// (Windows-specific) Duplicates will be removed.
        /// This method performs <see cref="StringComparison.Ordinal"/> comparisons and picks the smallest one (uppercase one).
        /// Some broken programs (for example, Cygwin) add names that differ only in cases to the environment block.
        /// </remarks>
        /// <returns>Created instance of the <see cref="ChildProcessCreationContext"/> class.</returns>
        public static ChildProcessCreationContext FromProcessEnvironment()
        {
            var processEnvVars = EnvironmentVariableListUtil.ToSortedDistinctKeyValuePairs(Environment.GetEnvironmentVariables());

            return(new ChildProcessCreationContext(processEnvVars.AsMemory()));
        }