Bug 592951: Use 'posix_spawnp' to launch child processes on Mac OS X. r=ted a=blocking-b6+
authorJosh Aas <joshmoz@gmail.com>
Fri, 10 Sep 2010 13:09:30 -0400
changeset 52392 ab61a67aebb01c3396dbb136f94a23699702405f
parent 52391 c24925eb966c686f3aa97617ed5818a1e9431f22
child 52393 f9fcf102493f6bc2a4c9dac8fc004ffdb92a4b63
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersted, blocking-b6
bugs592951
milestone2.0b6pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 592951: Use 'posix_spawnp' to launch child processes on Mac OS X. r=ted a=blocking-b6+
ipc/app/MozillaRuntimeMain.cpp
ipc/chromium/src/base/process_util.h
ipc/chromium/src/base/process_util_mac.mm
ipc/glue/GeckoChildProcessHost.cpp
toolkit/xre/nsEmbedFunctions.cpp
--- a/ipc/app/MozillaRuntimeMain.cpp
+++ b/ipc/app/MozillaRuntimeMain.cpp
@@ -49,43 +49,23 @@
 #include <windows.h>
 // we want a wmain entry point
 #include "nsWindowsWMain.cpp"
 #endif
 
 int
 main(int argc, char* argv[])
 {
-#if defined(MOZ_CRASHREPORTER)
-    if (argc < 2)
-        return 1;
-    const char* const crashReporterArg = argv[--argc];
-
-#  if defined(XP_WIN) || defined(XP_MACOSX)
-    // on windows and mac, |crashReporterArg| is the named pipe on which the
-    // server is listening for requests, or "-" if crash reporting is
-    // disabled.
-    if (0 != strcmp("-", crashReporterArg)
-        && !XRE_SetRemoteExceptionHandler(crashReporterArg))
-        return 1;
-#  elif defined(OS_LINUX)
-    // on POSIX, |crashReporterArg| is "true" if crash reporting is
-    // enabled, false otherwise
-    if (0 != strcmp("false", crashReporterArg)
-        && !XRE_SetRemoteExceptionHandler(NULL))
-        return 1;
-#  else
-#    error "OOP crash reporting unsupported on this platform"
-#  endif   
-#endif // if defined(MOZ_CRASHREPORTER)
-
 #if defined(XP_WIN) && defined(DEBUG_bent)
     MessageBox(NULL, L"Hi", L"Hi", MB_OK);
 #endif
 
-    GeckoProcessType proctype =
-        XRE_StringToChildProcessType(argv[argc - 1]);
+    // Check for the absolute minimum number of args we need to move
+    // forward here. We expect the last arg to be the child process type.
+    if (argc < 1)
+      return 1;
+    GeckoProcessType proctype = XRE_StringToChildProcessType(argv[--argc]);
 
-    nsresult rv = XRE_InitChildProcess(argc - 1, argv, proctype);
+    nsresult rv = XRE_InitChildProcess(argc, argv, proctype);
     NS_ENSURE_SUCCESS(rv, 1);
 
     return 0;
 }
