mirror of
https://github.com/dart-lang/sdk
synced 2024-11-05 18:22:09 +00:00
[dart:io] Adds ProcessStartMode.INHERIT_STDIO
Adds a ProcessStartMode in which the child process inherits the stdio handles from the parent. Change-Id: Ibe7b8ae08caccaed827ae0a911a3cced7803cb6b Reviewed-on: https://dart-review.googlesource.com/36362 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Zach Anderson <zra@google.com>
This commit is contained in:
parent
5de9c20fb0
commit
6523896c6f
10 changed files with 223 additions and 105 deletions
|
@ -71,6 +71,14 @@ static char** ExtractCStringList(Dart_Handle strings,
|
|||
return string_args;
|
||||
}
|
||||
|
||||
bool Process::ModeIsAttached(ProcessStartMode mode) {
|
||||
return (mode == kNormal) || (mode == kInheritStdio);
|
||||
}
|
||||
|
||||
bool Process::ModeHasStdio(ProcessStartMode mode) {
|
||||
return (mode == kNormal) || (mode == kDetachedWithStdio);
|
||||
}
|
||||
|
||||
void Process::ClearAllSignalHandlers() {
|
||||
for (intptr_t i = 1; i <= kLastSignal; i++) {
|
||||
ClearSignalHandler(i, ILLEGAL_PORT);
|
||||
|
@ -145,7 +153,7 @@ void FUNCTION_NAME(Process_Start)(Dart_NativeArguments args) {
|
|||
}
|
||||
}
|
||||
int64_t mode =
|
||||
DartUtils::GetInt64ValueCheckRange(Dart_GetNativeArgument(args, 6), 0, 2);
|
||||
DartUtils::GetInt64ValueCheckRange(Dart_GetNativeArgument(args, 6), 0, 3);
|
||||
Dart_Handle stdin_handle = Dart_GetNativeArgument(args, 7);
|
||||
Dart_Handle stdout_handle = Dart_GetNativeArgument(args, 8);
|
||||
Dart_Handle stderr_handle = Dart_GetNativeArgument(args, 9);
|
||||
|
@ -159,7 +167,7 @@ void FUNCTION_NAME(Process_Start)(Dart_NativeArguments args) {
|
|||
static_cast<ProcessStartMode>(mode), &process_stdout, &process_stdin,
|
||||
&process_stderr, &pid, &exit_event, &os_error_message);
|
||||
if (error_code == 0) {
|
||||
if (mode != kDetached) {
|
||||
if (Process::ModeHasStdio(static_cast<ProcessStartMode>(mode))) {
|
||||
Socket::SetSocketIdNativeField(stdin_handle, process_stdin,
|
||||
Socket::kFinalizerNormal);
|
||||
Socket::SetSocketIdNativeField(stdout_handle, process_stdout,
|
||||
|
@ -167,7 +175,7 @@ void FUNCTION_NAME(Process_Start)(Dart_NativeArguments args) {
|
|||
Socket::SetSocketIdNativeField(stderr_handle, process_stderr,
|
||||
Socket::kFinalizerNormal);
|
||||
}
|
||||
if (mode == kNormal) {
|
||||
if (Process::ModeIsAttached(static_cast<ProcessStartMode>(mode))) {
|
||||
Socket::SetSocketIdNativeField(exit_handle, exit_event,
|
||||
Socket::kFinalizerNormal);
|
||||
}
|
||||
|
|
|
@ -80,8 +80,9 @@ enum ProcessSignals {
|
|||
// To be kept in sync with ProcessStartMode consts in sdk/lib/io/process.dart.
|
||||
enum ProcessStartMode {
|
||||
kNormal = 0,
|
||||
kDetached = 1,
|
||||
kDetachedWithStdio = 2,
|
||||
kInheritStdio = 1,
|
||||
kDetached = 2,
|
||||
kDetachedWithStdio = 3,
|
||||
};
|
||||
|
||||
class Process {
|
||||
|
@ -153,6 +154,9 @@ class Process {
|
|||
static int64_t MaxRSS();
|
||||
static void GetRSSInformation(int64_t* max_rss, int64_t* current_rss);
|
||||
|
||||
static bool ModeIsAttached(ProcessStartMode mode);
|
||||
static bool ModeHasStdio(ProcessStartMode mode);
|
||||
|
||||
private:
|
||||
static int global_exit_code_;
|
||||
static Mutex* global_exit_code_mutex_;
|
||||
|
|
|
@ -319,7 +319,7 @@ class ProcessStarter {
|
|||
ExitCodeHandler::ProcessStarted();
|
||||
|
||||
// Register the child process if not detached.
|
||||
if (mode_ == kNormal) {
|
||||
if (Process::ModeIsAttached(mode_)) {
|
||||
err = RegisterProcess(pid);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
|
@ -339,7 +339,7 @@ class ProcessStarter {
|
|||
// Read the result of executing the child process.
|
||||
VOID_TEMP_FAILURE_RETRY(close(exec_control_[1]));
|
||||
exec_control_[1] = -1;
|
||||
if (mode_ == kNormal) {
|
||||
if (Process::ModeIsAttached(mode_)) {
|
||||
err = ReadExecResult();
|
||||
} else {
|
||||
err = ReadDetachedExecResult(&pid);
|
||||
|
@ -349,7 +349,7 @@ class ProcessStarter {
|
|||
|
||||
// Return error code if any failures.
|
||||
if (err != 0) {
|
||||
if (mode_ == kNormal) {
|
||||
if (Process::ModeIsAttached(mode_)) {
|
||||
// Since exec() failed, we're not interested in the exit code.
|
||||
// We close the reading side of the exit code pipe here.
|
||||
// GetProcessExitCodes will get a broken pipe error when it
|
||||
|
@ -362,7 +362,7 @@ class ProcessStarter {
|
|||
return err;
|
||||
}
|
||||
|
||||
if (mode_ != kDetached) {
|
||||
if (Process::ModeHasStdio(mode_)) {
|
||||
// Connect stdio, stdout and stderr.
|
||||
FDUtils::SetNonBlocking(read_in_[0]);
|
||||
*in_ = read_in_[0];
|
||||
|
@ -405,7 +405,7 @@ class ProcessStarter {
|
|||
}
|
||||
|
||||
// For detached processes the pipe to connect stderr and stdin are not used.
|
||||
if (mode_ != kDetached) {
|
||||
if (Process::ModeHasStdio(mode_)) {
|
||||
result = TEMP_FAILURE_RETRY(pipe2(read_err_, O_CLOEXEC));
|
||||
if (result < 0) {
|
||||
return CleanupAndReturnError();
|
||||
|
@ -428,7 +428,7 @@ class ProcessStarter {
|
|||
perror("Failed receiving notification message");
|
||||
exit(1);
|
||||
}
|
||||
if (mode_ == kNormal) {
|
||||
if (Process::ModeIsAttached(mode_)) {
|
||||
ExecProcess();
|
||||
} else {
|
||||
ExecDetachedProcess();
|
||||
|
@ -469,16 +469,20 @@ class ProcessStarter {
|
|||
}
|
||||
|
||||
void ExecProcess() {
|
||||
if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
}
|
||||
if (mode_ == kNormal) {
|
||||
if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
}
|
||||
if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
}
|
||||
} else {
|
||||
ASSERT(mode_ == kInheritStdio);
|
||||
}
|
||||
|
||||
if (working_directory_ != NULL &&
|
||||
|
|
|
@ -319,7 +319,7 @@ class ProcessStarter {
|
|||
ExitCodeHandler::ProcessStarted();
|
||||
|
||||
// Register the child process if not detached.
|
||||
if (mode_ == kNormal) {
|
||||
if (Process::ModeIsAttached(mode_)) {
|
||||
err = RegisterProcess(pid);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
|
@ -339,7 +339,7 @@ class ProcessStarter {
|
|||
// Read the result of executing the child process.
|
||||
VOID_TEMP_FAILURE_RETRY(close(exec_control_[1]));
|
||||
exec_control_[1] = -1;
|
||||
if (mode_ == kNormal) {
|
||||
if (Process::ModeIsAttached(mode_)) {
|
||||
err = ReadExecResult();
|
||||
} else {
|
||||
err = ReadDetachedExecResult(&pid);
|
||||
|
@ -349,7 +349,7 @@ class ProcessStarter {
|
|||
|
||||
// Return error code if any failures.
|
||||
if (err != 0) {
|
||||
if (mode_ == kNormal) {
|
||||
if (Process::ModeIsAttached(mode_)) {
|
||||
// Since exec() failed, we're not interested in the exit code.
|
||||
// We close the reading side of the exit code pipe here.
|
||||
// GetProcessExitCodes will get a broken pipe error when it
|
||||
|
@ -362,7 +362,7 @@ class ProcessStarter {
|
|||
return err;
|
||||
}
|
||||
|
||||
if (mode_ != kDetached) {
|
||||
if (Process::ModeHasStdio(mode_)) {
|
||||
// Connect stdio, stdout and stderr.
|
||||
FDUtils::SetNonBlocking(read_in_[0]);
|
||||
*in_ = read_in_[0];
|
||||
|
@ -405,7 +405,7 @@ class ProcessStarter {
|
|||
}
|
||||
|
||||
// For detached processes the pipe to connect stderr and stdin are not used.
|
||||
if (mode_ != kDetached) {
|
||||
if (Process::ModeHasStdio(mode_)) {
|
||||
result = TEMP_FAILURE_RETRY(pipe2(read_err_, O_CLOEXEC));
|
||||
if (result < 0) {
|
||||
return CleanupAndReturnError();
|
||||
|
@ -428,7 +428,7 @@ class ProcessStarter {
|
|||
perror("Failed receiving notification message");
|
||||
exit(1);
|
||||
}
|
||||
if (mode_ == kNormal) {
|
||||
if (Process::ModeIsAttached(mode_)) {
|
||||
ExecProcess();
|
||||
} else {
|
||||
ExecDetachedProcess();
|
||||
|
@ -469,16 +469,20 @@ class ProcessStarter {
|
|||
}
|
||||
|
||||
void ExecProcess() {
|
||||
if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
}
|
||||
if (mode_ == kNormal) {
|
||||
if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
}
|
||||
if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
}
|
||||
} else {
|
||||
ASSERT(mode_ == kInheritStdio);
|
||||
}
|
||||
|
||||
if (working_directory_ != NULL &&
|
||||
|
|
|
@ -313,7 +313,7 @@ class ProcessStarter {
|
|||
ExitCodeHandler::ProcessStarted();
|
||||
|
||||
// Register the child process if not detached.
|
||||
if (mode_ == kNormal) {
|
||||
if (Process::ModeIsAttached(mode_)) {
|
||||
err = RegisterProcess(pid);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
|
@ -333,7 +333,7 @@ class ProcessStarter {
|
|||
// Read the result of executing the child process.
|
||||
VOID_TEMP_FAILURE_RETRY(close(exec_control_[1]));
|
||||
exec_control_[1] = -1;
|
||||
if (mode_ == kNormal) {
|
||||
if (Process::ModeIsAttached(mode_)) {
|
||||
err = ReadExecResult();
|
||||
} else {
|
||||
err = ReadDetachedExecResult(&pid);
|
||||
|
@ -343,7 +343,7 @@ class ProcessStarter {
|
|||
|
||||
// Return error code if any failures.
|
||||
if (err != 0) {
|
||||
if (mode_ == kNormal) {
|
||||
if (Process::ModeIsAttached(mode_)) {
|
||||
// Since exec() failed, we're not interested in the exit code.
|
||||
// We close the reading side of the exit code pipe here.
|
||||
// GetProcessExitCodes will get a broken pipe error when it
|
||||
|
@ -356,7 +356,7 @@ class ProcessStarter {
|
|||
return err;
|
||||
}
|
||||
|
||||
if (mode_ != kDetached) {
|
||||
if (Process::ModeHasStdio(mode_)) {
|
||||
// Connect stdio, stdout and stderr.
|
||||
FDUtils::SetNonBlocking(read_in_[0]);
|
||||
*in_ = read_in_[0];
|
||||
|
@ -403,7 +403,7 @@ class ProcessStarter {
|
|||
FDUtils::SetCloseOnExec(read_in_[1]);
|
||||
|
||||
// For detached processes the pipe to connect stderr and stdin are not used.
|
||||
if (mode_ != kDetached) {
|
||||
if (Process::ModeHasStdio(mode_)) {
|
||||
result = TEMP_FAILURE_RETRY(pipe(read_err_));
|
||||
if (result < 0) {
|
||||
return CleanupAndReturnError();
|
||||
|
@ -430,7 +430,7 @@ class ProcessStarter {
|
|||
perror("Failed receiving notification message");
|
||||
exit(1);
|
||||
}
|
||||
if (mode_ == kNormal) {
|
||||
if (Process::ModeIsAttached(mode_)) {
|
||||
ExecProcess();
|
||||
} else {
|
||||
ExecDetachedProcess();
|
||||
|
@ -438,16 +438,20 @@ class ProcessStarter {
|
|||
}
|
||||
|
||||
void ExecProcess() {
|
||||
if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
}
|
||||
if (mode_ == kNormal) {
|
||||
if (TEMP_FAILURE_RETRY(dup2(write_out_[0], STDIN_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
}
|
||||
if (TEMP_FAILURE_RETRY(dup2(read_in_[1], STDOUT_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
if (TEMP_FAILURE_RETRY(dup2(read_err_[1], STDERR_FILENO)) == -1) {
|
||||
ReportChildError();
|
||||
}
|
||||
} else {
|
||||
ASSERT(mode_ == kInheritStdio);
|
||||
}
|
||||
|
||||
if (working_directory_ != NULL &&
|
||||
|
|
|
@ -281,7 +281,7 @@ class _ProcessImpl extends _ProcessImplNativeWrapper implements Process {
|
|||
}
|
||||
_mode = mode;
|
||||
|
||||
if (mode != ProcessStartMode.DETACHED) {
|
||||
if (_modeHasStdio(mode)) {
|
||||
// stdin going to process.
|
||||
_stdin = new _StdSink(new _Socket._writePipe().._owner = this);
|
||||
// stdout coming from process.
|
||||
|
@ -289,7 +289,7 @@ class _ProcessImpl extends _ProcessImplNativeWrapper implements Process {
|
|||
// stderr coming from process.
|
||||
_stderr = new _StdStream(new _Socket._readPipe().._owner = this);
|
||||
}
|
||||
if (mode == ProcessStartMode.NORMAL) {
|
||||
if (_modeIsAttached(mode)) {
|
||||
_exitHandler = new _Socket._readPipe();
|
||||
}
|
||||
_ended = false;
|
||||
|
@ -303,6 +303,16 @@ class _ProcessImpl extends _ProcessImplNativeWrapper implements Process {
|
|||
_NativeSocket get _stderrNativeSocket =>
|
||||
(_stderr._stream as _Socket)._nativeSocket;
|
||||
|
||||
static bool _modeIsAttached(ProcessStartMode mode) {
|
||||
return (mode == ProcessStartMode.NORMAL) ||
|
||||
(mode == ProcessStartMode.INHERIT_STDIO);
|
||||
}
|
||||
|
||||
static bool _modeHasStdio(ProcessStartMode mode) {
|
||||
return (mode == ProcessStartMode.NORMAL) ||
|
||||
(mode == ProcessStartMode.DETACHED_WITH_STDIO);
|
||||
}
|
||||
|
||||
static String _getShellCommand() {
|
||||
if (Platform.isWindows) {
|
||||
return 'cmd.exe';
|
||||
|
@ -390,7 +400,7 @@ class _ProcessImpl extends _ProcessImplNativeWrapper implements Process {
|
|||
|
||||
Future<Process> _start() {
|
||||
var completer = new Completer<Process>();
|
||||
if (_mode == ProcessStartMode.NORMAL) {
|
||||
if (_modeIsAttached(_mode)) {
|
||||
_exitCode = new Completer<int>();
|
||||
}
|
||||
// TODO(ager): Make the actual process starting really async instead of
|
||||
|
@ -404,10 +414,10 @@ class _ProcessImpl extends _ProcessImplNativeWrapper implements Process {
|
|||
_workingDirectory,
|
||||
_environment,
|
||||
_mode.index,
|
||||
_mode == ProcessStartMode.DETACHED ? null : _stdinNativeSocket,
|
||||
_mode == ProcessStartMode.DETACHED ? null : _stdoutNativeSocket,
|
||||
_mode == ProcessStartMode.DETACHED ? null : _stderrNativeSocket,
|
||||
_mode != ProcessStartMode.NORMAL ? null : _exitHandler._nativeSocket,
|
||||
_modeHasStdio(_mode) ? _stdinNativeSocket : null,
|
||||
_modeHasStdio(_mode) ? _stdoutNativeSocket : null,
|
||||
_modeHasStdio(_mode) ? _stderrNativeSocket : null,
|
||||
_modeIsAttached(_mode) ? _exitHandler._nativeSocket : null,
|
||||
status);
|
||||
if (!success) {
|
||||
completer.completeError(new ProcessException(
|
||||
|
@ -420,7 +430,7 @@ class _ProcessImpl extends _ProcessImplNativeWrapper implements Process {
|
|||
|
||||
// Setup an exit handler to handle internal cleanup and possible
|
||||
// callback when a process terminates.
|
||||
if (_mode == ProcessStartMode.NORMAL) {
|
||||
if (_modeIsAttached(_mode)) {
|
||||
int exitDataRead = 0;
|
||||
final int EXIT_DATA_SIZE = 8;
|
||||
List<int> exitDataBuffer = new List<int>(EXIT_DATA_SIZE);
|
||||
|
@ -436,7 +446,9 @@ class _ProcessImpl extends _ProcessImplNativeWrapper implements Process {
|
|||
_ended = true;
|
||||
_exitCode.complete(exitCode(exitDataBuffer));
|
||||
// Kill stdin, helping hand if the user forgot to do it.
|
||||
(_stdin._sink as _Socket).destroy();
|
||||
if (_modeHasStdio(_mode)) {
|
||||
(_stdin._sink as _Socket).destroy();
|
||||
}
|
||||
_resourceInfo.stopped();
|
||||
}
|
||||
|
||||
|
|
|
@ -504,39 +504,41 @@ class ProcessStarter {
|
|||
STARTUPINFOEXW startup_info;
|
||||
ZeroMemory(&startup_info, sizeof(startup_info));
|
||||
startup_info.StartupInfo.cb = sizeof(startup_info);
|
||||
startup_info.StartupInfo.hStdInput = stdin_handles_[kReadHandle];
|
||||
startup_info.StartupInfo.hStdOutput = stdout_handles_[kWriteHandle];
|
||||
startup_info.StartupInfo.hStdError = stderr_handles_[kWriteHandle];
|
||||
startup_info.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
|
||||
if (mode_ != kInheritStdio) {
|
||||
startup_info.StartupInfo.hStdInput = stdin_handles_[kReadHandle];
|
||||
startup_info.StartupInfo.hStdOutput = stdout_handles_[kWriteHandle];
|
||||
startup_info.StartupInfo.hStdError = stderr_handles_[kWriteHandle];
|
||||
startup_info.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
|
||||
|
||||
bool supports_proc_thread_attr_lists = EnsureInitialized();
|
||||
if (supports_proc_thread_attr_lists) {
|
||||
// Setup the handles to inherit. We only want to inherit the three handles
|
||||
// for stdin, stdout and stderr.
|
||||
SIZE_T size = 0;
|
||||
// The call to determine the size of an attribute list always fails with
|
||||
// ERROR_INSUFFICIENT_BUFFER and that error should be ignored.
|
||||
if (!init_proc_thread_attr_list(NULL, 1, 0, &size) &&
|
||||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
|
||||
return CleanupAndReturnError();
|
||||
bool supports_proc_thread_attr_lists = EnsureInitialized();
|
||||
if (supports_proc_thread_attr_lists) {
|
||||
// Setup the handles to inherit. We only want to inherit the three
|
||||
// handles for stdin, stdout and stderr.
|
||||
SIZE_T size = 0;
|
||||
// The call to determine the size of an attribute list always fails with
|
||||
// ERROR_INSUFFICIENT_BUFFER and that error should be ignored.
|
||||
if (!init_proc_thread_attr_list(NULL, 1, 0, &size) &&
|
||||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
|
||||
return CleanupAndReturnError();
|
||||
}
|
||||
attribute_list_ = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(
|
||||
Dart_ScopeAllocate(size));
|
||||
ZeroMemory(attribute_list_, size);
|
||||
if (!init_proc_thread_attr_list(attribute_list_, 1, 0, &size)) {
|
||||
return CleanupAndReturnError();
|
||||
}
|
||||
static const int kNumInheritedHandles = 3;
|
||||
HANDLE inherited_handles[kNumInheritedHandles] = {
|
||||
stdin_handles_[kReadHandle], stdout_handles_[kWriteHandle],
|
||||
stderr_handles_[kWriteHandle]};
|
||||
if (!update_proc_thread_attr(
|
||||
attribute_list_, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
|
||||
inherited_handles, kNumInheritedHandles * sizeof(HANDLE), NULL,
|
||||
NULL)) {
|
||||
return CleanupAndReturnError();
|
||||
}
|
||||
startup_info.lpAttributeList = attribute_list_;
|
||||
}
|
||||
attribute_list_ = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(
|
||||
Dart_ScopeAllocate(size));
|
||||
ZeroMemory(attribute_list_, size);
|
||||
if (!init_proc_thread_attr_list(attribute_list_, 1, 0, &size)) {
|
||||
return CleanupAndReturnError();
|
||||
}
|
||||
static const int kNumInheritedHandles = 3;
|
||||
HANDLE inherited_handles[kNumInheritedHandles] = {
|
||||
stdin_handles_[kReadHandle], stdout_handles_[kWriteHandle],
|
||||
stderr_handles_[kWriteHandle]};
|
||||
if (!update_proc_thread_attr(
|
||||
attribute_list_, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
|
||||
inherited_handles, kNumInheritedHandles * sizeof(HANDLE), NULL,
|
||||
NULL)) {
|
||||
return CleanupAndReturnError();
|
||||
}
|
||||
startup_info.lpAttributeList = attribute_list_;
|
||||
}
|
||||
|
||||
PROCESS_INFORMATION process_info;
|
||||
|
@ -545,7 +547,7 @@ class ProcessStarter {
|
|||
// Create process.
|
||||
DWORD creation_flags =
|
||||
EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT;
|
||||
if (mode_ != kNormal) {
|
||||
if (!Process::ModeIsAttached(mode_)) {
|
||||
creation_flags |= DETACHED_PROCESS;
|
||||
}
|
||||
BOOL result = CreateProcessW(
|
||||
|
@ -558,26 +560,33 @@ class ProcessStarter {
|
|||
reinterpret_cast<STARTUPINFOW*>(&startup_info), &process_info);
|
||||
|
||||
if (result == 0) {
|
||||
Log::PrintErr("CreateProcessW failed %d\n", GetLastError());
|
||||
return CleanupAndReturnError();
|
||||
}
|
||||
|
||||
CloseHandle(stdin_handles_[kReadHandle]);
|
||||
CloseHandle(stdout_handles_[kWriteHandle]);
|
||||
CloseHandle(stderr_handles_[kWriteHandle]);
|
||||
if (mode_ == kNormal) {
|
||||
if (mode_ != kInheritStdio) {
|
||||
CloseHandle(stdin_handles_[kReadHandle]);
|
||||
CloseHandle(stdout_handles_[kWriteHandle]);
|
||||
CloseHandle(stderr_handles_[kWriteHandle]);
|
||||
}
|
||||
if (Process::ModeIsAttached(mode_)) {
|
||||
ProcessInfoList::AddProcess(process_info.dwProcessId,
|
||||
process_info.hProcess,
|
||||
exit_handles_[kWriteHandle]);
|
||||
}
|
||||
if (mode_ != kDetached) {
|
||||
// Connect the three stdio streams.
|
||||
FileHandle* stdin_handle = new FileHandle(stdin_handles_[kWriteHandle]);
|
||||
FileHandle* stdout_handle = new FileHandle(stdout_handles_[kReadHandle]);
|
||||
FileHandle* stderr_handle = new FileHandle(stderr_handles_[kReadHandle]);
|
||||
*in_ = reinterpret_cast<intptr_t>(stdout_handle);
|
||||
*out_ = reinterpret_cast<intptr_t>(stdin_handle);
|
||||
*err_ = reinterpret_cast<intptr_t>(stderr_handle);
|
||||
if (mode_ == kNormal) {
|
||||
if (Process::ModeHasStdio(mode_)) {
|
||||
FileHandle* stdin_handle = new FileHandle(stdin_handles_[kWriteHandle]);
|
||||
FileHandle* stdout_handle =
|
||||
new FileHandle(stdout_handles_[kReadHandle]);
|
||||
FileHandle* stderr_handle =
|
||||
new FileHandle(stderr_handles_[kReadHandle]);
|
||||
*in_ = reinterpret_cast<intptr_t>(stdout_handle);
|
||||
*out_ = reinterpret_cast<intptr_t>(stdin_handle);
|
||||
*err_ = reinterpret_cast<intptr_t>(stderr_handle);
|
||||
}
|
||||
if (Process::ModeIsAttached(mode_)) {
|
||||
FileHandle* exit_handle = new FileHandle(exit_handles_[kReadHandle]);
|
||||
*exit_handler_ = reinterpret_cast<intptr_t>(exit_handle);
|
||||
}
|
||||
|
@ -603,13 +612,15 @@ class ProcessStarter {
|
|||
if (mode_ != kDetached) {
|
||||
// Open pipes for stdin, stdout, stderr and for communicating the exit
|
||||
// code.
|
||||
if (!CreateProcessPipe(stdin_handles_, pipe_names[0], kInheritRead) ||
|
||||
!CreateProcessPipe(stdout_handles_, pipe_names[1], kInheritWrite) ||
|
||||
!CreateProcessPipe(stderr_handles_, pipe_names[2], kInheritWrite)) {
|
||||
return CleanupAndReturnError();
|
||||
if (Process::ModeHasStdio(mode_)) {
|
||||
if (!CreateProcessPipe(stdin_handles_, pipe_names[0], kInheritRead) ||
|
||||
!CreateProcessPipe(stdout_handles_, pipe_names[1], kInheritWrite) ||
|
||||
!CreateProcessPipe(stderr_handles_, pipe_names[2], kInheritWrite)) {
|
||||
return CleanupAndReturnError();
|
||||
}
|
||||
}
|
||||
// Only open exit code pipe for non detached processes.
|
||||
if (mode_ == kNormal) {
|
||||
if (Process::ModeIsAttached(mode_)) {
|
||||
if (!CreateProcessPipe(exit_handles_, pipe_names[3], kInheritNone)) {
|
||||
return CleanupAndReturnError();
|
||||
}
|
||||
|
|
|
@ -141,12 +141,15 @@ enum ProcessStartMode {
|
|||
/// Normal child process.
|
||||
NORMAL,
|
||||
|
||||
/// Stdio handles are inherited by the child process.
|
||||
INHERIT_STDIO,
|
||||
|
||||
/// Detached child process with no open communication channel.
|
||||
DETACHED,
|
||||
|
||||
/// Detached child process with stdin, stdout and stderr still open
|
||||
/// for communication with the child.
|
||||
DETACHED_WITH_STDIO
|
||||
DETACHED_WITH_STDIO,
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
29
tests/standalone_2/io/process_inherit_stdio_script.dart
Normal file
29
tests/standalone_2/io/process_inherit_stdio_script.dart
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import "package:async_helper/async_helper.dart";
|
||||
|
||||
import "process_test_util.dart";
|
||||
|
||||
void main(List<String> args) {
|
||||
String arg = args[0];
|
||||
if (arg == "--child") {
|
||||
print(args[1]);
|
||||
return;
|
||||
}
|
||||
asyncStart();
|
||||
var script =
|
||||
Platform.script.resolve('process_inherit_stdio_script.dart').toFilePath();
|
||||
var future = Process.start(Platform.executable, [script, "--child", "foo"],
|
||||
mode: ProcessStartMode.INHERIT_STDIO);
|
||||
future.then((process) {
|
||||
process.exitCode.then((c) {
|
||||
asyncEnd();
|
||||
});
|
||||
});
|
||||
}
|
39
tests/standalone_2/io/process_inherit_stdio_test.dart
Normal file
39
tests/standalone_2/io/process_inherit_stdio_test.dart
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// OtherResources=process_inherit_stdio_script.dart
|
||||
|
||||
// Process test program to test 'inherit stdio' processes.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import "package:async_helper/async_helper.dart";
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
import "process_test_util.dart";
|
||||
|
||||
main() {
|
||||
asyncStart();
|
||||
// process_inherit_stdio_script.dart spawns a process in INHERIT_STDIO mode
|
||||
// that prints to its stdout. Since that child process inherits the stdout
|
||||
// of the process spawned here, we should see it.
|
||||
var script =
|
||||
Platform.script.resolve('process_inherit_stdio_script.dart').toFilePath();
|
||||
var future = Process.start(Platform.executable, [script, "foo"]);
|
||||
Completer<String> s = new Completer();
|
||||
future.then((process) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
process.stdout.transform(UTF8.decoder).listen((data) {
|
||||
buf.write(data);
|
||||
}, onDone: () {
|
||||
s.complete(buf.toString());
|
||||
});
|
||||
});
|
||||
s.future.then((String result) {
|
||||
Expect.isTrue(result.contains("foo"));
|
||||
asyncEnd();
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue