Index: util/test/multiprocess_exec_win.cc |
diff --git a/util/test/multiprocess_exec_win.cc b/util/test/multiprocess_exec_win.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..afd79b6046a869f70c8fe83aae4ecaee944aa4d7 |
--- /dev/null |
+++ b/util/test/multiprocess_exec_win.cc |
@@ -0,0 +1,206 @@ |
+// 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 "util/test/multiprocess_exec.h" |
+ |
+#include "base/logging.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "gtest/gtest.h" |
+ |
+namespace crashpad { |
+namespace test { |
+ |
+namespace { |
+ |
+// Ref: http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx |
+void AppendCommandLineArgument(const std::wstring& argument, |
+ std::wstring* command_line) { |
+ // Don't bother quoting if unnecessary. |
+ if (!argument.empty() && |
+ argument.find_first_of(L" \t\n\v\"") == std::wstring::npos) { |
+ command_line->append(argument); |
+ } else { |
+ command_line->push_back(L'"'); |
+ for (std::wstring::const_iterator i = argument.begin();; ++i) { |
+ size_t backslash_count = 0; |
+ while (i != argument.end() && *i == L'\\') { |
+ ++i; |
+ ++backslash_count; |
+ } |
+ if (i == argument.end()) { |
+ // Escape all backslashes, but let the terminating double quotation mark |
+ // we add below be interpreted as a metacharacter. |
+ command_line->append(backslash_count * 2, L'\\'); |
+ break; |
+ } else if (*i == L'"') { |
+ // Escape all backslashes and the following double quotation mark. |
+ command_line->append(backslash_count * 2 + 1, L'\\'); |
+ command_line->push_back(*i); |
+ } else { |
+ // Backslashes aren't special here. |
+ command_line->append(backslash_count, L'\\'); |
+ command_line->push_back(*i); |
+ } |
+ } |
+ command_line->push_back(L'"'); |
+ } |
+} |
+ |
+} // namespace |
+ |
+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); |
+} |
+ |
+Multiprocess::~Multiprocess() { |
+ delete info_; |
+} |
+ |
+void Multiprocess::PreFork() { |
+ NOTREACHED(); |
+} |
+ |
+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 std::string& command, |
+ const std::vector<std::string>* arguments) { |
+ command_ = command; |
+ if (arguments) { |
+ arguments_ = *arguments; |
+ } else { |
+ arguments_.clear(); |
+ } |
+} |
+ |
+MultiprocessExec::~MultiprocessExec() { |
+} |
+ |
+void MultiprocessExec::PreFork() { |
+ ASSERT_FALSE(command_.empty()); |
+ |
+ command_line_.clear(); |
+ AppendCommandLineArgument(base::UTF8ToUTF16(command_), &command_line_); |
+ for (size_t i = 0; i < arguments_.size(); ++i) { |
+ command_line_ += L" "; |
+ AppendCommandLineArgument(base::UTF8ToUTF16(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(nullptr, info()); |
+ 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(base::UTF8ToUTF16(command_).c_str(), |
+ &command_line_[0], // This cannot be constant, per MSDN. |
+ nullptr, |
+ nullptr, |
+ TRUE, |
+ 0, |
+ nullptr, |
+ nullptr, |
+ &startup_info, |
+ &info()->process_info)); |
+} |
+ |
+} // namespace test |
+} // namespace crashpad |