--- a/ipc/chromium/src/base/process_util.h
+++ b/ipc/chromium/src/base/process_util.h
@@ -126,46 +126,27 @@ bool LaunchApp(const std::wstring& cmdli
 // close-on-exec.  |fds_to_remap| defines a mapping of src fd->dest fd to
 // propagate FDs into the child process.
 //
 // As above, if wait is true, execute synchronously. The pid will be stored
 // in process_handle if that pointer is non-null.
 //
 // Note that the first argument in argv must point to the filename,
 // and must be fully specified.
-#ifdef OS_MACOSX
-typedef std::vector<std::pair<int, int> > file_handle_mapping_vector;
-bool LaunchApp(const std::vector<std::string>& argv,
-               const file_handle_mapping_vector& fds_to_remap,
-               bool wait, ProcessHandle* process_handle,
-               task_t* task_handle);
-
-#if defined(OS_LINUX) || defined(OS_MACOSX)
-typedef std::map<std::string, std::string> environment_map;
-bool LaunchApp(const std::vector<std::string>& argv,
-               const file_handle_mapping_vector& fds_to_remap,
-               const environment_map& env_vars_to_set,
-               bool wait, ProcessHandle* process_handle,
-               task_t* task_handle);
-#endif
-#else // !OS_MACOSX
 typedef std::vector<std::pair<int, int> > file_handle_mapping_vector;
 bool LaunchApp(const std::vector<std::string>& argv,
                const file_handle_mapping_vector& fds_to_remap,
                bool wait, ProcessHandle* process_handle);
 
-#if defined(OS_LINUX) || defined(OS_MACOSX)
 typedef std::map<std::string, std::string> environment_map;
 bool LaunchApp(const std::vector<std::string>& argv,
                const file_handle_mapping_vector& fds_to_remap,
                const environment_map& env_vars_to_set,
                bool wait, ProcessHandle* process_handle);
 #endif
-#endif
-#endif
 
 // Executes the application specified by cl. This function delegates to one
 // of the above two platform-specific functions.
 bool LaunchApp(const CommandLine& cl,
                bool wait, bool start_hidden, ProcessHandle* process_handle);
 
 #if defined(OS_WIN)
 // Executes the application specified by |cmd_line| and copies the contents
--- a/ipc/chromium/src/base/process_util_mac.mm
+++ b/ipc/chromium/src/base/process_util_mac.mm
@@ -12,217 +12,139 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 
 #include <string>
 
 #include "base/eintr_wrapper.h"
 #include "base/logging.h"
 #include "base/rand_util.h"
-#include "base/scoped_ptr.h"
 #include "base/string_util.h"
 #include "base/time.h"
-#include "chrome/common/mach_ipc_mac.h"
 
 namespace base {
 
-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(
-      "org.mozilla.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 file_handle_mapping_vector& fds_to_remap,
+               bool wait, ProcessHandle* process_handle) {
+  return LaunchApp(argv, fds_to_remap, environment_map(),
+                   wait, process_handle);
 }
 
 bool LaunchApp(const std::vector<std::string>& argv,
                const file_handle_mapping_vector& fds_to_remap,
-               bool wait, ProcessHandle* process_handle,
-               task_t* process_task) {
-  return LaunchApp(argv, fds_to_remap, environment_map(),
-                   wait, process_handle, process_task);
-}
+               const environment_map& env_vars_to_set,
+               bool wait, ProcessHandle* process_handle) {
+  bool retval = true;
+
+  char* argv_copy[argv.size() + 1];
+  for (size_t i = 0; i < argv.size(); i++) {
+    argv_copy[i] = const_cast<char*>(argv[i].c_str());
+  }
+  argv_copy[argv.size()] = NULL;
+
+  // Make sure we don't leak any FDs to the child process by marking all FDs
+  // as close-on-exec.
+  SetAllFDsToCloseOnExec();
 
-bool LaunchApp(
-    const std::vector<std::string>& argv,
-    const file_handle_mapping_vector& fds_to_remap,
-    const environment_map& environ,
-    bool wait,
-    ProcessHandle* process_handle,
-    task_t* task_handle) {
-  pid_t pid;
+  // Copy _NSGetEnviron() to a new char array and add the variables
+  // in env_vars_to_set.
+  // Existing variables are overwritten by env_vars_to_set.
+  int pos = 0;
+  environment_map combined_env_vars = env_vars_to_set;
+  while((*_NSGetEnviron())[pos] != NULL) {
+    std::string varString = (*_NSGetEnviron())[pos];
+    std::string varName = varString.substr(0, varString.find_first_of('='));
+    std::string varValue = varString.substr(varString.find_first_of('=') + 1);
+    if (combined_env_vars.find(varName) == combined_env_vars.end()) {
+      combined_env_vars[varName] = varValue;
+    }
+    pos++;
+  }
+  int varsLen = combined_env_vars.size() + 1;
 
-  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);
+  char** vars = new char*[varsLen];
+  int i = 0;
+  for (environment_map::const_iterator it = combined_env_vars.begin();
+       it != combined_env_vars.end(); ++it) {
+    std::string entry(it->first);
+    entry += "=";
+    entry += it->second;
+    vars[i] = strdup(entry.c_str());
+    i++;
+  }
+  vars[i] = NULL;
+
+  posix_spawn_file_actions_t file_actions;
+  if (posix_spawn_file_actions_init(&file_actions) != 0) {
+    for(int j = 0; j < varsLen; j++) {
+      free(vars[j]);
+    }  
+    delete[] vars;
+    return false;
   }
 
-  if (pid < 0)
-    return false;
-
-  if (pid == 0) {
-    // Child process
+  // Turn fds_to_remap array into a set of dup2 calls.
+  for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin();
+       it != fds_to_remap.end();
+       ++it) {
+    int src_fd = it->first;
+    int dest_fd = it->second;
 
-    InjectiveMultimap fd_shuffle;
-    for (file_handle_mapping_vector::const_iterator
-        it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) {
-      fd_shuffle.push_back(InjectionArc(it->first, it->second, false));
-    }
-
-    for (environment_map::const_iterator it = environ.begin();
-         it != environ.end(); ++it) {
-      if (it->first.empty())
-        continue;
-
-      if (it->second.empty()) {
-        unsetenv(it->first.c_str());
-      } else {
-        setenv(it->first.c_str(), it->second.c_str(), 1);
+    if (src_fd == dest_fd) {
+      int flags = fcntl(src_fd, F_GETFD);
+      if (flags != -1) {
+        fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC);
+      }
+    } else {
+      if (posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd) != 0) {
+        posix_spawn_file_actions_destroy(&file_actions);
+        for(int j = 0; j < varsLen; j++) {
+          free(vars[j]);
+        }  
+        delete[] vars;
+        return false;
       }
     }
+  }
 
-    // Obscure fork() rule: in the child, if you don't end up doing exec*(),
-    // you call _exit() instead of exit(). This is because _exit() does not
-    // call any previously-registered (in the parent) exit handlers, which
-    // might do things like block waiting for threads that don't even exist
-    // in the child.
-    if (!ShuffleFileDescriptors(fd_shuffle))
-      _exit(127);
+  int pid = 0;
+  int spawn_succeeded = (posix_spawnp(&pid,
+                                      argv_copy[0],
+                                      &file_actions,
+                                      NULL,
+                                      argv_copy,
+                                      vars) == 0);
 
-    // If we are using the SUID sandbox, it sets a magic environment variable
-    // ("SBX_D"), so we remove that variable from the environment here on the
-    // off chance that it's already set.
-    unsetenv("SBX_D");
-
-    CloseSuperfluousFds(fd_shuffle);
+  for(int j = 0; j < varsLen; j++) {
+    free(vars[j]);
+  }  
+  delete[] vars;
 
-    scoped_array<char*> argv_cstr(new char*[argv.size() + 1]);
-    for (size_t i = 0; i < argv.size(); i++)
-      argv_cstr[i] = const_cast<char*>(argv[i].c_str());
-    argv_cstr[argv.size()] = NULL;
-    execvp(argv_cstr[0], argv_cstr.get());
-    _exit(127);
+  posix_spawn_file_actions_destroy(&file_actions);
+
+  bool process_handle_valid = pid > 0;
+  if (!spawn_succeeded || !process_handle_valid) {
+    retval = false;
   } else {
-    // Parent process
     if (wait)
       HANDLE_EINTR(waitpid(pid, 0, 0));
 
     if (process_handle)
       *process_handle = pid;
   }
 
-  return true;
+  return retval;
 }
 
 bool LaunchApp(const CommandLine& cl,
                bool wait, bool start_hidden, ProcessHandle* process_handle) {
   // TODO(playmobil): Do we need to respect the start_hidden flag?
   file_handle_mapping_vector no_files;
-  return LaunchApp(cl.argv(), no_files, wait, process_handle, NULL);
+  return LaunchApp(cl.argv(), no_files, wait, process_handle);
 }
 
 NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name,
                                            const ProcessFilter* filter)
   : executable_name_(executable_name),
     index_of_kinfo_proc_(0),
     filter_(filter) {
   // Get a snapshot of all of my processes (yes, as we loop it can go stale, but
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -38,16 +38,20 @@
 
 #include "GeckoChildProcessHost.h"
 
 #include "base/command_line.h"
 #include "base/path_service.h"
 #include "base/string_util.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/process_watcher.h"
+#ifdef XP_MACOSX
+#include "chrome/common/mach_ipc_mac.h"
+#include "base/rand_util.h"
+#endif
 
 #include "prprf.h"
 
 #if defined(OS_LINUX)
 #  define XP_LINUX 1
 #endif
 #include "nsExceptionHandler.h"
 
@@ -219,19 +223,16 @@ GeckoChildProcessHost::PerformAsyncLaunc
 
   // We rely on the fact that InitializeChannel() has already been processed
   // on the IO thread before this point is reached.
   if (!GetChannel()) {
     return false;
   }
 
   base::ProcessHandle process;
-#if defined(XP_MACOSX)
-  task_t child_task;
-#endif
 
   // send the child the PID so that it can open a ProcessHandle back to us.
   // probably don't want to do this in the long run
   char pidstring[32];
   PR_snprintf(pidstring, sizeof(pidstring) - 1,
 	      "%ld", base::Process::Current().pid());
 
   const char* const childProcessType =
@@ -306,17 +307,16 @@ GeckoChildProcessHost::PerformAsyncLaunc
   if (mozilla::OmnijarPath()) {
     mozilla::OmnijarPath()->GetNativePath(omnijarPath);
     childArgv.push_back("-omnijar");
     childArgv.push_back(omnijarPath.get());
   }
 #endif
 
   childArgv.push_back(pidstring);
-  childArgv.push_back(childProcessType);
 
 #if defined(MOZ_CRASHREPORTER)
 #  if defined(OS_LINUX)
   int childCrashFd, childCrashRemapFd;
   if (!CrashReporter::CreateNotificationPipeForChild(
         &childCrashFd, &childCrashRemapFd))
     return false;
   if (0 <= childCrashFd) {
@@ -328,25 +328,73 @@ GeckoChildProcessHost::PerformAsyncLaunc
     // "false" == crash reporting disabled
     childArgv.push_back("false");
   }
 #  elif defined(XP_MACOSX)
   childArgv.push_back(CrashReporter::GetChildNotificationPipe());
 #  endif  // OS_LINUX
 #endif
 
+#ifdef XP_MACOSX
+  // Add a mach port to the command line so the child can communicate its
+  // 'task_t' back to the parent.
+  //
+  // 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("org.mozilla.machname.%d",
+                                                  base::RandInt(0, std::numeric_limits<int>::max()));
+  childArgv.push_back(mach_connection_name.c_str());
+#endif
+
+  childArgv.push_back(childProcessType);
+
   base::LaunchApp(childArgv, mFileMap,
 #if defined(OS_LINUX) || defined(OS_MACOSX)
                   newEnvVars,
 #endif
-                  false, &process
-#if defined(XP_MACOSX)
-                  , &child_task
+                  false, &process);
+
+#ifdef XP_MACOSX
+  // Wait for the child process to send us its 'task_t' data.
+  const int kTimeoutMs = 1000;
+
+  MachReceiveMessage child_message;
+  ReceivePort parent_recv_port(mach_connection_name.c_str());
+  kern_return_t err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs);
+  if (err != KERN_SUCCESS) {
+    std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err));
+    LOG(ERROR) << "parent WaitForMessage() failed: " << errString;
+    return false;
+  }
+
+  task_t child_task = child_message.GetTranslatedPort(0);
+  if (child_task == MACH_PORT_NULL) {
+    LOG(ERROR) << "parent GetTranslatedPort(0) failed.";
+    return false;
+  }
+
+  if (child_message.GetTranslatedPort(1) == MACH_PORT_NULL) {
+    LOG(ERROR) << "parent GetTranslatedPort(1) failed.";
+    return false;
+  }
+  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 false;
+  }
+
+  err = parent_sender.SendMessage(parent_message, kTimeoutMs);
+  if (err != KERN_SUCCESS) {
+    std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err));
+    LOG(ERROR) << "parent SendMessage() failed: " << errString;
+    return false;
+  }
 #endif
