1 // Copyright 2015 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "test/multiprocess_exec.h"
16
17 #include <sys/types.h>
18
19 #include "base/check.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "gtest/gtest.h"
22 #include "util/win/command_line.h"
23
24 namespace crashpad {
25 namespace test {
26
27 namespace internal {
28
29 struct MultiprocessInfo {
MultiprocessInfocrashpad::test::internal::MultiprocessInfo30 MultiprocessInfo() {}
31 ScopedFileHANDLE pipe_c2p_read;
32 ScopedFileHANDLE pipe_c2p_write;
33 ScopedFileHANDLE pipe_p2c_read;
34 ScopedFileHANDLE pipe_p2c_write;
35 PROCESS_INFORMATION process_info;
36 };
37
38 } // namespace internal
39
Multiprocess()40 Multiprocess::Multiprocess()
41 : info_(nullptr),
42 code_(EXIT_SUCCESS),
43 reason_(kTerminationNormal) {
44 }
45
Run()46 void Multiprocess::Run() {
47 // Set up and spawn the child process.
48 ASSERT_NO_FATAL_FAILURE(PreFork());
49 RunChild();
50
51 // And then run the parent actions in this process.
52 RunParent();
53
54 // Reap the child.
55 WaitForSingleObject(info_->process_info.hProcess, INFINITE);
56 CloseHandle(info_->process_info.hThread);
57 CloseHandle(info_->process_info.hProcess);
58 }
59
SetExpectedChildTermination(TerminationReason reason,ReturnCodeType code)60 void Multiprocess::SetExpectedChildTermination(TerminationReason reason,
61 ReturnCodeType code) {
62 EXPECT_EQ(info_, nullptr)
63 << "SetExpectedChildTermination() must be called before Run()";
64 reason_ = reason;
65 code_ = code;
66 }
67
~Multiprocess()68 Multiprocess::~Multiprocess() {
69 delete info_;
70 }
71
ReadPipeHandle() const72 FileHandle Multiprocess::ReadPipeHandle() const {
73 // This is the parent case, it's stdin in the child.
74 return info_->pipe_c2p_read.get();
75 }
76
WritePipeHandle() const77 FileHandle Multiprocess::WritePipeHandle() const {
78 // This is the parent case, it's stdout in the child.
79 return info_->pipe_p2c_write.get();
80 }
81
CloseReadPipe()82 void Multiprocess::CloseReadPipe() {
83 info_->pipe_c2p_read.reset();
84 }
85
CloseWritePipe()86 void Multiprocess::CloseWritePipe() {
87 info_->pipe_p2c_write.reset();
88 }
89
RunParent()90 void Multiprocess::RunParent() {
91 MultiprocessParent();
92
93 info_->pipe_c2p_read.reset();
94 info_->pipe_p2c_write.reset();
95 }
96
RunChild()97 void Multiprocess::RunChild() {
98 MultiprocessChild();
99
100 info_->pipe_c2p_write.reset();
101 info_->pipe_p2c_read.reset();
102 }
103
MultiprocessExec()104 MultiprocessExec::MultiprocessExec()
105 : Multiprocess(), command_(), arguments_(), command_line_() {
106 }
107
SetChildCommand(const base::FilePath & command,const std::vector<std::string> * arguments)108 void MultiprocessExec::SetChildCommand(
109 const base::FilePath& command,
110 const std::vector<std::string>* arguments) {
111 command_ = command;
112 if (arguments) {
113 arguments_ = *arguments;
114 } else {
115 arguments_.clear();
116 }
117 }
118
~MultiprocessExec()119 MultiprocessExec::~MultiprocessExec() {
120 }
121
PreFork()122 void MultiprocessExec::PreFork() {
123 ASSERT_FALSE(command_.empty());
124
125 command_line_.clear();
126 AppendCommandLineArgument(command_.value(), &command_line_);
127 for (size_t i = 0; i < arguments_.size(); ++i) {
128 AppendCommandLineArgument(base::UTF8ToWide(arguments_[i]), &command_line_);
129 }
130
131 // Make pipes for child-to-parent and parent-to-child communication. Mark them
132 // as inheritable via the SECURITY_ATTRIBUTES, but use SetHandleInformation to
133 // ensure that the parent sides are not inherited.
134 ASSERT_EQ(info(), nullptr);
135 set_info(new internal::MultiprocessInfo());
136
137 SECURITY_ATTRIBUTES security_attributes = {0};
138 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
139 security_attributes.bInheritHandle = TRUE;
140
141 HANDLE c2p_read, c2p_write;
142 PCHECK(CreatePipe(&c2p_read, &c2p_write, &security_attributes, 0));
143 PCHECK(SetHandleInformation(c2p_read, HANDLE_FLAG_INHERIT, 0));
144 info()->pipe_c2p_read.reset(c2p_read);
145 info()->pipe_c2p_write.reset(c2p_write);
146
147 HANDLE p2c_read, p2c_write;
148 PCHECK(CreatePipe(&p2c_read, &p2c_write, &security_attributes, 0));
149 PCHECK(SetHandleInformation(p2c_write, HANDLE_FLAG_INHERIT, 0));
150 info()->pipe_p2c_read.reset(p2c_read);
151 info()->pipe_p2c_write.reset(p2c_write);
152 }
153
MultiprocessChild()154 void MultiprocessExec::MultiprocessChild() {
155 STARTUPINFO startup_info = {0};
156 startup_info.cb = sizeof(startup_info);
157 startup_info.hStdInput = info()->pipe_p2c_read.get();
158 startup_info.hStdOutput = info()->pipe_c2p_write.get();
159 startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
160 startup_info.dwFlags = STARTF_USESTDHANDLES;
161 PCHECK(CreateProcess(command_.value().c_str(),
162 &command_line_[0], // This cannot be constant, per MSDN.
163 nullptr,
164 nullptr,
165 TRUE,
166 0,
167 nullptr,
168 nullptr,
169 &startup_info,
170 &info()->process_info));
171 }
172
ChildProcess()173 ProcessType MultiprocessExec::ChildProcess() {
174 return info()->process_info.hProcess;
175 }
176
177 } // namespace test
178 } // namespace crashpad
179