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();
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; } using var bw = new MyBinaryWriter(InitialBufferCapacity); var stateHolder = UnixChildProcessState.Create(); try { bw.Write(0U); // Dummy length to be rewritten later 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 (environmentVariables == null) { bw.Write(0U); } else { bw.Write((uint)environmentVariables.Count); foreach (var(name, value) in environmentVariables) { bw.WriteEnvironmentVariable(name, value); } } bw.RewritePrefixedLength(); var subchannel = _helperProcess.RentSubchannelAsync(default).AsTask().GetAwaiter().GetResult();