summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/mac/scoped_mach_port.cc5
-rw-r--r--base/mac/scoped_mach_port.h2
-rw-r--r--base/process_util.h40
-rw-r--r--base/process_util_posix.cc93
-rw-r--r--content/app/content_main_runner.cc28
-rw-r--r--content/browser/child_process_launcher.cc36
-rw-r--r--content/browser/mach_broker_mac.cc232
-rw-r--r--content/browser/mach_broker_mac.h55
-rw-r--r--content/browser/mach_broker_mac.mm309
-rw-r--r--content/browser/mach_broker_mac_unittest.cc16
-rw-r--r--content/content_browser.gypi7
11 files changed, 447 insertions, 376 deletions
diff --git a/base/mac/scoped_mach_port.cc b/base/mac/scoped_mach_port.cc
index 9e45a85..652e3f4 100644
--- a/base/mac/scoped_mach_port.cc
+++ b/base/mac/scoped_mach_port.cc
@@ -11,14 +11,9 @@ ScopedMachPort::ScopedMachPort(mach_port_t port) : port_(port) {
}
ScopedMachPort::~ScopedMachPort() {
- reset();
-}
-
-void ScopedMachPort::reset(mach_port_t port) {
if (port_ != MACH_PORT_NULL) {
mach_port_deallocate(mach_task_self(), port_);
}
- port_ = port;
}
} // namespace mac
diff --git a/base/mac/scoped_mach_port.h b/base/mac/scoped_mach_port.h
index cc2ef20..d2aa2f7 100644
--- a/base/mac/scoped_mach_port.h
+++ b/base/mac/scoped_mach_port.h
@@ -22,8 +22,6 @@ class BASE_EXPORT ScopedMachPort {
~ScopedMachPort();
- void reset(mach_port_t port = MACH_PORT_NULL);
-
operator mach_port_t() const {
return port_;
}
diff --git a/base/process_util.h b/base/process_util.h
index a06f689..54d8533 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -232,6 +232,13 @@ BASE_EXPORT void CloseSuperfluousFds(const InjectiveMultimap& saved_map);
typedef std::vector<std::pair<std::string, std::string> > EnvironmentVector;
typedef std::vector<std::pair<int, int> > FileHandleMappingVector;
+#if defined(OS_MACOSX)
+// Used with LaunchOptions::synchronize and LaunchSynchronize, a
+// LaunchSynchronizationHandle is an opaque value that LaunchProcess will
+// create and set, and that LaunchSynchronize will consume and destroy.
+typedef int* LaunchSynchronizationHandle;
+#endif // defined(OS_MACOSX)
+
// Options for launching a subprocess that are passed to LaunchProcess().
// The default constructor constructs the object with default options.
struct LaunchOptions {
@@ -259,6 +266,9 @@ struct LaunchOptions {
#if defined(OS_CHROMEOS)
, ctrl_terminal_fd(-1)
#endif // OS_CHROMEOS
+#if defined(OS_MACOSX)
+ , synchronize(NULL)
+#endif // defined(OS_MACOSX)
#endif // !defined(OS_WIN)
{}
@@ -340,6 +350,27 @@ struct LaunchOptions {
int ctrl_terminal_fd;
#endif // defined(OS_CHROMEOS)
+#if defined(OS_MACOSX)
+ // When non-NULL, a new LaunchSynchronizationHandle will be created and
+ // stored in *synchronize whenever LaunchProcess returns true in the parent
+ // process. The child process will have been created (with fork) but will
+ // be waiting (before exec) for the parent to call LaunchSynchronize with
+ // this handle. Only when LaunchSynchronize is called will the child be
+ // permitted to continue execution and call exec. LaunchSynchronize
+ // destroys the handle created by LaunchProcess.
+ //
+ // When synchronize is non-NULL, the parent must call LaunchSynchronize
+ // whenever LaunchProcess returns true. No exceptions.
+ //
+ // Synchronization is useful when the parent process needs to guarantee that
+ // it can take some action (such as recording the newly-forked child's
+ // process ID) before the child does something (such as using its process ID
+ // to communicate with its parent).
+ //
+ // |synchronize| and |wait| must not both be set simultaneously.
+ LaunchSynchronizationHandle* synchronize;
+#endif // defined(OS_MACOSX)
+
#endif // !defined(OS_WIN)
};
@@ -411,6 +442,15 @@ BASE_EXPORT bool LaunchProcess(const std::vector<std::string>& argv,
// The returned array is allocated using new[] and must be freed by the caller.
BASE_EXPORT char** AlterEnvironment(const EnvironmentVector& changes,
const char* const* const env);
+
+#if defined(OS_MACOSX)
+
+// After a successful call to LaunchProcess with LaunchOptions::synchronize
+// set, the parent process must call LaunchSynchronize to allow the child
+// process to proceed, and to destroy the LaunchSynchronizationHandle.
+BASE_EXPORT void LaunchSynchronize(LaunchSynchronizationHandle handle);
+
+#endif // defined(OS_MACOSX)
#endif // defined(OS_POSIX)
#if defined(OS_WIN)
diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc
index 98ec183..52e964b 100644
--- a/base/process_util_posix.cc
+++ b/base/process_util_posix.cc
@@ -604,6 +604,15 @@ bool LaunchProcess(const std::vector<std::string>& argv,
fd_shuffle_size = options.fds_to_remap->size();
}
+#if defined(OS_MACOSX)
+ if (options.synchronize) {
+ // When synchronizing, the "read" end of the synchronization pipe needs
+ // to make it to the child process. This is handled by mapping it back to
+ // itself.
+ ++fd_shuffle_size;
+ }
+#endif // defined(OS_MACOSX)
+
InjectiveMultimap fd_shuffle1;
InjectiveMultimap fd_shuffle2;
fd_shuffle1.reserve(fd_shuffle_size);
@@ -614,6 +623,34 @@ bool LaunchProcess(const std::vector<std::string>& argv,
if (options.environ)
new_environ.reset(AlterEnvironment(*options.environ, GetEnvironment()));
+#if defined(OS_MACOSX)
+ int synchronization_pipe_fds[2];
+ file_util::ScopedFD synchronization_read_fd;
+ file_util::ScopedFD synchronization_write_fd;
+
+ if (options.synchronize) {
+ // wait means "don't return from LaunchProcess until the child exits", and
+ // synchronize means "return from LaunchProcess but don't let the child
+ // run until LaunchSynchronize is called". These two options are highly
+ // incompatible.
+ DCHECK(!options.wait);
+
+ // Create the pipe used for synchronization.
+ if (HANDLE_EINTR(pipe(synchronization_pipe_fds)) != 0) {
+ DPLOG(ERROR) << "pipe";
+ return false;
+ }
+
+ // The parent process will only use synchronization_write_fd as the write
+ // side of the pipe. It can close the read side as soon as the child
+ // process has forked off. The child process will only use
+ // synchronization_read_fd as the read side of the pipe. In that process,
+ // the write side can be closed as soon as it has forked.
+ synchronization_read_fd.reset(&synchronization_pipe_fds[0]);
+ synchronization_write_fd.reset(&synchronization_pipe_fds[1]);
+ }
+#endif // OS_MACOSX
+
pid_t pid;
#if defined(OS_LINUX)
if (options.clone_flags) {
@@ -706,6 +743,13 @@ bool LaunchProcess(const std::vector<std::string>& argv,
RAW_LOG(INFO, "Right after signal/exception handler restoration.");
}
+#if defined(OS_MACOSX)
+ if (options.synchronize) {
+ // The "write" side of the synchronization pipe belongs to the parent.
+ synchronization_write_fd.reset(); // closes synchronization_pipe_fds[1]
+ }
+#endif // defined(OS_MACOSX)
+
#if 0
// When debugging it can be helpful to check that we really aren't making
// any hidden calls to malloc.
@@ -745,6 +789,16 @@ bool LaunchProcess(const std::vector<std::string>& argv,
RAW_LOG(INFO, "Right after fd_shuffle push_backs.");
}
+#if defined(OS_MACOSX)
+ if (options.synchronize) {
+ // Remap the read side of the synchronization pipe back onto itself,
+ // ensuring that it won't be closed by CloseSuperfluousFds.
+ int keep_fd = *synchronization_read_fd.get();
+ fd_shuffle1.push_back(InjectionArc(keep_fd, keep_fd, false));
+ fd_shuffle2.push_back(InjectionArc(keep_fd, keep_fd, false));
+ }
+#endif // defined(OS_MACOSX)
+
if (options.environ)
SetEnvironment(new_environ.get());
@@ -762,6 +816,24 @@ bool LaunchProcess(const std::vector<std::string>& argv,
RAW_LOG(INFO, "Right after CloseSuperfluousFds");
}
+#if defined(OS_MACOSX)
+ if (options.synchronize) {
+ // Do a blocking read to wait until the parent says it's OK to proceed.
+ // The byte that's read here is written by LaunchSynchronize.
+ char read_char;
+ int read_result =
+ HANDLE_EINTR(read(*synchronization_read_fd.get(), &read_char, 1));
+ if (read_result != 1) {
+ RAW_LOG(ERROR, "LaunchProcess: synchronization read: error");
+ _exit(127);
+ }
+
+ // The pipe is no longer useful. Don't let it live on in the new process
+ // after exec.
+ synchronization_read_fd.reset(); // closes synchronization_pipe_fds[0]
+ }
+#endif // defined(OS_MACOSX)
+
if (options.debug) {
RAW_LOG(INFO, "Right before execvp");
}
@@ -786,6 +858,14 @@ bool LaunchProcess(const std::vector<std::string>& argv,
if (process_handle)
*process_handle = pid;
+
+#if defined(OS_MACOSX)
+ if (options.synchronize) {
+ // The "read" side of the synchronization pipe belongs to the child.
+ synchronization_read_fd.reset(); // closes synchronization_pipe_fds[0]
+ *options.synchronize = new int(*synchronization_write_fd.release());
+ }
+#endif // defined(OS_MACOSX)
}
return true;
@@ -798,6 +878,19 @@ bool LaunchProcess(const CommandLine& cmdline,
return LaunchProcess(cmdline.argv(), options, process_handle);
}
+#if defined(OS_MACOSX)
+void LaunchSynchronize(LaunchSynchronizationHandle handle) {
+ int synchronization_fd = *handle;
+ file_util::ScopedFD synchronization_fd_closer(&synchronization_fd);
+ delete handle;
+
+ // Write a '\0' character to the pipe.
+ if (HANDLE_EINTR(write(synchronization_fd, "", 1)) != 1) {
+ DPLOG(ERROR) << "write";
+ }
+}
+#endif // defined(OS_MACOSX)
+
ProcessMetrics::~ProcessMetrics() { }
void RaiseProcessToHighPriority() {
diff --git a/content/app/content_main_runner.cc b/content/app/content_main_runner.cc
index 257f701..256f78f 100644
--- a/content/app/content_main_runner.cc
+++ b/content/app/content_main_runner.cc
@@ -180,6 +180,32 @@ base::LazyInstance<ContentUtilityClient>
static CAppModule _Module;
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+
+// Completes the Mach IPC handshake by sending this process' task port to the
+// parent process. The parent is listening on the Mach port given by
+// |GetMachPortName()|. The task port is used by the parent to get CPU/memory
+// stats to display in the task manager.
+void SendTaskPortToParentProcess() {
+ const mach_msg_timeout_t kTimeoutMs = 100;
+ const int32_t kMessageId = 0;
+ std::string mach_port_name = MachBroker::GetMachPortName();
+
+ base::MachSendMessage child_message(kMessageId);
+ if (!child_message.AddDescriptor(mach_task_self())) {
+ LOG(ERROR) << "child AddDescriptor(mach_task_self()) failed.";
+ return;
+ }
+
+ base::MachPortSender child_sender(mach_port_name.c_str());
+ kern_return_t err = child_sender.SendMessage(child_message, kTimeoutMs);
+ if (err != KERN_SUCCESS) {
+ LOG(ERROR) <<
+ base::StringPrintf("child SendMessage() failed: 0x%x %s", err,
+ mach_error_string(err));
+ }
+}
+
#endif // defined(OS_WIN)
#if defined(OS_POSIX) && !defined(OS_IOS)
@@ -640,7 +666,7 @@ class ContentMainRunnerImpl : public ContentMainRunner {
if (!process_type.empty() &&
(!delegate || delegate->ShouldSendMachPort(process_type))) {
- MachBroker::ChildSendTaskPortToParent();
+ SendTaskPortToParentProcess();
}
#elif defined(OS_WIN)
// This must be done early enough since some helper functions like
diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
index bde7f93..66e7c83 100644
--- a/content/browser/child_process_launcher.cc
+++ b/content/browser/child_process_launcher.cc
@@ -266,30 +266,30 @@ class ChildProcessLauncher::Context
options.fds_to_remap = &fds_to_map;
#if defined(OS_MACOSX)
- // Hold the MachBroker lock for the duration of LaunchProcess. The child
- // will send its task port to the parent almost immediately after startup.
- // The Mach message will be delivered to the parent, but updating the
- // record of the launch will wait until after the placeholder PID is
- // inserted below. This ensures that while the child process may send its
- // port to the parent prior to the parent leaving LaunchProcess, the
- // order in which the record in MachBroker is updated is correct.
- MachBroker* broker = MachBroker::GetInstance();
- broker->GetLock().Acquire();
-
- // Make sure the MachBroker is running, and inform it to expect a
- // check-in from the new process.
- broker->EnsureRunning();
+ // Use synchronization to make sure that the MachBroker is ready to
+ // receive a check-in from the new process before the new process
+ // actually tries to check in.
+ base::LaunchSynchronizationHandle synchronization_handle;
+ options.synchronize = &synchronization_handle;
#endif // defined(OS_MACOSX)
bool launched = base::LaunchProcess(*cmd_line, options, &handle);
#if defined(OS_MACOSX)
- if (launched)
- broker->AddPlaceholderForPid(handle);
+ if (launched) {
+ MachBroker* broker = MachBroker::GetInstance();
+ {
+ base::AutoLock lock(broker->GetLock());
+
+ // Make sure the MachBroker is running, and inform it to expect a
+ // check-in from the new process.
+ broker->EnsureRunning();
+ broker->AddPlaceholderForPid(handle);
+ }
- // After updating the broker, release the lock and let the child's
- // messasge be processed on the broker's thread.
- broker->GetLock().Release();
+ // Now that the MachBroker is ready, the child may continue.
+ base::LaunchSynchronize(synchronization_handle);
+ }
#endif // defined(OS_MACOSX)
if (!launched)
diff --git a/content/browser/mach_broker_mac.cc b/content/browser/mach_broker_mac.cc
new file mode 100644
index 0000000..b255635
--- /dev/null
+++ b/content/browser/mach_broker_mac.cc
@@ -0,0 +1,232 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/mach_broker_mac.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mach_ipc_mac.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/threading/platform_thread.h"
+#include "content/browser/renderer_host/render_process_host_impl.h"
+#include "content/public/browser/child_process_data.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/common/content_switches.h"
+
+namespace content {
+
+namespace {
+// Prints a string representation of a Mach error code.
+std::string MachErrorCode(kern_return_t err) {
+ return base::StringPrintf("0x%x %s", err, mach_error_string(err));
+}
+} // namespace
+
+class MachListenerThreadDelegate : public base::PlatformThread::Delegate {
+ public:
+ MachListenerThreadDelegate(MachBroker* broker) : broker_(broker) {
+ DCHECK(broker_);
+ std::string port_name = MachBroker::GetMachPortName();
+
+ // Create the receive port in the constructor, not in ThreadMain(). It is
+ // important to create and register the receive port before starting the
+ // thread so that child processes will always have someone who's listening.
+ receive_port_.reset(new base::ReceivePort(port_name.c_str()));
+ }
+
+ // Implement |PlatformThread::Delegate|.
+ virtual void ThreadMain() OVERRIDE {
+ base::MachReceiveMessage message;
+ kern_return_t err;
+ while ((err = receive_port_->WaitForMessage(&message,
+ MACH_MSG_TIMEOUT_NONE)) ==
+ KERN_SUCCESS) {
+ // 0 was the secret message id. Reject any messages that don't have it.
+ if (message.GetMessageID() != 0) {
+ LOG(ERROR) << "Received message with incorrect id: "
+ << message.GetMessageID();
+ continue;
+ }
+
+ const task_t child_task = message.GetTranslatedPort(0);
+ if (child_task == MACH_PORT_NULL) {
+ LOG(ERROR) << "parent GetTranslatedPort(0) failed.";
+ continue;
+ }
+
+ // It is possible for the child process to die after the call to
+ // |pid_for_task()| but before the call to |FinalizePid()|. To prevent
+ // leaking MachBroker map entries in this case, lock around both these
+ // calls. If the child dies, the death notification will be processed
+ // after the call to FinalizePid(), ensuring proper cleanup.
+ base::AutoLock lock(broker_->GetLock());
+
+ int pid;
+ err = pid_for_task(child_task, &pid);
+ if (err == KERN_SUCCESS) {
+ broker_->FinalizePid(pid,
+ MachBroker::MachInfo().SetTask(child_task));
+ } else {
+ LOG(ERROR) << "Error getting pid for task " << child_task
+ << ": " << MachErrorCode(err);
+ }
+ }
+
+ LOG(ERROR) << "Mach listener thread exiting; "
+ << "parent WaitForMessage() likely failed: "
+ << MachErrorCode(err);
+ }
+
+ private:
+ // The Mach port to listen on. Created on thread startup.
+ scoped_ptr<base::ReceivePort> receive_port_;
+
+ // The MachBroker to use when new child task rights are received. Can be
+ // NULL.
+ MachBroker* broker_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(MachListenerThreadDelegate);
+};
+
+// Returns the global MachBroker.
+MachBroker* MachBroker::GetInstance() {
+ return Singleton<MachBroker, LeakySingletonTraits<MachBroker> >::get();
+}
+
+void MachBroker::EnsureRunning() {
+ lock_.AssertAcquired();
+
+ if (!listener_thread_started_) {
+ listener_thread_started_ = true;
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&MachBroker::RegisterNotifications, base::Unretained(this)));
+
+ // Intentional leak. This thread is never joined or reaped.
+ base::PlatformThread::CreateNonJoinable(
+ 0, new MachListenerThreadDelegate(this));
+ }
+}
+
+// Adds a placeholder to the map for the given pid with MACH_PORT_NULL.
+void MachBroker::AddPlaceholderForPid(base::ProcessHandle pid) {
+ lock_.AssertAcquired();
+
+ MachInfo mach_info;
+ DCHECK_EQ(0u, mach_map_.count(pid));
+ mach_map_[pid] = mach_info;
+}
+
+// Updates the mapping for |pid| to include the given |mach_info|.
+void MachBroker::FinalizePid(base::ProcessHandle pid,
+ const MachInfo& mach_info) {
+ lock_.AssertAcquired();
+
+ const int count = mach_map_.count(pid);
+ if (count == 0) {
+ // Do nothing for unknown pids.
+ LOG(ERROR) << "Unknown process " << pid << " is sending Mach IPC messages!";
+ return;
+ }
+
+ DCHECK_EQ(1, count);
+ DCHECK(mach_map_[pid].mach_task_ == MACH_PORT_NULL);
+ if (mach_map_[pid].mach_task_ == MACH_PORT_NULL)
+ mach_map_[pid] = mach_info;
+}
+
+// Removes all mappings belonging to |pid| from the broker.
+void MachBroker::InvalidatePid(base::ProcessHandle pid) {
+ base::AutoLock lock(lock_);
+ MachBroker::MachMap::iterator it = mach_map_.find(pid);
+ if (it == mach_map_.end())
+ return;
+
+ kern_return_t kr = mach_port_deallocate(mach_task_self(),
+ it->second.mach_task_);
+ LOG_IF(WARNING, kr != KERN_SUCCESS)
+ << "Failed to mach_port_deallocate mach task " << it->second.mach_task_
+ << ", error " << MachErrorCode(kr);
+ mach_map_.erase(it);
+}
+
+base::Lock& MachBroker::GetLock() {
+ return lock_;
+}
+
+// Returns the mach task belonging to |pid|.
+mach_port_t MachBroker::TaskForPid(base::ProcessHandle pid) const {
+ base::AutoLock lock(lock_);
+ MachBroker::MachMap::const_iterator it = mach_map_.find(pid);
+ if (it == mach_map_.end())
+ return MACH_PORT_NULL;
+ return it->second.mach_task_;
+}
+
+void MachBroker::BrowserChildProcessHostDisconnected(
+ const ChildProcessData& data) {
+ InvalidatePid(data.handle);
+}
+
+void MachBroker::BrowserChildProcessCrashed(const ChildProcessData& data) {
+ InvalidatePid(data.handle);
+}
+
+void MachBroker::Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ // TODO(rohitrao): These notifications do not always carry the proper PIDs,
+ // especially when the renderer is already gone or has crashed. Find a better
+ // way to listen for child process deaths. http://crbug.com/55734
+ base::ProcessHandle handle = 0;
+ switch (type) {
+ case NOTIFICATION_RENDERER_PROCESS_CLOSED:
+ handle = Details<RenderProcessHost::RendererClosedDetails>(
+ details)->handle;
+ break;
+ case NOTIFICATION_RENDERER_PROCESS_TERMINATED:
+ handle = Source<RenderProcessHost>(source)->GetHandle();
+ break;
+ default:
+ NOTREACHED() << "Unexpected notification";
+ break;
+ }
+ InvalidatePid(handle);
+}
+
+// static
+std::string MachBroker::GetMachPortName() {
+ const CommandLine* command_line = CommandLine::ForCurrentProcess();
+ const bool is_child = command_line->HasSwitch(switches::kProcessType);
+
+ // In non-browser (child) processes, use the parent's pid.
+ const pid_t pid = is_child ? getppid() : getpid();
+ return base::StringPrintf("%s.rohitfork.%d", base::mac::BaseBundleID(), pid);
+}
+
+MachBroker::MachBroker() : listener_thread_started_(false) {
+}
+
+MachBroker::~MachBroker() {}
+
+void MachBroker::RegisterNotifications() {
+ registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CLOSED,
+ NotificationService::AllBrowserContextsAndSources());
+ registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
+ NotificationService::AllBrowserContextsAndSources());
+
+ // No corresponding StopObservingBrowserChildProcesses,
+ // we leak this singleton.
+ BrowserChildProcessObserver::Add(this);
+}
+
+} // namespace content
diff --git a/content/browser/mach_broker_mac.h b/content/browser/mach_broker_mac.h
index e38e86f..011df04 100644
--- a/content/browser/mach_broker_mac.h
+++ b/content/browser/mach_broker_mac.h
@@ -38,31 +38,49 @@ class CONTENT_EXPORT MachBroker : public base::ProcessMetrics::PortProvider,
public BrowserChildProcessObserver,
public NotificationObserver {
public:
- // For use in child processes. This will send the task port of the current
- // process over Mach IPC to the port registered by name (via this class) in
- // the parent process. Returns true if the message was sent successfully
- // and false if otherwise.
- static bool ChildSendTaskPortToParent();
-
// Returns the global MachBroker.
static MachBroker* GetInstance();
- // The lock that protects this MachBroker object. Clients MUST acquire and
- // release this lock around calls to EnsureRunning(), PlaceholderForPid(),
- // and FinalizePid().
- base::Lock& GetLock();
-
// Performs any necessary setup that cannot happen in the constructor.
// Callers MUST acquire the lock given by GetLock() before calling this
// method (and release the lock afterwards).
void EnsureRunning();
+ struct MachInfo {
+ MachInfo() : mach_task_(MACH_PORT_NULL) {}
+
+ MachInfo& SetTask(mach_port_t task) {
+ mach_task_ = task;
+ return *this;
+ }
+
+ mach_port_t mach_task_;
+ };
+
// Adds a placeholder to the map for the given pid with MACH_PORT_NULL.
// Callers are expected to later update the port with FinalizePid(). Callers
// MUST acquire the lock given by GetLock() before calling this method (and
// release the lock afterwards).
void AddPlaceholderForPid(base::ProcessHandle pid);
+ // Updates the mapping for |pid| to include the given |mach_info|. Does
+ // nothing if PlaceholderForPid() has not already been called for the given
+ // |pid|. Callers MUST acquire the lock given by GetLock() before calling
+ // this method (and release the lock afterwards).
+ void FinalizePid(base::ProcessHandle pid, const MachInfo& mach_info);
+
+ // Removes all mappings belonging to |pid| from the broker.
+ void InvalidatePid(base::ProcessHandle pid);
+
+ // The lock that protects this MachBroker object. Clients MUST acquire and
+ // release this lock around calls to EnsureRunning(), PlaceholderForPid(),
+ // and FinalizePid().
+ base::Lock& GetLock();
+
+ // Returns the Mach port name to use when sending or receiving messages.
+ // Does the Right Thing in the browser and in child processes.
+ static std::string GetMachPortName();
+
// Implement |ProcessMetrics::PortProvider|.
virtual mach_port_t TaskForPid(base::ProcessHandle process) const OVERRIDE;
@@ -78,24 +96,11 @@ class CONTENT_EXPORT MachBroker : public base::ProcessMetrics::PortProvider,
const NotificationDetails& details) OVERRIDE;
private:
friend class MachBrokerTest;
- friend class MachListenerThreadDelegate;
friend struct DefaultSingletonTraits<MachBroker>;
MachBroker();
virtual ~MachBroker();
- // Updates the mapping for |pid| to include the given |mach_info|. Does
- // nothing if PlaceholderForPid() has not already been called for the given
- // |pid|. Callers MUST acquire the lock given by GetLock() before calling
- // this method (and release the lock afterwards).
- void FinalizePid(base::ProcessHandle pid, mach_port_t task_port);
-
- // Removes all mappings belonging to |pid| from the broker.
- void InvalidatePid(base::ProcessHandle pid);
-
- // Returns the Mach port name to use when sending or receiving messages.
- // Does the Right Thing in the browser and in child processes.
- static std::string GetMachPortName();
// Callback used to register notifications on the UI thread.
void RegisterNotifications();
@@ -107,7 +112,7 @@ class CONTENT_EXPORT MachBroker : public base::ProcessMetrics::PortProvider,
NotificationRegistrar registrar_;
// Stores mach info for every process in the broker.
- typedef std::map<base::ProcessHandle, mach_port_t> MachMap;
+ typedef std::map<base::ProcessHandle, MachInfo> MachMap;
MachMap mach_map_;
// Mutex that guards |mach_map_|.
diff --git a/content/browser/mach_broker_mac.mm b/content/browser/mach_broker_mac.mm
deleted file mode 100644
index d765cf5..0000000
--- a/content/browser/mach_broker_mac.mm
+++ /dev/null
@@ -1,309 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/mach_broker_mac.h"
-
-#include <bsm/libbsm.h>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/command_line.h"
-#include "base/logging.h"
-#include "base/mac/foundation_util.h"
-#include "base/mac/scoped_mach_port.h"
-#include "base/mach_ipc_mac.h"
-#include "base/string_util.h"
-#include "base/stringprintf.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/threading/platform_thread.h"
-#include "content/browser/renderer_host/render_process_host_impl.h"
-#include "content/public/browser/child_process_data.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_types.h"
-#include "content/public/common/content_switches.h"
-
-namespace content {
-
-namespace {
-
-// Prints a string representation of a Mach error code.
-std::string MachErrorCode(kern_return_t err) {
- return base::StringPrintf("0x%x %s", err, mach_error_string(err));
-}
-
-// Mach message structure used in the child as a sending message.
-struct MachBroker_ChildSendMsg {
- mach_msg_header_t header;
- mach_msg_body_t body;
- mach_msg_port_descriptor_t child_task_port;
-};
-
-// Complement to the ChildSendMsg, this is used in the parent for receiving
-// a message. Contains a message trailer with audit information.
-struct MachBroker_ParentRecvMsg : public MachBroker_ChildSendMsg {
- mach_msg_audit_trailer_t trailer;
-};
-
-} // namespace
-
-class MachListenerThreadDelegate : public base::PlatformThread::Delegate {
- public:
- explicit MachListenerThreadDelegate(MachBroker* broker)
- : broker_(broker),
- server_port_(MACH_PORT_NULL) {
- DCHECK(broker_);
- }
-
- bool Init() {
- DCHECK(server_port_ == MACH_PORT_NULL);
-
- mach_port_t port;
- kern_return_t kr = mach_port_allocate(mach_task_self(),
- MACH_PORT_RIGHT_RECEIVE,
- &port);
- if (kr != KERN_SUCCESS) {
- LOG(ERROR) << "Failed to allocate MachBroker server port: "
- << MachErrorCode(kr);
- return false;
- }
-
- // Allocate a send right for the server port.
- kr = mach_port_insert_right(
- mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
- if (kr != KERN_SUCCESS) {
- LOG(ERROR) << "Failed to insert send right for MachBroker server port: "
- << MachErrorCode(kr);
- return false;
- }
-
- server_port_.reset(port);
-
- // Register the port with the bootstrap server. Because bootstrap_register
- // is deprecated, this has to be wraped in an ObjC interface.
- NSPort* ns_port = [NSMachPort portWithMachPort:port
- options:NSMachPortDeallocateNone];
- NSString* name = base::SysUTF8ToNSString(broker_->GetMachPortName());
- return [[NSMachBootstrapServer sharedInstance] registerPort:ns_port
- name:name];
- }
-
- // Implement |PlatformThread::Delegate|.
- virtual void ThreadMain() OVERRIDE {
- MachBroker_ParentRecvMsg msg;
- bzero(&msg, sizeof(msg));
- msg.header.msgh_size = sizeof(msg);
- msg.header.msgh_local_port = server_port_.get();
-
- kern_return_t kr;
- do {
- // Use the kernel audit information to make sure this message is from
- // a task that this process spawned. The kernel audit token contains the
- // unspoofable pid of the task that sent the message.
- mach_msg_option_t options = MACH_RCV_MSG |
- MACH_RCV_TRAILER_TYPE(MACH_RCV_TRAILER_AUDIT) |
- MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
-
- kr = mach_msg(&msg.header, options, 0, sizeof(msg), server_port_,
- MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
- if (kr == KERN_SUCCESS) {
- // TODO(rsesek): In the 10.7 SDK, there's audit_token_to_pid().
- pid_t child_pid;
- audit_token_to_au32(msg.trailer.msgh_audit,
- NULL, NULL, NULL, NULL, NULL, &child_pid, NULL, NULL);
-
- mach_port_t child_task_port = msg.child_task_port.name;
-
- // Take the lock and update the broker information.
- base::AutoLock lock(broker_->GetLock());
- broker_->FinalizePid(child_pid, child_task_port);
- }
- } while (kr == KERN_SUCCESS);
-
- LOG(ERROR) << "MachBroker thread exiting; mach_msg() likely failed: "
- << MachErrorCode(kr);
- }
-
- private:
- // The MachBroker to use when new child task rights are received. Can be
- // NULL.
- MachBroker* broker_; // weak
-
- base::mac::ScopedMachPort server_port_;
-
- DISALLOW_COPY_AND_ASSIGN(MachListenerThreadDelegate);
-};
-
-bool MachBroker::ChildSendTaskPortToParent() {
- // Look up the named MachBroker port that's been registered with the
- // bootstrap server.
- mach_port_t bootstrap_port;
- kern_return_t kr = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
- if (kr != KERN_SUCCESS) {
- LOG(ERROR) << "Failed to look up bootstrap port: " << MachErrorCode(kr);
- return false;
- }
-
- mach_port_t parent_port;
- kr = bootstrap_look_up(bootstrap_port,
- const_cast<char*>(GetMachPortName().c_str()), &parent_port);
- if (kr != KERN_SUCCESS) {
- LOG(ERROR) << "Failed to look up named parent port: " << MachErrorCode(kr);
- return false;
- }
-
- // Create the check in message. This will copy a send right on this process'
- // (the child's) task port and send it to the parent.
- MachBroker_ChildSendMsg msg;
- bzero(&msg, sizeof(msg));
- msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND) |
- MACH_MSGH_BITS_COMPLEX;
- msg.header.msgh_remote_port = parent_port;
- msg.header.msgh_size = sizeof(msg);
- msg.body.msgh_descriptor_count = 1;
- msg.child_task_port.name = mach_task_self();
- msg.child_task_port.disposition = MACH_MSG_TYPE_PORT_SEND;
- msg.child_task_port.type = MACH_MSG_PORT_DESCRIPTOR;
-
- kr = mach_msg(&msg.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, sizeof(msg),
- 0, MACH_PORT_NULL, 100 /*milliseconds*/, MACH_PORT_NULL);
- if (kr != KERN_SUCCESS) {
- LOG(ERROR) << "Failed to send task port to parent: " << MachErrorCode(kr);
- return false;
- }
-
- return true;
-}
-
-MachBroker* MachBroker::GetInstance() {
- return Singleton<MachBroker, LeakySingletonTraits<MachBroker> >::get();
-}
-
-base::Lock& MachBroker::GetLock() {
- return lock_;
-}
-
-void MachBroker::EnsureRunning() {
- lock_.AssertAcquired();
-
- if (!listener_thread_started_) {
- listener_thread_started_ = true;
-
- BrowserThread::PostTask(
- BrowserThread::UI, FROM_HERE,
- base::Bind(&MachBroker::RegisterNotifications, base::Unretained(this)));
-
- // Intentional leak. This thread is never joined or reaped.
- MachListenerThreadDelegate* thread = new MachListenerThreadDelegate(this);
- if (thread->Init()) {
- base::PlatformThread::CreateNonJoinable(0, thread);
- } else {
- LOG(ERROR) << "Failed to initialize the MachListenerThreadDelegate";
- }
- }
-}
-
-void MachBroker::AddPlaceholderForPid(base::ProcessHandle pid) {
- lock_.AssertAcquired();
-
- DCHECK_EQ(0u, mach_map_.count(pid));
- mach_map_[pid] = MACH_PORT_NULL;
-}
-
-mach_port_t MachBroker::TaskForPid(base::ProcessHandle pid) const {
- base::AutoLock lock(lock_);
- MachBroker::MachMap::const_iterator it = mach_map_.find(pid);
- if (it == mach_map_.end())
- return MACH_PORT_NULL;
- return it->second;
-}
-
-void MachBroker::BrowserChildProcessHostDisconnected(
- const ChildProcessData& data) {
- InvalidatePid(data.handle);
-}
-
-void MachBroker::BrowserChildProcessCrashed(const ChildProcessData& data) {
- InvalidatePid(data.handle);
-}
-
-void MachBroker::Observe(int type,
- const NotificationSource& source,
- const NotificationDetails& details) {
- // TODO(rohitrao): These notifications do not always carry the proper PIDs,
- // especially when the renderer is already gone or has crashed. Find a better
- // way to listen for child process deaths. http://crbug.com/55734
- base::ProcessHandle handle = 0;
- switch (type) {
- case NOTIFICATION_RENDERER_PROCESS_CLOSED:
- handle = Details<RenderProcessHost::RendererClosedDetails>(
- details)->handle;
- break;
- case NOTIFICATION_RENDERER_PROCESS_TERMINATED:
- handle = Source<RenderProcessHost>(source)->GetHandle();
- break;
- default:
- NOTREACHED() << "Unexpected notification";
- break;
- }
- InvalidatePid(handle);
-}
-
-MachBroker::MachBroker() : listener_thread_started_(false) {
-}
-
-MachBroker::~MachBroker() {}
-
-void MachBroker::FinalizePid(base::ProcessHandle pid,
- mach_port_t task_port) {
- lock_.AssertAcquired();
-
- MachMap::iterator it = mach_map_.find(pid);
- if (it == mach_map_.end()) {
- // Do nothing for unknown pids.
- LOG(ERROR) << "Unknown process " << pid << " is sending Mach IPC messages!";
- return;
- }
-
- DCHECK(it->second == MACH_PORT_NULL);
- if (it->second == MACH_PORT_NULL)
- it->second = task_port;
-}
-
-void MachBroker::InvalidatePid(base::ProcessHandle pid) {
- base::AutoLock lock(lock_);
- MachBroker::MachMap::iterator it = mach_map_.find(pid);
- if (it == mach_map_.end())
- return;
-
- kern_return_t kr = mach_port_deallocate(mach_task_self(),
- it->second);
- LOG_IF(WARNING, kr != KERN_SUCCESS)
- << "Failed to mach_port_deallocate mach task " << it->second
- << ", error " << MachErrorCode(kr);
- mach_map_.erase(it);
-}
-
-// static
-std::string MachBroker::GetMachPortName() {
- const CommandLine* command_line = CommandLine::ForCurrentProcess();
- const bool is_child = command_line->HasSwitch(switches::kProcessType);
-
- // In non-browser (child) processes, use the parent's pid.
- const pid_t pid = is_child ? getppid() : getpid();
- return base::StringPrintf("%s.rohitfork.%d", base::mac::BaseBundleID(), pid);
-}
-
-void MachBroker::RegisterNotifications() {
- registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_CLOSED,
- NotificationService::AllBrowserContextsAndSources());
- registrar_.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED,
- NotificationService::AllBrowserContextsAndSources());
-
- // No corresponding StopObservingBrowserChildProcesses,
- // we leak this singleton.
- BrowserChildProcessObserver::Add(this);
-}
-
-} // namespace content
diff --git a/content/browser/mach_broker_mac_unittest.cc b/content/browser/mach_broker_mac_unittest.cc
index a7eca4f..7bd88a5 100644
--- a/content/browser/mach_broker_mac_unittest.cc
+++ b/content/browser/mach_broker_mac_unittest.cc
@@ -17,15 +17,11 @@ class MachBrokerTest : public testing::Test {
broker_.AddPlaceholderForPid(pid);
}
- void InvalidatePid(base::ProcessHandle pid) {
- broker_.InvalidatePid(pid);
- }
-
// Helper function to acquire/release locks and call |FinalizePid()|.
void FinalizePid(base::ProcessHandle pid,
- mach_port_t task_port) {
+ const MachBroker::MachInfo& mach_info) {
base::AutoLock lock(broker_.GetLock());
- broker_.FinalizePid(pid, task_port);
+ broker_.FinalizePid(pid, mach_info);
}
protected:
@@ -43,7 +39,7 @@ TEST_F(MachBrokerTest, AddPlaceholderAndFinalize) {
EXPECT_EQ(0u, broker_.TaskForPid(1));
// Finalize PID 1.
- FinalizePid(1, 100u);
+ FinalizePid(1, MachBroker::MachInfo().SetTask(100u));
EXPECT_EQ(100u, broker_.TaskForPid(1));
// Should be no entry for PID 2.
@@ -52,16 +48,16 @@ TEST_F(MachBrokerTest, AddPlaceholderAndFinalize) {
TEST_F(MachBrokerTest, Invalidate) {
AddPlaceholderForPid(1);
- FinalizePid(1, 100u);
+ FinalizePid(1, MachBroker::MachInfo().SetTask(100u));
EXPECT_EQ(100u, broker_.TaskForPid(1));
- InvalidatePid(1u);
+ broker_.InvalidatePid(1u);
EXPECT_EQ(0u, broker_.TaskForPid(1));
}
TEST_F(MachBrokerTest, FinalizeUnknownPid) {
// Finalizing an entry for an unknown pid should not add it to the map.
- FinalizePid(1u, 100u);
+ FinalizePid(1u, MachBroker::MachInfo().SetTask(100u));
EXPECT_EQ(0u, broker_.TaskForPid(1u));
}
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index 7174603..017d291 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -587,7 +587,7 @@
'browser/loader/throttling_resource_handler.h',
'browser/loader/transfer_navigation_resource_throttle.cc',
'browser/loader/transfer_navigation_resource_throttle.h',
- 'browser/mach_broker_mac.mm',
+ 'browser/mach_broker_mac.cc',
'browser/mach_broker_mac.h',
'browser/media_devices_monitor.cc',
'browser/media/media_internals.cc',
@@ -1201,11 +1201,6 @@
'dependencies': [
'../third_party/sudden_motion_sensor/sudden_motion_sensor.gyp:sudden_motion_sensor',
],
- 'link_settings': {
- 'libraries': [
- '-lbsm',
- ],
- },
}],
['chromeos==1', {
'dependencies': [