OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/zygote/zygote_linux.h" | 5 #include "content/zygote/zygote_linux.h" |
6 | 6 |
7 #include <fcntl.h> | 7 #include <fcntl.h> |
8 #include <string.h> | 8 #include <string.h> |
9 #include <sys/socket.h> | 9 #include <sys/socket.h> |
10 #include <sys/types.h> | 10 #include <sys/types.h> |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
92 #endif | 92 #endif |
93 } | 93 } |
94 | 94 |
95 for (;;) { | 95 for (;;) { |
96 // This function call can return multiple times, once per fork(). | 96 // This function call can return multiple times, once per fork(). |
97 if (HandleRequestFromBrowser(kBrowserDescriptor)) | 97 if (HandleRequestFromBrowser(kBrowserDescriptor)) |
98 return true; | 98 return true; |
99 } | 99 } |
100 } | 100 } |
101 | 101 |
102 bool Zygote::GetProcessInfo(base::ProcessHandle pid, | |
103 ZygoteProcessInfo* process_info) { | |
104 DCHECK(process_info); | |
105 const ZygoteProcessMap::const_iterator it = process_info_map_.find(pid); | |
106 if (it == process_info_map_.end()) { | |
107 return false; | |
108 } | |
109 *process_info = it->second; | |
110 return true; | |
111 } | |
112 | |
102 bool Zygote::UsingSUIDSandbox() const { | 113 bool Zygote::UsingSUIDSandbox() const { |
103 return sandbox_flags_ & kSandboxLinuxSUID; | 114 return sandbox_flags_ & kSandboxLinuxSUID; |
104 } | 115 } |
105 | 116 |
106 bool Zygote::HandleRequestFromBrowser(int fd) { | 117 bool Zygote::HandleRequestFromBrowser(int fd) { |
107 std::vector<int> fds; | 118 std::vector<int> fds; |
108 char buf[kZygoteMaxMessageLength]; | 119 char buf[kZygoteMaxMessageLength]; |
109 const ssize_t len = UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds); | 120 const ssize_t len = UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds); |
110 | 121 |
111 if (len == 0 || (len == -1 && errno == ECONNRESET)) { | 122 if (len == 0 || (len == -1 && errno == ECONNRESET)) { |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
159 void Zygote::HandleReapRequest(int fd, | 170 void Zygote::HandleReapRequest(int fd, |
160 const Pickle& pickle, | 171 const Pickle& pickle, |
161 PickleIterator iter) { | 172 PickleIterator iter) { |
162 base::ProcessId child; | 173 base::ProcessId child; |
163 | 174 |
164 if (!pickle.ReadInt(&iter, &child)) { | 175 if (!pickle.ReadInt(&iter, &child)) { |
165 LOG(WARNING) << "Error parsing reap request from browser"; | 176 LOG(WARNING) << "Error parsing reap request from browser"; |
166 return; | 177 return; |
167 } | 178 } |
168 | 179 |
169 if (process_info_map_.find(child) == process_info_map_.end()) { | 180 ZygoteProcessInfo child_info; |
170 // TODO(jln): test on more bots and add a DCHECK. | 181 if (!GetProcessInfo(child, &child_info)) { |
171 LOG(ERROR) << "Child not found!"; | 182 LOG(ERROR) << "Child not found!"; |
183 NOTREACHED(); | |
172 return; | 184 return; |
173 } | 185 } |
174 const base::ProcessId actual_child = process_info_map_[child].internal_pid; | 186 |
175 const bool started_from_helper = | 187 if (!child_info.started_from_helper) { |
176 process_info_map_[child].started_from_helper; | |
177 if (!started_from_helper) { | |
178 // TODO(jln): this old code is completely broken. See crbug.com/274855. | 188 // TODO(jln): this old code is completely broken. See crbug.com/274855. |
179 base::EnsureProcessTerminated(actual_child); | 189 base::EnsureProcessTerminated(child_info.internal_pid); |
180 } else { | 190 } else { |
181 // For processes from the helper, send a GetTerminationStatus request | 191 // For processes from the helper, send a GetTerminationStatus request |
182 // with known_dead set to true. | 192 // with known_dead set to true. |
183 // This is not perfect, as the process may be killed instantly, but is | 193 // This is not perfect, as the process may be killed instantly, but is |
184 // better than ignoring the request. | 194 // better than ignoring the request. |
185 base::TerminationStatus status; | 195 base::TerminationStatus status; |
186 int exit_code; | 196 int exit_code; |
187 bool got_termination_status = | 197 bool got_termination_status = |
188 GetTerminationStatus(child, true /* known_dead */, &status, &exit_code); | 198 GetTerminationStatus(child, true /* known_dead */, &status, &exit_code); |
189 DCHECK(got_termination_status); | 199 DCHECK(got_termination_status); |
190 } | 200 } |
191 process_info_map_.erase(child); | 201 process_info_map_.erase(child); |
192 } | 202 } |
193 | 203 |
194 bool Zygote::GetTerminationStatus(base::ProcessHandle real_pid, | 204 bool Zygote::GetTerminationStatus(base::ProcessHandle real_pid, |
195 bool known_dead, | 205 bool known_dead, |
196 base::TerminationStatus* status, | 206 base::TerminationStatus* status, |
197 int* exit_code) { | 207 int* exit_code) { |
198 // Do we know about this child? | 208 |
199 if (process_info_map_.find(real_pid) == process_info_map_.end()) { | 209 ZygoteProcessInfo child_info; |
200 // TODO(jln): test on more bots and add a DCHECK. | 210 if (!GetProcessInfo(real_pid, &child_info)) { |
201 LOG(ERROR) << "Zygote::GetTerminationStatus for unknown PID " | 211 LOG(ERROR) << "Zygote::GetTerminationStatus for unknown PID " |
202 << real_pid; | 212 << real_pid; |
213 NOTREACHED(); | |
203 return false; | 214 return false; |
204 } | 215 } |
205 // We know about |real_pid|. | 216 // We know about |real_pid|. |
206 const base::ProcessHandle child = | 217 const base::ProcessHandle child = child_info.internal_pid; |
207 process_info_map_[real_pid].internal_pid; | 218 if (child_info.started_from_helper) { |
208 const bool started_from_helper = | |
209 process_info_map_[real_pid].started_from_helper; | |
210 if (started_from_helper) { | |
211 // Let the helper handle the request. | 219 // Let the helper handle the request. |
212 DCHECK(helper_); | 220 DCHECK(helper_); |
213 if (!helper_->GetTerminationStatus(child, known_dead, status, exit_code)) { | 221 if (!helper_->GetTerminationStatus(child, known_dead, status, exit_code)) { |
214 return false; | 222 return false; |
215 } | 223 } |
216 } else { | 224 } else { |
217 // Handle the request directly. | 225 // Handle the request directly. |
218 if (known_dead) { | 226 if (known_dead) { |
219 // If we know that the process is already dead and the kernel is cleaning | 227 // If we know that the process is already dead and the kernel is cleaning |
220 // it up, we do want to wait until it becomes a zombie and not risk | 228 // it up, we do want to wait until it becomes a zombie and not risk |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
254 } | 262 } |
255 | 263 |
256 base::TerminationStatus status; | 264 base::TerminationStatus status; |
257 int exit_code; | 265 int exit_code; |
258 | 266 |
259 bool got_termination_status = | 267 bool got_termination_status = |
260 GetTerminationStatus(child_requested, known_dead, &status, &exit_code); | 268 GetTerminationStatus(child_requested, known_dead, &status, &exit_code); |
261 if (!got_termination_status) { | 269 if (!got_termination_status) { |
262 // Assume that if we can't find the child in the sandbox, then | 270 // Assume that if we can't find the child in the sandbox, then |
263 // it terminated normally. | 271 // it terminated normally. |
264 // TODO(jln): add a DCHECK. | 272 NOTREACHED(); |
265 status = base::TERMINATION_STATUS_NORMAL_TERMINATION; | 273 status = base::TERMINATION_STATUS_NORMAL_TERMINATION; |
266 exit_code = RESULT_CODE_NORMAL_EXIT; | 274 exit_code = RESULT_CODE_NORMAL_EXIT; |
267 } | 275 } |
268 | 276 |
269 Pickle write_pickle; | 277 Pickle write_pickle; |
270 write_pickle.WriteInt(static_cast<int>(status)); | 278 write_pickle.WriteInt(static_cast<int>(status)); |
271 write_pickle.WriteInt(exit_code); | 279 write_pickle.WriteInt(exit_code); |
272 ssize_t written = | 280 ssize_t written = |
273 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())); | 281 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())); |
274 if (written != static_cast<ssize_t>(write_pickle.size())) | 282 if (written != static_cast<ssize_t>(write_pickle.size())) |
275 PLOG(ERROR) << "write"; | 283 PLOG(ERROR) << "write"; |
276 } | 284 } |
277 | 285 |
278 int Zygote::ForkWithRealPid(const std::string& process_type, | 286 int Zygote::ForkWithRealPid(const std::string& process_type, |
279 std::vector<int>& fds, | 287 std::vector<int>& fds, |
280 const std::string& channel_switch, | 288 const std::string& channel_switch, |
281 std::string* uma_name, | 289 std::string* uma_name, |
282 int* uma_sample, | 290 int* uma_sample, |
283 int* uma_boundary_value) { | 291 int* uma_boundary_value) { |
284 const bool use_helper = (helper_ && helper_->CanHelp(process_type, | 292 const bool use_helper = (helper_ && helper_->CanHelp(process_type, |
285 uma_name, | 293 uma_name, |
286 uma_sample, | 294 uma_sample, |
287 uma_boundary_value)); | 295 uma_boundary_value)); |
288 // TODO(jln): this shortcut is silly and does nothing but make the code | |
289 // harder to follow and to test. Get rid of it. | |
290 if (!(use_helper || UsingSUIDSandbox())) { | |
Mark Seaborn
2013/08/21 17:49:33
I assume you've tested the removal of this by runn
jln (very slow on Chromium)
2013/08/21 19:22:32
Yes, that was tested. We still have a few bots wit
| |
291 pid_t pid = fork(); | |
292 if (pid > 0) { | |
293 process_info_map_[pid].internal_pid = pid; | |
294 process_info_map_[pid].started_from_helper = use_helper; | |
295 } | |
296 return pid; | |
297 } | |
298 | |
299 int dummy_fd; | 296 int dummy_fd; |
300 ino_t dummy_inode; | 297 ino_t dummy_inode; |
301 int pipe_fds[2] = { -1, -1 }; | 298 int pipe_fds[2] = { -1, -1 }; |
302 base::ProcessId pid = 0; | 299 base::ProcessId pid = 0; |
303 | 300 |
304 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); | 301 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); |
305 if (dummy_fd < 0) { | 302 if (dummy_fd < 0) { |
306 LOG(ERROR) << "Failed to create dummy FD"; | 303 LOG(ERROR) << "Failed to create dummy FD"; |
307 goto error; | 304 goto error; |
308 } | 305 } |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
383 goto error; | 380 goto error; |
384 } | 381 } |
385 } else { | 382 } else { |
386 // If no SUID sandbox is involved then no pid translation is | 383 // If no SUID sandbox is involved then no pid translation is |
387 // necessary. | 384 // necessary. |
388 real_pid = pid; | 385 real_pid = pid; |
389 } | 386 } |
390 | 387 |
391 // Now set-up this process to be tracked by the Zygote. | 388 // Now set-up this process to be tracked by the Zygote. |
392 if (process_info_map_.find(real_pid) != process_info_map_.end()) { | 389 if (process_info_map_.find(real_pid) != process_info_map_.end()) { |
393 // TODO(jln): add DCHECK. | |
394 LOG(ERROR) << "Already tracking PID " << real_pid; | 390 LOG(ERROR) << "Already tracking PID " << real_pid; |
391 NOTREACHED(); | |
395 } | 392 } |
396 process_info_map_[real_pid].internal_pid = pid; | 393 process_info_map_[real_pid].internal_pid = pid; |
397 process_info_map_[real_pid].started_from_helper = use_helper; | 394 process_info_map_[real_pid].started_from_helper = use_helper; |
398 | 395 |
399 if (use_helper) { | 396 if (use_helper) { |
400 if (!helper_->AckChild(pipe_fds[1], channel_switch)) { | 397 if (!helper_->AckChild(pipe_fds[1], channel_switch)) { |
401 LOG(ERROR) << "Failed to synchronise with zygote fork helper"; | 398 LOG(ERROR) << "Failed to synchronise with zygote fork helper"; |
402 goto error; | 399 goto error; |
403 } | 400 } |
404 } else { | 401 } else { |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
542 PickleIterator iter) { | 539 PickleIterator iter) { |
543 if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_))) != | 540 if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_))) != |
544 sizeof(sandbox_flags_)) { | 541 sizeof(sandbox_flags_)) { |
545 PLOG(ERROR) << "write"; | 542 PLOG(ERROR) << "write"; |
546 } | 543 } |
547 | 544 |
548 return false; | 545 return false; |
549 } | 546 } |
550 | 547 |
551 } // namespace content | 548 } // namespace content |
OLD | NEW |