OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 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 <dirent.h> | 5 #include <dirent.h> |
6 #include <errno.h> | 6 #include <errno.h> |
7 #include <fcntl.h> | 7 #include <fcntl.h> |
8 #include <signal.h> | 8 #include <signal.h> |
9 #include <stdlib.h> | 9 #include <stdlib.h> |
10 #include <sys/resource.h> | 10 #include <sys/resource.h> |
11 #include <sys/time.h> | 11 #include <sys/time.h> |
12 #include <sys/types.h> | 12 #include <sys/types.h> |
13 #include <sys/wait.h> | 13 #include <sys/wait.h> |
14 #include <unistd.h> | 14 #include <unistd.h> |
15 | 15 |
16 #include <limits> | 16 #include <limits> |
17 #include <set> | 17 #include <set> |
18 | 18 |
19 #include "base/debug_util.h" | 19 #include "base/debug_util.h" |
20 #include "base/eintr_wrapper.h" | 20 #include "base/eintr_wrapper.h" |
21 #include "base/logging.h" | 21 #include "base/logging.h" |
22 #include "base/platform_thread.h" | 22 #include "base/platform_thread.h" |
23 #include "base/process_util.h" | 23 #include "base/process_util.h" |
| 24 #include "base/rand_util.h" |
24 #include "base/scoped_ptr.h" | 25 #include "base/scoped_ptr.h" |
25 #include "base/sys_info.h" | 26 #include "base/sys_info.h" |
26 #include "base/time.h" | 27 #include "base/time.h" |
27 #include "base/waitable_event.h" | 28 #include "base/waitable_event.h" |
28 | 29 |
| 30 #if defined(OS_MACOSX) |
| 31 #include "base/mach_ipc_mac.h" |
| 32 #endif |
| 33 |
29 const int kMicrosecondsPerSecond = 1000000; | 34 const int kMicrosecondsPerSecond = 1000000; |
30 | 35 |
31 namespace base { | 36 namespace base { |
32 | 37 |
33 namespace { | 38 namespace { |
34 | 39 |
35 int WaitpidWithTimeout(ProcessHandle handle, int64 wait_milliseconds, | 40 int WaitpidWithTimeout(ProcessHandle handle, int64 wait_milliseconds, |
36 bool* success) { | 41 bool* success) { |
37 // This POSIX version of this function only guarantees that we wait no less | 42 // This POSIX version of this function only guarantees that we wait no less |
38 // than |wait_milliseconds| for the proces to exit. The child process may | 43 // than |wait_milliseconds| for the proces to exit. The child process may |
(...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
273 if (i <= STDERR_FILENO) | 278 if (i <= STDERR_FILENO) |
274 continue; | 279 continue; |
275 | 280 |
276 int flags = fcntl(i, F_GETFD); | 281 int flags = fcntl(i, F_GETFD); |
277 if ((flags == -1) || (fcntl(i, F_SETFD, flags | FD_CLOEXEC) == -1)) { | 282 if ((flags == -1) || (fcntl(i, F_SETFD, flags | FD_CLOEXEC) == -1)) { |
278 DLOG(ERROR) << "fcntl failure."; | 283 DLOG(ERROR) << "fcntl failure."; |
279 } | 284 } |
280 } | 285 } |
281 } | 286 } |
282 | 287 |
| 288 #if defined(OS_MACOSX) |
| 289 static std::string MachErrorCode(kern_return_t err) { |
| 290 return StringPrintf("0x%x %s", err, mach_error_string(err)); |
| 291 } |
| 292 |
| 293 // Forks the current process and returns the child's |task_t| in the parent |
| 294 // process. |
| 295 static pid_t fork_and_get_task(task_t* child_task) { |
| 296 const int kTimeoutMs = 100; |
| 297 kern_return_t err; |
| 298 |
| 299 // Put a random number into the channel name, so that a compromised renderer |
| 300 // can't pretend being the child that's forked off. |
| 301 std::string mach_connection_name = StringPrintf( |
| 302 "com.google.Chrome.samplingfork.%p.%d", |
| 303 child_task, base::RandInt(0, std::numeric_limits<int>::max())); |
| 304 ReceivePort parent_recv_port(mach_connection_name.c_str()); |
| 305 |
| 306 // Error handling philosophy: If Mach IPC fails, don't touch |child_task| but |
| 307 // return a valid pid. If IPC fails in the child, the parent will have to wait |
| 308 // until kTimeoutMs is over. This is not optimal, but I've never seen it |
| 309 // happen, and stuff should still mostly work. |
| 310 pid_t pid = fork(); |
| 311 switch (pid) { |
| 312 case -1: |
| 313 return pid; |
| 314 case 0: { // child |
| 315 ReceivePort child_recv_port; |
| 316 |
| 317 MachSendMessage child_message(/* id= */0); |
| 318 if (!child_message.AddDescriptor(mach_task_self())) { |
| 319 LOG(ERROR) << "child AddDescriptor(mach_task_self()) failed."; |
| 320 return pid; |
| 321 } |
| 322 mach_port_t raw_child_recv_port = child_recv_port.GetPort(); |
| 323 if (!child_message.AddDescriptor(raw_child_recv_port)) { |
| 324 LOG(ERROR) << "child AddDescriptor(" << raw_child_recv_port |
| 325 << ") failed."; |
| 326 return pid; |
| 327 } |
| 328 |
| 329 MachPortSender child_sender(mach_connection_name.c_str()); |
| 330 err = child_sender.SendMessage(child_message, kTimeoutMs); |
| 331 if (err != KERN_SUCCESS) { |
| 332 LOG(ERROR) << "child SendMessage() failed: " << MachErrorCode(err); |
| 333 return pid; |
| 334 } |
| 335 |
| 336 MachReceiveMessage parent_message; |
| 337 err = child_recv_port.WaitForMessage(&parent_message, kTimeoutMs); |
| 338 if (err != KERN_SUCCESS) { |
| 339 LOG(ERROR) << "child WaitForMessage() failed: " << MachErrorCode(err); |
| 340 return pid; |
| 341 } |
| 342 |
| 343 if (parent_message.GetTranslatedPort(0) == MACH_PORT_NULL) { |
| 344 LOG(ERROR) << "child GetTranslatedPort(0) failed."; |
| 345 return pid; |
| 346 } |
| 347 err = task_set_bootstrap_port(mach_task_self(), |
| 348 parent_message.GetTranslatedPort(0)); |
| 349 if (err != KERN_SUCCESS) { |
| 350 LOG(ERROR) << "child task_set_bootstrap_port() failed: " |
| 351 << MachErrorCode(err); |
| 352 return pid; |
| 353 } |
| 354 break; |
| 355 } |
| 356 default: { // parent |
| 357 MachReceiveMessage child_message; |
| 358 err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs); |
| 359 if (err != KERN_SUCCESS) { |
| 360 LOG(ERROR) << "parent WaitForMessage() failed: " << MachErrorCode(err); |
| 361 return pid; |
| 362 } |
| 363 |
| 364 if (child_message.GetTranslatedPort(0) == MACH_PORT_NULL) { |
| 365 LOG(ERROR) << "parent GetTranslatedPort(0) failed."; |
| 366 return pid; |
| 367 } |
| 368 *child_task = child_message.GetTranslatedPort(0); |
| 369 |
| 370 if (child_message.GetTranslatedPort(1) == MACH_PORT_NULL) { |
| 371 LOG(ERROR) << "parent GetTranslatedPort(1) failed."; |
| 372 return pid; |
| 373 } |
| 374 MachPortSender parent_sender(child_message.GetTranslatedPort(1)); |
| 375 |
| 376 MachSendMessage parent_message(/* id= */0); |
| 377 if (!parent_message.AddDescriptor(bootstrap_port)) { |
| 378 LOG(ERROR) << "parent AddDescriptor(" << bootstrap_port << ") failed."; |
| 379 return pid; |
| 380 } |
| 381 |
| 382 err = parent_sender.SendMessage(parent_message, kTimeoutMs); |
| 383 if (err != KERN_SUCCESS) { |
| 384 LOG(ERROR) << "parent SendMessage() failed: " << MachErrorCode(err); |
| 385 return pid; |
| 386 } |
| 387 break; |
| 388 } |
| 389 } |
| 390 return pid; |
| 391 } |
| 392 |
283 bool LaunchApp(const std::vector<std::string>& argv, | 393 bool LaunchApp(const std::vector<std::string>& argv, |
284 const environment_vector& environ, | 394 const environment_vector& environ, |
285 const file_handle_mapping_vector& fds_to_remap, | 395 const file_handle_mapping_vector& fds_to_remap, |
286 bool wait, ProcessHandle* process_handle) { | 396 bool wait, ProcessHandle* process_handle) { |
287 pid_t pid = fork(); | 397 return LaunchAppAndGetTask( |
| 398 argv, environ, fds_to_remap, wait, NULL, process_handle); |
| 399 } |
| 400 #endif // defined(OS_MACOSX) |
| 401 |
| 402 #if defined(OS_MACOSX) |
| 403 bool LaunchAppAndGetTask( |
| 404 #else |
| 405 bool LaunchApp( |
| 406 #endif |
| 407 const std::vector<std::string>& argv, |
| 408 const environment_vector& environ, |
| 409 const file_handle_mapping_vector& fds_to_remap, |
| 410 bool wait, |
| 411 #if defined(OS_MACOSX) |
| 412 task_t* task_handle, |
| 413 #endif |
| 414 ProcessHandle* process_handle) { |
| 415 pid_t pid; |
| 416 #if defined(OS_MACOSX) |
| 417 if (task_handle == NULL) { |
| 418 pid = fork(); |
| 419 } else { |
| 420 // On OS X, the task_t for a process is needed for several reasons. Sadly, |
| 421 // the function task_for_pid() requires privileges a normal user doesn't |
| 422 // have. Instead, a short-lived Mach IPC connection is opened between parent |
| 423 // and child, and the child sends its task_t to the parent at fork time. |
| 424 *task_handle = MACH_PORT_NULL; |
| 425 pid = fork_and_get_task(task_handle); |
| 426 } |
| 427 #else |
| 428 pid = fork(); |
| 429 #endif |
288 if (pid < 0) | 430 if (pid < 0) |
289 return false; | 431 return false; |
290 | 432 |
291 if (pid == 0) { | 433 if (pid == 0) { |
292 // Child process | 434 // Child process |
293 #if defined(OS_MACOSX) | 435 #if defined(OS_MACOSX) |
294 RestoreDefaultExceptionHandler(); | 436 RestoreDefaultExceptionHandler(); |
295 #endif | 437 #endif |
296 | 438 |
297 InjectiveMultimap fd_shuffle; | 439 InjectiveMultimap fd_shuffle; |
(...skipping 379 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
677 const ProcessFilter* filter) { | 819 const ProcessFilter* filter) { |
678 bool exited_cleanly = | 820 bool exited_cleanly = |
679 WaitForProcessesToExit(executable_name, wait_milliseconds, | 821 WaitForProcessesToExit(executable_name, wait_milliseconds, |
680 filter); | 822 filter); |
681 if (!exited_cleanly) | 823 if (!exited_cleanly) |
682 KillProcesses(executable_name, exit_code, filter); | 824 KillProcesses(executable_name, exit_code, filter); |
683 return exited_cleanly; | 825 return exited_cleanly; |
684 } | 826 } |
685 | 827 |
686 } // namespace base | 828 } // namespace base |
OLD | NEW |