OLD | NEW |
---|---|
(Empty) | |
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 "util/test/multiprocess_exec.h" | |
16 | |
17 #include "base/logging.h" | |
18 #include "base/strings/utf_string_conversions.h" | |
19 #include "gtest/gtest.h" | |
20 | |
21 namespace crashpad { | |
22 namespace test { | |
23 | |
24 namespace { | |
25 | |
26 void ArgvQuote(const std::wstring& argument, std::wstring* command_line) { | |
Mark Mentovai
2015/01/28 19:58:28
It’s not obvious that this is additive. Add a comm
scottmg
2015/01/28 22:14:58
Done.
| |
27 // Don't bother quoting if unnecessary. | |
28 if (argument.empty() == false && | |
Mark Mentovai
2015/01/28 19:58:28
Adapt to local style: !argument.empty(), std::wstr
scottmg
2015/01/28 22:14:58
Done.
| |
29 argument.find_first_of(L" \t\n\v\"") == argument.npos) { | |
30 command_line->append(argument); | |
31 } else { | |
32 // Ref: http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04 /23/everyone-quotes-arguments-the-wrong-way.aspx | |
Mark Mentovai
2015/01/28 19:58:28
I’m glad you did this correctly. Sloppy quoting is
Mark Mentovai
2015/01/28 19:58:28
Promote this to the function-level comment?
scottmg
2015/01/28 22:14:58
Done.
| |
33 command_line->push_back(L'"'); | |
34 for (std::wstring::const_iterator i = argument.begin();; ++i) { | |
35 unsigned backslash_count = 0; | |
Mark Mentovai
2015/01/28 19:58:28
size_t
scottmg
2015/01/28 22:14:58
Done.
| |
36 while (i != argument.end() && *i == L'\\') { | |
37 ++i; | |
38 ++backslash_count; | |
39 } | |
40 if (i == argument.end()) { | |
41 // Escape all backslashes, but let the terminating double quotation mark | |
42 // we add below be interpreted as a metacharacter. | |
43 command_line->append(backslash_count * 2, L'\\'); | |
44 break; | |
45 } else if (*i == L'"') { | |
46 // Escape all backslashes and the following double quotation mark. | |
47 command_line->append(backslash_count * 2 + 1, L'\\'); | |
48 command_line->push_back(*i); | |
49 } else { | |
50 // Backslashes aren't special here. | |
51 command_line->append(backslash_count, L'\\'); | |
52 command_line->push_back(*i); | |
53 } | |
54 } | |
55 command_line->push_back(L'"'); | |
56 } | |
57 } | |
58 | |
59 } // namespace | |
60 | |
61 namespace internal { | |
62 | |
63 struct MultiprocessInfo { | |
64 MultiprocessInfo() {} | |
65 ScopedFileHANDLE pipe_c2p_read; | |
66 ScopedFileHANDLE pipe_c2p_write; | |
67 ScopedFileHANDLE pipe_p2c_read; | |
68 ScopedFileHANDLE pipe_p2c_write; | |
69 PROCESS_INFORMATION process_info; | |
70 }; | |
71 | |
72 } // namespace internal | |
73 | |
74 Multiprocess::Multiprocess() | |
Mark Mentovai
2015/01/28 19:58:28
There should be a comment somewhere (maybe here, m
scottmg
2015/01/28 22:14:58
Done in header.
| |
75 : info_(nullptr), | |
76 code_(EXIT_SUCCESS), | |
77 reason_(kTerminationNormal) { | |
78 } | |
79 | |
80 void Multiprocess::Run() { | |
81 ASSERT_NO_FATAL_FAILURE(PreFork()); | |
82 RunChild(); | |
Mark Mentovai
2015/01/28 19:58:28
To that end, this whole function is kind of weird.
scottmg
2015/01/28 22:14:58
It's setting up and spawning the child, then runni
Mark Mentovai
2015/01/28 22:32:54
scottmg wrote:
| |
83 RunParent(); | |
84 | |
85 // Reap the child. | |
86 WaitForSingleObject(info_->process_info.hProcess, INFINITE); | |
87 CloseHandle(info_->process_info.hThread); | |
88 CloseHandle(info_->process_info.hProcess); | |
89 } | |
90 | |
91 Multiprocess::~Multiprocess() { | |
92 delete info_; | |
93 } | |
94 | |
95 void Multiprocess::PreFork() { | |
96 NOTREACHED(); | |
97 } | |
98 | |
99 FileHandle Multiprocess::ReadPipeHandle() const { | |
100 // This is the parent case, it's stdin in the child. | |
101 return info_->pipe_c2p_read.get(); | |
102 } | |
103 | |
104 FileHandle Multiprocess::WritePipeHandle() const { | |
105 // This is the parent case, it's stdout in the child. | |
106 return info_->pipe_p2c_write.get(); | |
107 } | |
108 | |
Mark Mentovai
2015/01/28 19:58:28
You haven’t implemented ChildPID() (but you probab
scottmg
2015/01/28 22:14:58
Done.
| |
109 void Multiprocess::RunParent() { | |
110 MultiprocessParent(); | |
111 | |
112 info_->pipe_c2p_read.reset(); | |
113 info_->pipe_p2c_write.reset(); | |
114 } | |
115 | |
116 void Multiprocess::RunChild() { | |
117 MultiprocessChild(); | |
118 | |
119 info_->pipe_c2p_write.reset(); | |
120 info_->pipe_p2c_read.reset(); | |
121 } | |
122 | |
123 MultiprocessExec::MultiprocessExec() | |
124 : Multiprocess(), command_(), arguments_(), argv_() { | |
125 } | |
126 | |
127 void MultiprocessExec::SetChildCommand( | |
128 const std::string& command, | |
129 const std::vector<std::string>* arguments) { | |
130 command_ = command; | |
131 if (arguments) { | |
132 arguments_ = *arguments; | |
133 } else { | |
134 arguments_.clear(); | |
135 } | |
136 } | |
137 | |
138 MultiprocessExec::~MultiprocessExec() { | |
139 } | |
140 | |
141 void MultiprocessExec::PreFork() { | |
142 ASSERT_FALSE(command_.empty()); | |
143 | |
144 argv_.clear(); | |
145 ArgvQuote(base::UTF8ToUTF16(command_), &argv_); | |
146 for (size_t i = 0; i < arguments_.size(); ++i) { | |
147 argv_ += L" "; | |
148 ArgvQuote(base::UTF8ToUTF16(arguments_[i]), &argv_); | |
149 } | |
150 | |
151 // Make pipes for child-to-parent and parent-to-child communication. Mark them | |
152 // as inheritable via the SECURITY_ATTRIBUTES, but use SetHandleInformation to | |
153 // ensure that the parent sides are not inherited. | |
154 ASSERT_EQ(nullptr, info()); | |
155 set_info(new internal::MultiprocessInfo); | |
Mark Mentovai
2015/01/28 19:58:28
() to be explicit.
scottmg
2015/01/28 22:14:58
Done.
| |
156 | |
157 SECURITY_ATTRIBUTES security_attributes = {0}; | |
158 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); | |
159 security_attributes.bInheritHandle = TRUE; | |
160 | |
161 HANDLE c2p_read, c2p_write; | |
162 PCHECK(CreatePipe(&c2p_read, &c2p_write, &security_attributes, 0)); | |
163 PCHECK(SetHandleInformation(c2p_read, HANDLE_FLAG_INHERIT, 0)); | |
164 info()->pipe_c2p_read.reset(c2p_read); | |
165 info()->pipe_c2p_write.reset(c2p_write); | |
166 | |
167 HANDLE p2c_read, p2c_write; | |
168 PCHECK(CreatePipe(&p2c_read, &p2c_write, &security_attributes, 0)); | |
169 PCHECK(SetHandleInformation(p2c_write, HANDLE_FLAG_INHERIT, 0)); | |
170 info()->pipe_p2c_read.reset(p2c_read); | |
171 info()->pipe_p2c_write.reset(p2c_write); | |
172 } | |
173 | |
174 void MultiprocessExec::MultiprocessChild() { | |
175 STARTUPINFO startup_info = {0}; | |
176 startup_info.cb = sizeof(startup_info); | |
177 startup_info.hStdInput = info()->pipe_p2c_read.get(); | |
178 startup_info.hStdOutput = info()->pipe_c2p_write.get(); | |
179 startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); | |
180 startup_info.dwFlags = STARTF_USESTDHANDLES; | |
181 PCHECK(CreateProcess(base::UTF8ToUTF16(command_).c_str(), | |
182 &argv_[0], // This cannot be constant, per MSDN. | |
183 nullptr, | |
184 nullptr, | |
185 TRUE, | |
186 0, | |
187 nullptr, | |
188 nullptr, | |
189 &startup_info, | |
190 &info()->process_info)); | |
191 } | |
192 | |
193 } // namespace test | |
194 } // namespace crashpad | |
OLD | NEW |