-                  );
 
 //--------------------------------------------------
 #elif defined(OS_WIN)
 
   FilePath exePath =
     FilePath::FromWStringHack(CommandLine::ForCurrentProcess()->program());
   exePath = exePath.DirName();
 
@@ -370,22 +418,24 @@ GeckoChildProcessHost::PerformAsyncLaunc
   if (mozilla::OmnijarPath()) {
     mozilla::OmnijarPath()->GetPath(omnijarPath);
     cmdLine.AppendLooseValue(UTF8ToWide("-omnijar"));
     cmdLine.AppendLooseValue(omnijarPath.get());
   }
 #endif
 
   cmdLine.AppendLooseValue(UTF8ToWide(pidstring));
-  cmdLine.AppendLooseValue(UTF8ToWide(childProcessType));
+
 #if defined(MOZ_CRASHREPORTER)
   cmdLine.AppendLooseValue(
     UTF8ToWide(CrashReporter::GetChildNotificationPipe()));
 #endif
 
+  cmdLine.AppendLooseValue(UTF8ToWide(childProcessType));
+
   base::LaunchApp(cmdLine, false, false, &process);
 
 #else
 #  error Sorry
 #endif
 
   if (!process) {
     return false;
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -70,16 +70,19 @@
 #include "nsExceptionHandler.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsWidgetsCID.h"
 #include "nsXREDirProvider.h"
 
 #include "mozilla/Omnijar.h"
 #ifdef MOZ_IPC
+#if defined(XP_MACOSX)
+#include "chrome/common/mach_ipc_mac.h"
+#endif
 #include "nsX11ErrorHandler.h"
 #include "base/at_exit.h"
 #include "base/command_line.h"
 #include "base/message_loop.h"
 #include "base/process_util.h"
 #include "chrome/common/child_process.h"
 #include "chrome/common/notification_service.h"
 
@@ -304,16 +307,87 @@ XRE_InitChildProcess(int aArgc,
                      GeckoProcessType aProcess)
 {
   NS_ENSURE_ARG_MIN(aArgc, 2);
   NS_ENSURE_ARG_POINTER(aArgv);
   NS_ENSURE_ARG_POINTER(aArgv[0]);
 
   sChildProcessType = aProcess;
 
+  // Complete 'task_t' exchange for Mac OS X. This structure has the same size
+  // regardless of architecture so we don't have any cross-arch issues here.
+#ifdef XP_MACOSX
+  if (aArgc < 1)
+    return 1;
+  const char* const mach_port_name = aArgv[--aArgc];
+
+  const int kTimeoutMs = 1000;
+
+  MachSendMessage child_message(0);
+  if (!child_message.AddDescriptor(mach_task_self())) {
+    NS_WARNING("child AddDescriptor(mach_task_self()) failed.");
+    return 1;
+  }
+
+  ReceivePort child_recv_port;
+  mach_port_t raw_child_recv_port = child_recv_port.GetPort();
+  if (!child_message.AddDescriptor(raw_child_recv_port)) {
+    NS_WARNING("Adding descriptor to message failed");
+    return 1;
+  }
+
+  MachPortSender child_sender(mach_port_name);
+  kern_return_t err = child_sender.SendMessage(child_message, kTimeoutMs);
+  if (err != KERN_SUCCESS) {
+    NS_WARNING("child SendMessage() failed");
+    return 1;
+  }
+
+  MachReceiveMessage parent_message;
+  err = child_recv_port.WaitForMessage(&parent_message, kTimeoutMs);
+  if (err != KERN_SUCCESS) {
+    NS_WARNING("child WaitForMessage() failed");
+    return 1;
+  }
+
+  if (parent_message.GetTranslatedPort(0) == MACH_PORT_NULL) {
+    NS_WARNING("child GetTranslatedPort(0) failed");
+    return 1;
+  }
+  err = task_set_bootstrap_port(mach_task_self(),
+                                parent_message.GetTranslatedPort(0));
+  if (err != KERN_SUCCESS) {
+    NS_WARNING("child task_set_bootstrap_port() failed");
+    return 1;
+  }
+#endif
+  
+#if defined(MOZ_CRASHREPORTER)
+  if (aArgc < 1)
+    return 1;
+  const char* const crashReporterArg = aArgv[--aArgc];
+  
+#  if defined(XP_WIN) || defined(XP_MACOSX)
+  // on windows and mac, |crashReporterArg| is the named pipe on which the
+  // server is listening for requests, or "-" if crash reporting is
+  // disabled.
+  if (0 != strcmp("-", crashReporterArg)
+      && !XRE_SetRemoteExceptionHandler(crashReporterArg))
+    return 1;
+#  elif defined(OS_LINUX)
+  // on POSIX, |crashReporterArg| is "true" if crash reporting is
+  // enabled, false otherwise
+  if (0 != strcmp("false", crashReporterArg)
+      && !XRE_SetRemoteExceptionHandler(NULL))
+    return 1;
+#  else
+#    error "OOP crash reporting unsupported on this platform"
+#  endif   
+#endif // if defined(MOZ_CRASHREPORTER)
+
   gArgv = aArgv;
   gArgc = aArgc;
 
   SetupErrorHandling(aArgv[0]);
   
 #if defined(MOZ_WIDGET_GTK2)
   g_thread_init(NULL);
 #endif