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())); }