// Copyright 2015 The Crashpad Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "test/multiprocess_exec.h" #include #include "base/check.h" #include "base/strings/utf_string_conversions.h" #include "gtest/gtest.h" #include "util/win/command_line.h" namespace crashpad { namespace test { namespace internal { struct MultiprocessInfo { MultiprocessInfo() {} ScopedFileHANDLE pipe_c2p_read; ScopedFileHANDLE pipe_c2p_write; ScopedFileHANDLE pipe_p2c_read; ScopedFileHANDLE pipe_p2c_write; PROCESS_INFORMATION process_info; }; } // namespace internal Multiprocess::Multiprocess() : info_(nullptr), code_(EXIT_SUCCESS), reason_(kTerminationNormal) { } void Multiprocess::Run() { // Set up and spawn the child process. ASSERT_NO_FATAL_FAILURE(PreFork()); RunChild(); // And then run the parent actions in this process. RunParent(); // Reap the child. WaitForSingleObject(info_->process_info.hProcess, INFINITE); CloseHandle(info_->process_info.hThread); CloseHandle(info_->process_info.hProcess); } void Multiprocess::SetExpectedChildTermination(TerminationReason reason, ReturnCodeType code) { EXPECT_EQ(info_, nullptr) << "SetExpectedChildTermination() must be called before Run()"; reason_ = reason; code_ = code; } Multiprocess::~Multiprocess() { delete info_; } FileHandle Multiprocess::ReadPipeHandle() const { // This is the parent case, it's stdin in the child. return info_->pipe_c2p_read.get(); } FileHandle Multiprocess::WritePipeHandle() const { // This is the parent case, it's stdout in the child. return info_->pipe_p2c_write.get(); } void Multiprocess::CloseReadPipe() { info_->pipe_c2p_read.reset(); } void Multiprocess::CloseWritePipe() { info_->pipe_p2c_write.reset(); } void Multiprocess::RunParent() { MultiprocessParent(); info_->pipe_c2p_read.reset(); info_->pipe_p2c_write.reset(); } void Multiprocess::RunChild() { MultiprocessChild(); info_->pipe_c2p_write.reset(); info_->pipe_p2c_read.reset(); } MultiprocessExec::MultiprocessExec() : Multiprocess(), command_(), arguments_(), command_line_() { } void MultiprocessExec::SetChildCommand( const base::FilePath& command, const std::vector* arguments) { command_ = command; if (arguments) { arguments_ = *arguments; } else { arguments_.clear(); } } MultiprocessExec::~MultiprocessExec() { } void MultiprocessExec::PreFork() { ASSERT_FALSE(command_.empty()); command_line_.clear(); AppendCommandLineArgument(command_.value(), &command_line_); for (size_t i = 0; i < arguments_.size(); ++i) { AppendCommandLineArgument(base::UTF8ToWide(arguments_[i]), &command_line_); } // Make pipes for child-to-parent and parent-to-child communication. Mark them // as inheritable via the SECURITY_ATTRIBUTES, but use SetHandleInformation to // ensure that the parent sides are not inherited. ASSERT_EQ(info(), nullptr); set_info(new internal::MultiprocessInfo()); SECURITY_ATTRIBUTES security_attributes = {0}; security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); security_attributes.bInheritHandle = TRUE; HANDLE c2p_read, c2p_write; PCHECK(CreatePipe(&c2p_read, &c2p_write, &security_attributes, 0)); PCHECK(SetHandleInformation(c2p_read, HANDLE_FLAG_INHERIT, 0)); info()->pipe_c2p_read.reset(c2p_read); info()->pipe_c2p_write.reset(c2p_write); HANDLE p2c_read, p2c_write; PCHECK(CreatePipe(&p2c_read, &p2c_write, &security_attributes, 0)); PCHECK(SetHandleInformation(p2c_write, HANDLE_FLAG_INHERIT, 0)); info()->pipe_p2c_read.reset(p2c_read); info()->pipe_p2c_write.reset(p2c_write); } void MultiprocessExec::MultiprocessChild() { STARTUPINFO startup_info = {0}; startup_info.cb = sizeof(startup_info); startup_info.hStdInput = info()->pipe_p2c_read.get(); startup_info.hStdOutput = info()->pipe_c2p_write.get(); startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); startup_info.dwFlags = STARTF_USESTDHANDLES; PCHECK(CreateProcess(command_.value().c_str(), &command_line_[0], // This cannot be constant, per MSDN. nullptr, nullptr, TRUE, 0, nullptr, nullptr, &startup_info, &info()->process_info)); } ProcessType MultiprocessExec::ChildProcess() { return info()->process_info.hProcess; } } // namespace test } // namespace crashpad