| Index: base/process_util_posix.cc
|
| diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc
|
| index 21626732b8c23eac4a1d59bfea816dca70c35fc8..a771fa25293cc810026157c129b03a04931cfd80 100644
|
| --- a/base/process_util_posix.cc
|
| +++ b/base/process_util_posix.cc
|
| @@ -21,11 +21,16 @@
|
| #include "base/logging.h"
|
| #include "base/platform_thread.h"
|
| #include "base/process_util.h"
|
| +#include "base/rand_util.h"
|
| #include "base/scoped_ptr.h"
|
| #include "base/sys_info.h"
|
| #include "base/time.h"
|
| #include "base/waitable_event.h"
|
|
|
| +#if defined(OS_MACOSX)
|
| +#include "base/mach_ipc_mac.h"
|
| +#endif
|
| +
|
| const int kMicrosecondsPerSecond = 1000000;
|
|
|
| namespace base {
|
| @@ -280,11 +285,148 @@ void SetAllFDsToCloseOnExec() {
|
| }
|
| }
|
|
|
| +#if defined(OS_MACOSX)
|
| +static std::string MachErrorCode(kern_return_t err) {
|
| + return StringPrintf("0x%x %s", err, mach_error_string(err));
|
| +}
|
| +
|
| +// Forks the current process and returns the child's |task_t| in the parent
|
| +// process.
|
| +static pid_t fork_and_get_task(task_t* child_task) {
|
| + const int kTimeoutMs = 100;
|
| + kern_return_t err;
|
| +
|
| + // Put a random number into the channel name, so that a compromised renderer
|
| + // can't pretend being the child that's forked off.
|
| + std::string mach_connection_name = StringPrintf(
|
| + "com.google.Chrome.samplingfork.%p.%d",
|
| + child_task, base::RandInt(0, std::numeric_limits<int>::max()));
|
| + ReceivePort parent_recv_port(mach_connection_name.c_str());
|
| +
|
| + // Error handling philosophy: If Mach IPC fails, don't touch |child_task| but
|
| + // return a valid pid. If IPC fails in the child, the parent will have to wait
|
| + // until kTimeoutMs is over. This is not optimal, but I've never seen it
|
| + // happen, and stuff should still mostly work.
|
| + pid_t pid = fork();
|
| + switch (pid) {
|
| + case -1:
|
| + return pid;
|
| + case 0: { // child
|
| + ReceivePort child_recv_port;
|
| +
|
| + MachSendMessage child_message(/* id= */0);
|
| + if (!child_message.AddDescriptor(mach_task_self())) {
|
| + LOG(ERROR) << "child AddDescriptor(mach_task_self()) failed.";
|
| + return pid;
|
| + }
|
| + mach_port_t raw_child_recv_port = child_recv_port.GetPort();
|
| + if (!child_message.AddDescriptor(raw_child_recv_port)) {
|
| + LOG(ERROR) << "child AddDescriptor(" << raw_child_recv_port
|
| + << ") failed.";
|
| + return pid;
|
| + }
|
| +
|
| + MachPortSender child_sender(mach_connection_name.c_str());
|
| + err = child_sender.SendMessage(child_message, kTimeoutMs);
|
| + if (err != KERN_SUCCESS) {
|
| + LOG(ERROR) << "child SendMessage() failed: " << MachErrorCode(err);
|
| + return pid;
|
| + }
|
| +
|
| + MachReceiveMessage parent_message;
|
| + err = child_recv_port.WaitForMessage(&parent_message, kTimeoutMs);
|
| + if (err != KERN_SUCCESS) {
|
| + LOG(ERROR) << "child WaitForMessage() failed: " << MachErrorCode(err);
|
| + return pid;
|
| + }
|
| +
|
| + if (parent_message.GetTranslatedPort(0) == MACH_PORT_NULL) {
|
| + LOG(ERROR) << "child GetTranslatedPort(0) failed.";
|
| + return pid;
|
| + }
|
| + err = task_set_bootstrap_port(mach_task_self(),
|
| + parent_message.GetTranslatedPort(0));
|
| + if (err != KERN_SUCCESS) {
|
| + LOG(ERROR) << "child task_set_bootstrap_port() failed: "
|
| + << MachErrorCode(err);
|
| + return pid;
|
| + }
|
| + break;
|
| + }
|
| + default: { // parent
|
| + MachReceiveMessage child_message;
|
| + err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs);
|
| + if (err != KERN_SUCCESS) {
|
| + LOG(ERROR) << "parent WaitForMessage() failed: " << MachErrorCode(err);
|
| + return pid;
|
| + }
|
| +
|
| + if (child_message.GetTranslatedPort(0) == MACH_PORT_NULL) {
|
| + LOG(ERROR) << "parent GetTranslatedPort(0) failed.";
|
| + return pid;
|
| + }
|
| + *child_task = child_message.GetTranslatedPort(0);
|
| +
|
| + if (child_message.GetTranslatedPort(1) == MACH_PORT_NULL) {
|
| + LOG(ERROR) << "parent GetTranslatedPort(1) failed.";
|
| + return pid;
|
| + }
|
| + MachPortSender parent_sender(child_message.GetTranslatedPort(1));
|
| +
|
| + MachSendMessage parent_message(/* id= */0);
|
| + if (!parent_message.AddDescriptor(bootstrap_port)) {
|
| + LOG(ERROR) << "parent AddDescriptor(" << bootstrap_port << ") failed.";
|
| + return pid;
|
| + }
|
| +
|
| + err = parent_sender.SendMessage(parent_message, kTimeoutMs);
|
| + if (err != KERN_SUCCESS) {
|
| + LOG(ERROR) << "parent SendMessage() failed: " << MachErrorCode(err);
|
| + return pid;
|
| + }
|
| + break;
|
| + }
|
| + }
|
| + return pid;
|
| +}
|
| +
|
| bool LaunchApp(const std::vector<std::string>& argv,
|
| const environment_vector& environ,
|
| const file_handle_mapping_vector& fds_to_remap,
|
| bool wait, ProcessHandle* process_handle) {
|
| - pid_t pid = fork();
|
| + return LaunchAppAndGetTask(
|
| + argv, environ, fds_to_remap, wait, NULL, process_handle);
|
| +}
|
| +#endif // defined(OS_MACOSX)
|
| +
|
| +#if defined(OS_MACOSX)
|
| +bool LaunchAppAndGetTask(
|
| +#else
|
| +bool LaunchApp(
|
| +#endif
|
| + const std::vector<std::string>& argv,
|
| + const environment_vector& environ,
|
| + const file_handle_mapping_vector& fds_to_remap,
|
| + bool wait,
|
| +#if defined(OS_MACOSX)
|
| + task_t* task_handle,
|
| +#endif
|
| + ProcessHandle* process_handle) {
|
| + pid_t pid;
|
| +#if defined(OS_MACOSX)
|
| + if (task_handle == NULL) {
|
| + pid = fork();
|
| + } else {
|
| + // On OS X, the task_t for a process is needed for several reasons. Sadly,
|
| + // the function task_for_pid() requires privileges a normal user doesn't
|
| + // have. Instead, a short-lived Mach IPC connection is opened between parent
|
| + // and child, and the child sends its task_t to the parent at fork time.
|
| + *task_handle = MACH_PORT_NULL;
|
| + pid = fork_and_get_task(task_handle);
|
| + }
|
| +#else
|
| + pid = fork();
|
| +#endif
|
| if (pid < 0)
|
| return false;
|
|
|
|
|