diff options
Diffstat (limited to 'ipc/chromium/src/chrome/common')
30 files changed, 5248 insertions, 0 deletions
diff --git a/ipc/chromium/src/chrome/common/child_process.cc b/ipc/chromium/src/chrome/common/child_process.cc new file mode 100644 index 000000000..bade255c5 --- /dev/null +++ b/ipc/chromium/src/chrome/common/child_process.cc @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "chrome/common/child_process.h" + +#include "base/basictypes.h" +#include "base/string_util.h" +#include "chrome/common/child_thread.h" + +ChildProcess* ChildProcess::child_process_; + +ChildProcess::ChildProcess(ChildThread* child_thread) + : child_thread_(child_thread) { + DCHECK(!child_process_); + child_process_ = this; + if (child_thread_.get()) // null in unittests. + child_thread_->Run(); +} + +ChildProcess::~ChildProcess() { + DCHECK(child_process_ == this); + + if (child_thread_.get()) + child_thread_->Stop(); + + child_process_ = NULL; +} diff --git a/ipc/chromium/src/chrome/common/child_process.h b/ipc/chromium/src/chrome/common/child_process.h new file mode 100644 index 000000000..67b759fa2 --- /dev/null +++ b/ipc/chromium/src/chrome/common/child_process.h @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + +#ifndef CHROME_COMMON_CHILD_PROCESS_H__ +#define CHROME_COMMON_CHILD_PROCESS_H__ + +#include <string> +#include <vector> +#include "base/basictypes.h" +#include "base/message_loop.h" +#include "base/waitable_event.h" +#include "mozilla/UniquePtr.h" + +class ChildThread; + + +// Base class for child processes of the browser process (i.e. renderer and +// plugin host). This is a singleton object for each child process. +class ChildProcess { + public: + // Child processes should have an object that derives from this class. The + // constructor will return once ChildThread has started. + explicit ChildProcess(ChildThread* child_thread); + virtual ~ChildProcess(); + + // Getter for this process' main thread. + ChildThread* child_thread() { return child_thread_.get(); } + + // Getter for the one ChildProcess object for this process. + static ChildProcess* current() { return child_process_; } + + private: + // NOTE: make sure that child_thread_ is listed before shutdown_event_, since + // it depends on it (indirectly through IPC::SyncChannel). + mozilla::UniquePtr<ChildThread> child_thread_; + + // The singleton instance for this process. + static ChildProcess* child_process_; + + DISALLOW_EVIL_CONSTRUCTORS(ChildProcess); +}; + +#endif // CHROME_COMMON_CHILD_PROCESS_H__ diff --git a/ipc/chromium/src/chrome/common/child_process_host.cc b/ipc/chromium/src/chrome/common/child_process_host.cc new file mode 100644 index 000000000..ec324d2bf --- /dev/null +++ b/ipc/chromium/src/chrome/common/child_process_host.cc @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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 "chrome/common/child_process_host.h" + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/process_util.h" +#include "base/singleton.h" +#include "base/waitable_event.h" +#include "mozilla/ipc/ProcessChild.h" +#include "mozilla/ipc/BrowserProcessSubThread.h" +#include "mozilla/ipc/Transport.h" +typedef mozilla::ipc::BrowserProcessSubThread ChromeThread; +#include "chrome/common/process_watcher.h" + +using mozilla::ipc::FileDescriptor; + +ChildProcessHost::ChildProcessHost() + : ALLOW_THIS_IN_INITIALIZER_LIST(listener_(this)), + opening_channel_(false) { +} + + +ChildProcessHost::~ChildProcessHost() { +} + +bool ChildProcessHost::CreateChannel() { + channel_id_ = IPC::Channel::GenerateVerifiedChannelID(std::wstring()); + channel_.reset(new IPC::Channel( + channel_id_, IPC::Channel::MODE_SERVER, &listener_)); + if (!channel_->Connect()) + return false; + + opening_channel_ = true; + + return true; +} + +bool ChildProcessHost::CreateChannel(FileDescriptor& aFileDescriptor) { + if (channel_.get()) { + channel_->Close(); + } + channel_ = mozilla::ipc::OpenDescriptor(aFileDescriptor, IPC::Channel::MODE_SERVER); + if (!channel_->Connect()) { + return false; + } + + opening_channel_ = true; + + return true; +} + +ChildProcessHost::ListenerHook::ListenerHook(ChildProcessHost* host) + : host_(host) { +} + +void ChildProcessHost::ListenerHook::OnMessageReceived( + IPC::Message&& msg) { + host_->OnMessageReceived(mozilla::Move(msg)); +} + +void ChildProcessHost::ListenerHook::OnChannelConnected(int32_t peer_pid) { + host_->opening_channel_ = false; + host_->OnChannelConnected(peer_pid); +} + +void ChildProcessHost::ListenerHook::OnChannelError() { + host_->opening_channel_ = false; + host_->OnChannelError(); +} + +void ChildProcessHost::ListenerHook::GetQueuedMessages(std::queue<IPC::Message>& queue) { + host_->GetQueuedMessages(queue); +} diff --git a/ipc/chromium/src/chrome/common/child_process_host.h b/ipc/chromium/src/chrome/common/child_process_host.h new file mode 100644 index 000000000..6411d3a6e --- /dev/null +++ b/ipc/chromium/src/chrome/common/child_process_host.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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. + +#ifndef CHROME_COMMON_CHILD_PROCESS_HOST_H_ +#define CHROME_COMMON_CHILD_PROCESS_HOST_H_ + +#include "build/build_config.h" + +#include <list> + +#include "base/basictypes.h" +#include "chrome/common/ipc_channel.h" +#include "mozilla/UniquePtr.h" + +namespace mozilla { +namespace ipc { +class FileDescriptor; +} +} + +// Plugins/workers and other child processes that live on the IO thread should +// derive from this class. +class ChildProcessHost : public IPC::Channel::Listener { + public: + virtual ~ChildProcessHost(); + + protected: + explicit ChildProcessHost(); + + // Derived classes return true if it's ok to shut down the child process. + virtual bool CanShutdown() = 0; + + // Creates the IPC channel. Returns true iff it succeeded. + bool CreateChannel(); + + bool CreateChannel(mozilla::ipc::FileDescriptor& aFileDescriptor); + + // IPC::Channel::Listener implementation: + virtual void OnMessageReceived(IPC::Message&& msg) { } + virtual void OnChannelConnected(int32_t peer_pid) { } + virtual void OnChannelError() { } + + bool opening_channel() { return opening_channel_; } + const std::wstring& channel_id() { return channel_id_; } + + const IPC::Channel& channel() const { return *channel_; } + IPC::Channel* channelp() const { return channel_.get(); } + + private: + // By using an internal class as the IPC::Channel::Listener, we can intercept + // OnMessageReceived/OnChannelConnected and do our own processing before + // calling the subclass' implementation. + class ListenerHook : public IPC::Channel::Listener { + public: + explicit ListenerHook(ChildProcessHost* host); + virtual void OnMessageReceived(IPC::Message&& msg); + virtual void OnChannelConnected(int32_t peer_pid); + virtual void OnChannelError(); + virtual void GetQueuedMessages(std::queue<IPC::Message>& queue); + private: + ChildProcessHost* host_; + }; + + ListenerHook listener_; + + // True while we're waiting the channel to be opened. + bool opening_channel_; + + // The IPC::Channel. + mozilla::UniquePtr<IPC::Channel> channel_; + + // IPC Channel's id. + std::wstring channel_id_; +}; + +#endif // CHROME_COMMON_CHILD_PROCESS_HOST_H_ diff --git a/ipc/chromium/src/chrome/common/child_thread.cc b/ipc/chromium/src/chrome/common/child_thread.cc new file mode 100644 index 000000000..619f73c5d --- /dev/null +++ b/ipc/chromium/src/chrome/common/child_thread.cc @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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 "chrome/common/child_thread.h" + +#include "base/string_util.h" +#include "base/command_line.h" +#include "chrome/common/child_process.h" +#include "chrome/common/chrome_switches.h" + +ChildThread::ChildThread(Thread::Options options) + : Thread("Chrome_ChildThread"), + owner_loop_(MessageLoop::current()), + options_(options) { + DCHECK(owner_loop_); + channel_name_ = CommandLine::ForCurrentProcess()->GetSwitchValue( + switches::kProcessChannelID); +} + +ChildThread::~ChildThread() { +} + +bool ChildThread::Run() { + bool r = StartWithOptions(options_); + return r; +} + +void ChildThread::OnChannelError() { + RefPtr<mozilla::Runnable> task = new MessageLoop::QuitTask(); + owner_loop_->PostTask(task.forget()); +} + +void ChildThread::OnMessageReceived(IPC::Message&& msg) { +} + +ChildThread* ChildThread::current() { + return ChildProcess::current()->child_thread(); +} + +void ChildThread::Init() { + channel_ = mozilla::MakeUnique<IPC::Channel>(channel_name_, + IPC::Channel::MODE_CLIENT, + this); + +} + +void ChildThread::CleanUp() { + // Need to destruct the SyncChannel to the browser before we go away because + // it caches a pointer to this thread. + channel_ = nullptr; +} diff --git a/ipc/chromium/src/chrome/common/child_thread.h b/ipc/chromium/src/chrome/common/child_thread.h new file mode 100644 index 000000000..a36d16ec9 --- /dev/null +++ b/ipc/chromium/src/chrome/common/child_thread.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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. + +#ifndef CHROME_COMMON_CHILD_THREAD_H_ +#define CHROME_COMMON_CHILD_THREAD_H_ + +#include "base/thread.h" +#include "chrome/common/ipc_channel.h" +#include "mozilla/UniquePtr.h" + +class ResourceDispatcher; + +// Child processes's background thread should derive from this class. +class ChildThread : public IPC::Channel::Listener, + public base::Thread { + public: + // Creates the thread. + explicit ChildThread(Thread::Options options); + virtual ~ChildThread(); + + protected: + friend class ChildProcess; + + // Starts the thread. + bool Run(); + + protected: + // Returns the one child thread. + static ChildThread* current(); + + IPC::Channel* channel() { return channel_.get(); } + + // Thread implementation. + virtual void Init(); + virtual void CleanUp(); + + private: + // IPC::Channel::Listener implementation: + virtual void OnMessageReceived(IPC::Message&& msg); + virtual void OnChannelError(); + + // The message loop used to run tasks on the thread that started this thread. + MessageLoop* owner_loop_; + + std::wstring channel_name_; + mozilla::UniquePtr<IPC::Channel> channel_; + + Thread::Options options_; + + DISALLOW_EVIL_CONSTRUCTORS(ChildThread); +}; + +#endif // CHROME_COMMON_CHILD_THREAD_H_ diff --git a/ipc/chromium/src/chrome/common/chrome_switches.cc b/ipc/chromium/src/chrome/common/chrome_switches.cc new file mode 100644 index 000000000..082d60637 --- /dev/null +++ b/ipc/chromium/src/chrome/common/chrome_switches.cc @@ -0,0 +1,18 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2009 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 "chrome/common/chrome_switches.h" + +namespace switches { + +// Can't find the switch you are looking for? try looking in +// base/base_switches.cc instead. + +// The value of this switch tells the child process which +// IPC channel the browser expects to use to communicate with it. +const wchar_t kProcessChannelID[] = L"channel"; + +} // namespace switches diff --git a/ipc/chromium/src/chrome/common/chrome_switches.h b/ipc/chromium/src/chrome/common/chrome_switches.h new file mode 100644 index 000000000..5dd4446a8 --- /dev/null +++ b/ipc/chromium/src/chrome/common/chrome_switches.h @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2009 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. + +// Defines all the command-line switches used by Chrome. + +#ifndef CHROME_COMMON_CHROME_SWITCHES_H__ +#define CHROME_COMMON_CHROME_SWITCHES_H__ + +#if defined(COMPILER_MSVC) +#include <string.h> +#endif + +namespace switches { + +extern const wchar_t kProcessChannelID[]; + +} // namespace switches + +#endif // CHROME_COMMON_CHROME_SWITCHES_H__ diff --git a/ipc/chromium/src/chrome/common/file_descriptor_set_posix.cc b/ipc/chromium/src/chrome/common/file_descriptor_set_posix.cc new file mode 100644 index 000000000..9e70ff820 --- /dev/null +++ b/ipc/chromium/src/chrome/common/file_descriptor_set_posix.cc @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "chrome/common/file_descriptor_set_posix.h" + +#include "base/eintr_wrapper.h" +#include "base/logging.h" + +#include <unistd.h> + +FileDescriptorSet::FileDescriptorSet() + : consumed_descriptor_highwater_(0) { +} + +FileDescriptorSet::~FileDescriptorSet() { + if (consumed_descriptor_highwater_ == descriptors_.size()) + return; + + CHROMIUM_LOG(WARNING) << "FileDescriptorSet destroyed with unconsumed descriptors"; + // We close all the descriptors where the close flag is set. If this + // message should have been transmitted, then closing those with close + // flags set mirrors the expected behaviour. + // + // If this message was received with more descriptors than expected + // (which could a DOS against the browser by a rogue renderer) then all + // the descriptors have their close flag set and we free all the extra + // kernel resources. + for (unsigned i = consumed_descriptor_highwater_; + i < descriptors_.size(); ++i) { + if (descriptors_[i].auto_close) + HANDLE_EINTR(close(descriptors_[i].fd)); + } +} + +bool FileDescriptorSet::Add(int fd) { + if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE) + return false; + + struct base::FileDescriptor sd; + sd.fd = fd; + sd.auto_close = false; + descriptors_.push_back(sd); + return true; +} + +bool FileDescriptorSet::AddAndAutoClose(int fd) { + if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE) + return false; + + struct base::FileDescriptor sd; + sd.fd = fd; + sd.auto_close = true; + descriptors_.push_back(sd); + DCHECK(descriptors_.size() <= MAX_DESCRIPTORS_PER_MESSAGE); + return true; +} + +int FileDescriptorSet::GetDescriptorAt(unsigned index) const { + if (index >= descriptors_.size()) + return -1; + + // We should always walk the descriptors in order, so it's reasonable to + // enforce this. Consider the case where a compromised renderer sends us + // the following message: + // + // ExampleMsg: + // num_fds:2 msg:FD(index = 1) control:SCM_RIGHTS {n, m} + // + // Here the renderer sent us a message which should have a descriptor, but + // actually sent two in an attempt to fill our fd table and kill us. By + // setting the index of the descriptor in the message to 1 (it should be + // 0), we would record a highwater of 1 and then consider all the + // descriptors to have been used. + // + // So we can either track of the use of each descriptor in a bitset, or we + // can enforce that we walk the indexes strictly in order. + // + // There's one more wrinkle: When logging messages, we may reparse them. So + // we have an exception: When the consumed_descriptor_highwater_ is at the + // end of the array and index 0 is requested, we reset the highwater value. + if (index == 0 && consumed_descriptor_highwater_ == descriptors_.size()) + consumed_descriptor_highwater_ = 0; + + if (index != consumed_descriptor_highwater_) + return -1; + + consumed_descriptor_highwater_ = index + 1; + return descriptors_[index].fd; +} + +void FileDescriptorSet::GetDescriptors(int* buffer) const { + for (std::vector<base::FileDescriptor>::const_iterator + i = descriptors_.begin(); i != descriptors_.end(); ++i) { + *(buffer++) = i->fd; + } +} + +void FileDescriptorSet::CommitAll() { + for (std::vector<base::FileDescriptor>::iterator + i = descriptors_.begin(); i != descriptors_.end(); ++i) { + if (i->auto_close) + HANDLE_EINTR(close(i->fd)); + } + descriptors_.clear(); + consumed_descriptor_highwater_ = 0; +} + +void FileDescriptorSet::SetDescriptors(const int* buffer, unsigned count) { + DCHECK_LE(count, MAX_DESCRIPTORS_PER_MESSAGE); + DCHECK_EQ(descriptors_.size(), 0u); + DCHECK_EQ(consumed_descriptor_highwater_, 0u); + + descriptors_.reserve(count); + for (unsigned i = 0; i < count; ++i) { + struct base::FileDescriptor sd; + sd.fd = buffer[i]; + sd.auto_close = true; + descriptors_.push_back(sd); + } +} diff --git a/ipc/chromium/src/chrome/common/file_descriptor_set_posix.h b/ipc/chromium/src/chrome/common/file_descriptor_set_posix.h new file mode 100644 index 000000000..b6b153fb1 --- /dev/null +++ b/ipc/chromium/src/chrome/common/file_descriptor_set_posix.h @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2009 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. + +#ifndef CHROME_COMMON_FILE_DESCRIPTOR_SET_POSIX_H_ +#define CHROME_COMMON_FILE_DESCRIPTOR_SET_POSIX_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/file_descriptor_posix.h" +#include "nsISupportsImpl.h" + +// ----------------------------------------------------------------------------- +// A FileDescriptorSet is an ordered set of POSIX file descriptors. These are +// associated with IPC messages so that descriptors can be transmitted over a +// UNIX domain socket. +// ----------------------------------------------------------------------------- +class FileDescriptorSet { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileDescriptorSet) + FileDescriptorSet(); + + // Mac and Linux both limit the number of file descriptors per message to + // slightly more than 250. + enum { + MAX_DESCRIPTORS_PER_MESSAGE = 250 + }; + + // --------------------------------------------------------------------------- + // Interfaces for building during message serialisation... + + // Add a descriptor to the end of the set. Returns false iff the set is full. + bool Add(int fd); + // Add a descriptor to the end of the set and automatically close it after + // transmission. Returns false iff the set is full. + bool AddAndAutoClose(int fd); + + // --------------------------------------------------------------------------- + + + // --------------------------------------------------------------------------- + // Interfaces for accessing during message deserialisation... + + // Return the number of descriptors + unsigned size() const { return descriptors_.size(); } + // Return true if no unconsumed descriptors remain + bool empty() const { return descriptors_.empty(); } + // Fetch the nth descriptor from the beginning of the set. Code using this + // /must/ access the descriptors in order, except that it may wrap from the + // end to index 0 again. + // + // This interface is designed for the deserialising code as it doesn't + // support close flags. + // returns: file descriptor, or -1 on error + int GetDescriptorAt(unsigned n) const; + + // --------------------------------------------------------------------------- + + + // --------------------------------------------------------------------------- + // Interfaces for transmission... + + // Fill an array with file descriptors without 'consuming' them. CommitAll + // must be called after these descriptors have been transmitted. + // buffer: (output) a buffer of, at least, size() integers. + void GetDescriptors(int* buffer) const; + // This must be called after transmitting the descriptors returned by + // GetDescriptors. It marks all the descriptors as consumed and closes those + // which are auto-close. + void CommitAll(); + + // --------------------------------------------------------------------------- + + + // --------------------------------------------------------------------------- + // Interfaces for receiving... + + // Set the contents of the set from the given buffer. This set must be empty + // before calling. The auto-close flag is set on all the descriptors so that + // unconsumed descriptors are closed on destruction. + void SetDescriptors(const int* buffer, unsigned count); + + // --------------------------------------------------------------------------- + + private: + ~FileDescriptorSet(); + + // A vector of descriptors and close flags. If this message is sent, then + // these descriptors are sent as control data. After sending, any descriptors + // with a true flag are closed. If this message has been received, then these + // are the descriptors which were received and all close flags are true. + std::vector<base::FileDescriptor> descriptors_; + + // This contains the index of the next descriptor which should be consumed. + // It's used in a couple of ways. Firstly, at destruction we can check that + // all the descriptors have been read (with GetNthDescriptor). Secondly, we + // can check that they are read in order. + mutable unsigned consumed_descriptor_highwater_; + + DISALLOW_COPY_AND_ASSIGN(FileDescriptorSet); +}; + +#endif // CHROME_COMMON_FILE_DESCRIPTOR_SET_POSIX_H_ diff --git a/ipc/chromium/src/chrome/common/ipc_channel.cc b/ipc/chromium/src/chrome/common/ipc_channel.cc new file mode 100644 index 000000000..3d24d57b5 --- /dev/null +++ b/ipc/chromium/src/chrome/common/ipc_channel.cc @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2012 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 "chrome/common/ipc_channel.h" + +#include <limits> + +#include "base/atomic_sequence_num.h" +#include "base/process_util.h" +#include "base/rand_util.h" +#include "base/string_util.h" + +namespace { + +// Global atomic used to guarantee channel IDs are unique. +base::StaticAtomicSequenceNumber g_last_id; + +} // namespace + +namespace IPC { + +// static +std::wstring Channel::GenerateUniqueRandomChannelID() { + // Note: the string must start with the current process id, this is how + // some child processes determine the pid of the parent. + // + // This is composed of a unique incremental identifier, the process ID of + // the creator, an identifier for the child instance, and a strong random + // component. The strong random component prevents other processes from + // hijacking or squatting on predictable channel names. + + return StringPrintf(L"%d.%u.%d", + base::GetCurrentProcId(), + g_last_id.GetNext(), + base::RandInt(0, std::numeric_limits<int32_t>::max())); +} + +} // namespace IPC
\ No newline at end of file diff --git a/ipc/chromium/src/chrome/common/ipc_channel.h b/ipc/chromium/src/chrome/common/ipc_channel.h new file mode 100644 index 000000000..e6b45986e --- /dev/null +++ b/ipc/chromium/src/chrome/common/ipc_channel.h @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + +#ifndef CHROME_COMMON_IPC_CHANNEL_H_ +#define CHROME_COMMON_IPC_CHANNEL_H_ + +#include <string> + +#include <queue> +#include "chrome/common/ipc_message.h" + +namespace IPC { + +//------------------------------------------------------------------------------ + +class Channel { + // Security tests need access to the pipe handle. + friend class ChannelTest; + + public: + // Implemented by consumers of a Channel to receive messages. + class Listener { + public: + virtual ~Listener() {} + + // Called when a message is received. + virtual void OnMessageReceived(Message&& message) = 0; + + // Called when the channel is connected and we have received the internal + // Hello message from the peer. + virtual void OnChannelConnected(int32_t peer_pid) {} + + // Called when an error is detected that causes the channel to close. + // This method is not called when a channel is closed normally. + virtual void OnChannelError() {} + + // If the listener has queued messages, swap them for |queue| like so + // swap(impl->my_queued_messages, queue); + virtual void GetQueuedMessages(std::queue<Message>& queue) {} + }; + + enum Mode { + MODE_SERVER, + MODE_CLIENT + }; + + enum { + // The maximum message size in bytes. Attempting to receive a + // message of this size or bigger results in a channel error. + kMaximumMessageSize = 256 * 1024 * 1024, + + // Ammount of data to read at once from the pipe. + kReadBufferSize = 4 * 1024, + + // Maximum size of a message that we allow to be copied (rather than moved). + kMaxCopySize = 32 * 1024, + }; + + // Initialize a Channel. + // + // |channel_id| identifies the communication Channel. + // |mode| specifies whether this Channel is to operate in server mode or + // client mode. In server mode, the Channel is responsible for setting up the + // IPC object, whereas in client mode, the Channel merely connects to the + // already established IPC object. + // |listener| receives a callback on the current thread for each newly + // received message. + // + Channel(const std::wstring& channel_id, Mode mode, Listener* listener); + + // XXX it would nice not to have yet more platform-specific code in + // here but it's just not worth the trouble. +# if defined(OS_POSIX) + // Connect to a pre-created channel |fd| as |mode|. + Channel(int fd, Mode mode, Listener* listener); +# elif defined(OS_WIN) + // Connect to a pre-created channel as |mode|. Clients connect to + // the pre-existing server pipe, and servers take over |server_pipe|. + Channel(const std::wstring& channel_id, void* server_pipe, + Mode mode, Listener* listener); +# endif + + ~Channel(); + + // Connect the pipe. On the server side, this will initiate + // waiting for connections. On the client, it attempts to + // connect to a pre-existing pipe. Note, calling Connect() + // will not block the calling thread and may complete + // asynchronously. + bool Connect(); + + // Close this Channel explicitly. May be called multiple times. + void Close(); + + // Modify the Channel's listener. + Listener* set_listener(Listener* listener); + + // Send a message over the Channel to the listener on the other end. + // + // |message| must be allocated using operator new. This object will be + // deleted once the contents of the Message have been sent. + // + // If you Send() a message on a Close()'d channel, we delete the message + // immediately. + bool Send(Message* message); + + // Unsound_IsClosed() and Unsound_NumQueuedMessages() are safe to call from + // any thread, but the value returned may be out of date, because we don't + // use any synchronization when reading or writing it. + bool Unsound_IsClosed() const; + uint32_t Unsound_NumQueuedMessages() const; + +#if defined(OS_POSIX) + // On POSIX an IPC::Channel wraps a socketpair(), this method returns the + // FD # for the client end of the socket and the equivalent FD# to use for + // mapping it into the Child process. + // This method may only be called on the server side of a channel. + // + // If the kTestingChannelID flag is specified on the command line then + // a named FIFO is used as the channel transport mechanism rather than a + // socketpair() in which case this method returns -1 for both parameters. + void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const; + + // Return the file descriptor for communication with the peer. + int GetFileDescriptor() const; + + // Reset the file descriptor for communication with the peer. + void ResetFileDescriptor(int fd); + + // Close the client side of the socketpair. + void CloseClientFileDescriptor(); + +#elif defined(OS_WIN) + // Return the server pipe handle. + void* GetServerPipeHandle() const; +#endif // defined(OS_POSIX) + + // Generates a channel ID that's non-predictable and unique. + static std::wstring GenerateUniqueRandomChannelID(); + + // Generates a channel ID that, if passed to the client as a shared secret, + // will validate that the client's authenticity. On platforms that do not + // require additional validation this is simply calls GenerateUniqueRandomChannelID(). + // For portability the prefix should not include the \ character. + static std::wstring GenerateVerifiedChannelID(const std::wstring& prefix); + + private: + // PIMPL to which all channel calls are delegated. + class ChannelImpl; + ChannelImpl *channel_impl_; + + enum { +#if defined(OS_MACOSX) + // If the channel receives a message that contains file descriptors, then + // it will reply back with this message, indicating that the message has + // been received. The sending channel can then close any descriptors that + // had been marked as auto_close. This works around a sendmsg() bug on BSD + // where the kernel can eagerly close file descriptors that are in message + // queues but not yet delivered. + RECEIVED_FDS_MESSAGE_TYPE = kuint16max - 1, +#endif + + // The Hello message is internal to the Channel class. It is sent + // by the peer when the channel is connected. The message contains + // just the process id (pid). The message has a special routing_id + // (MSG_ROUTING_NONE) and type (HELLO_MESSAGE_TYPE). + HELLO_MESSAGE_TYPE = kuint16max // Maximum value of message type (uint16_t), + // to avoid conflicting with normal + // message types, which are enumeration + // constants starting from 0. + }; +}; + +} // namespace IPC + +#endif // CHROME_COMMON_IPC_CHANNEL_H_ diff --git a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc new file mode 100644 index 000000000..0d3a2b16c --- /dev/null +++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc @@ -0,0 +1,958 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 "chrome/common/ipc_channel_posix.h" + +#include <errno.h> +#include <fcntl.h> +#if defined(OS_MACOSX) +#include <sched.h> +#endif +#include <stddef.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <sys/uio.h> + +#include <string> +#include <map> + +#include "base/command_line.h" +#include "base/eintr_wrapper.h" +#include "base/lock.h" +#include "base/logging.h" +#include "base/process_util.h" +#include "base/string_util.h" +#include "base/singleton.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/file_descriptor_set_posix.h" +#include "chrome/common/ipc_message_utils.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/UniquePtr.h" + +#ifdef MOZ_FAULTY +#include "mozilla/ipc/Faulty.h" +#endif + +// Work around possible OS limitations. +static const size_t kMaxIOVecSize = 256; + +#ifdef MOZ_TASK_TRACER +#include "GeckoTaskTracerImpl.h" +using namespace mozilla::tasktracer; +#endif + +namespace IPC { + +// IPC channels on Windows use named pipes (CreateNamedPipe()) with +// channel ids as the pipe names. Channels on POSIX use anonymous +// Unix domain sockets created via socketpair() as pipes. These don't +// quite line up. +// +// When creating a child subprocess, the parent side of the fork +// arranges it such that the initial control channel ends up on the +// magic file descriptor kClientChannelFd in the child. Future +// connections (file descriptors) can then be passed via that +// connection via sendmsg(). + +//------------------------------------------------------------------------------ +namespace { + +// The PipeMap class works around this quirk related to unit tests: +// +// When running as a server, we install the client socket in a +// specific file descriptor number (@kClientChannelFd). However, we +// also have to support the case where we are running unittests in the +// same process. (We do not support forking without execing.) +// +// Case 1: normal running +// The IPC server object will install a mapping in PipeMap from the +// name which it was given to the client pipe. When forking the client, the +// GetClientFileDescriptorMapping will ensure that the socket is installed in +// the magic slot (@kClientChannelFd). The client will search for the +// mapping, but it won't find any since we are in a new process. Thus the +// magic fd number is returned. Once the client connects, the server will +// close its copy of the client socket and remove the mapping. +// +// Case 2: unittests - client and server in the same process +// The IPC server will install a mapping as before. The client will search +// for a mapping and find out. It duplicates the file descriptor and +// connects. Once the client connects, the server will close the original +// copy of the client socket and remove the mapping. Thus, when the client +// object closes, it will close the only remaining copy of the client socket +// in the fd table and the server will see EOF on its side. +// +// TODO(port): a client process cannot connect to multiple IPC channels with +// this scheme. + +class PipeMap { + public: + // Lookup a given channel id. Return -1 if not found. + int Lookup(const std::string& channel_id) { + AutoLock locked(lock_); + + ChannelToFDMap::const_iterator i = map_.find(channel_id); + if (i == map_.end()) + return -1; + return i->second; + } + + // Remove the mapping for the given channel id. No error is signaled if the + // channel_id doesn't exist + void Remove(const std::string& channel_id) { + AutoLock locked(lock_); + + ChannelToFDMap::iterator i = map_.find(channel_id); + if (i != map_.end()) + map_.erase(i); + } + + // Insert a mapping from @channel_id to @fd. It's a fatal error to insert a + // mapping if one already exists for the given channel_id + void Insert(const std::string& channel_id, int fd) { + AutoLock locked(lock_); + DCHECK(fd != -1); + + ChannelToFDMap::const_iterator i = map_.find(channel_id); + CHECK(i == map_.end()) << "Creating second IPC server for '" + << channel_id + << "' while first still exists"; + map_[channel_id] = fd; + } + + private: + Lock lock_; + typedef std::map<std::string, int> ChannelToFDMap; + ChannelToFDMap map_; +}; + +// This is the file descriptor number that a client process expects to find its +// IPC socket. +static const int kClientChannelFd = 3; + +// Used to map a channel name to the equivalent FD # in the client process. +int ChannelNameToClientFD(const std::string& channel_id) { + // See the large block comment above PipeMap for the reasoning here. + const int fd = Singleton<PipeMap>()->Lookup(channel_id); + if (fd != -1) + return dup(fd); + + // If we don't find an entry, we assume that the correct value has been + // inserted in the magic slot. + return kClientChannelFd; +} + +//------------------------------------------------------------------------------ +const size_t kMaxPipeNameLength = sizeof(((sockaddr_un*)0)->sun_path); + +bool SetCloseOnExec(int fd) { + int flags = fcntl(fd, F_GETFD); + if (flags == -1) + return false; + + flags |= FD_CLOEXEC; + if (fcntl(fd, F_SETFD, flags) == -1) + return false; + + return true; +} + +} // namespace +//------------------------------------------------------------------------------ + +Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode, + Listener* listener) + : factory_(this) { + Init(mode, listener); + + if (!CreatePipe(channel_id, mode)) { + // The pipe may have been closed already. + CHROMIUM_LOG(WARNING) << "Unable to create pipe named \"" << channel_id << + "\" in " << (mode == MODE_SERVER ? "server" : "client") << + " mode error(" << strerror(errno) << ")."; + } +} + +Channel::ChannelImpl::ChannelImpl(int fd, Mode mode, Listener* listener) + : factory_(this) { + Init(mode, listener); + pipe_ = fd; + waiting_connect_ = (MODE_SERVER == mode); + + EnqueueHelloMessage(); +} + +void Channel::ChannelImpl::Init(Mode mode, Listener* listener) { + DCHECK(kControlBufferSlopBytes >= CMSG_SPACE(0)); + + mode_ = mode; + is_blocked_on_write_ = false; + partial_write_iter_.reset(); + input_buf_offset_ = 0; + server_listen_pipe_ = -1; + pipe_ = -1; + client_pipe_ = -1; + listener_ = listener; + waiting_connect_ = true; + processing_incoming_ = false; + closed_ = false; +#if defined(OS_MACOSX) + last_pending_fd_id_ = 0; +#endif + output_queue_length_ = 0; +} + +bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id, + Mode mode) { + DCHECK(server_listen_pipe_ == -1 && pipe_ == -1); + + // socketpair() + pipe_name_ = WideToASCII(channel_id); + if (mode == MODE_SERVER) { + int pipe_fds[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) != 0) { + mozilla::ipc::AnnotateCrashReportWithErrno("IpcCreatePipeSocketPairErrno", errno); + return false; + } + // Set both ends to be non-blocking. + if (fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK) == -1 || + fcntl(pipe_fds[1], F_SETFL, O_NONBLOCK) == -1) { + mozilla::ipc::AnnotateCrashReportWithErrno("IpcCreatePipeFcntlErrno", errno); + HANDLE_EINTR(close(pipe_fds[0])); + HANDLE_EINTR(close(pipe_fds[1])); + return false; + } + + if (!SetCloseOnExec(pipe_fds[0]) || + !SetCloseOnExec(pipe_fds[1])) { + mozilla::ipc::AnnotateCrashReportWithErrno("IpcCreatePipeCloExecErrno", errno); + HANDLE_EINTR(close(pipe_fds[0])); + HANDLE_EINTR(close(pipe_fds[1])); + return false; + } + + pipe_ = pipe_fds[0]; + client_pipe_ = pipe_fds[1]; + + if (pipe_name_.length()) { + Singleton<PipeMap>()->Insert(pipe_name_, client_pipe_); + } + } else { + pipe_ = ChannelNameToClientFD(pipe_name_); + DCHECK(pipe_ > 0); + waiting_connect_ = false; + } + + // Create the Hello message to be sent when Connect is called + return EnqueueHelloMessage(); +} + +/** + * Reset the file descriptor for communication with the peer. + */ +void Channel::ChannelImpl::ResetFileDescriptor(int fd) { + NS_ASSERTION(fd > 0 && fd == pipe_, "Invalid file descriptor"); + + EnqueueHelloMessage(); +} + +bool Channel::ChannelImpl::EnqueueHelloMessage() { + mozilla::UniquePtr<Message> msg(new Message(MSG_ROUTING_NONE, + HELLO_MESSAGE_TYPE)); + if (!msg->WriteInt(base::GetCurrentProcId())) { + Close(); + return false; + } + + OutputQueuePush(msg.release()); + return true; +} + +bool Channel::ChannelImpl::Connect() { + if (pipe_ == -1) { + return false; + } + + MessageLoopForIO::current()->WatchFileDescriptor( + pipe_, + true, + MessageLoopForIO::WATCH_READ, + &read_watcher_, + this); + waiting_connect_ = false; + + if (!waiting_connect_) + return ProcessOutgoingMessages(); + return true; +} + +bool Channel::ChannelImpl::ProcessIncomingMessages() { + struct msghdr msg = {0}; + struct iovec iov; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = input_cmsg_buf_; + + for (;;) { + msg.msg_controllen = sizeof(input_cmsg_buf_); + + if (pipe_ == -1) + return false; + + // In some cases the beginning of a message will be stored in input_buf_. We + // don't want to overwrite that, so we store the new data after it. + iov.iov_base = input_buf_ + input_buf_offset_; + iov.iov_len = Channel::kReadBufferSize - input_buf_offset_; + + // Read from pipe. + // recvmsg() returns 0 if the connection has closed or EAGAIN if no data + // is waiting on the pipe. + ssize_t bytes_read = HANDLE_EINTR(recvmsg(pipe_, &msg, MSG_DONTWAIT)); + + if (bytes_read < 0) { + if (errno == EAGAIN) { + return true; + } else { + CHROMIUM_LOG(ERROR) << "pipe error (" << pipe_ << "): " << strerror(errno); + return false; + } + } else if (bytes_read == 0) { + // The pipe has closed... + Close(); + return false; + } + DCHECK(bytes_read); + + if (client_pipe_ != -1) { + Singleton<PipeMap>()->Remove(pipe_name_); + HANDLE_EINTR(close(client_pipe_)); + client_pipe_ = -1; + } + + // a pointer to an array of |num_wire_fds| file descriptors from the read + const int* wire_fds = NULL; + unsigned num_wire_fds = 0; + + // walk the list of control messages and, if we find an array of file + // descriptors, save a pointer to the array + + // This next if statement is to work around an OSX issue where + // CMSG_FIRSTHDR will return non-NULL in the case that controllen == 0. + // Here's a test case: + // + // int main() { + // struct msghdr msg; + // msg.msg_control = &msg; + // msg.msg_controllen = 0; + // if (CMSG_FIRSTHDR(&msg)) + // printf("Bug found!\n"); + // } + if (msg.msg_controllen > 0) { + // On OSX, CMSG_FIRSTHDR doesn't handle the case where controllen is 0 + // and will return a pointer into nowhere. + for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0); + DCHECK(payload_len % sizeof(int) == 0); + wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); + num_wire_fds = payload_len / 4; + + if (msg.msg_flags & MSG_CTRUNC) { + CHROMIUM_LOG(ERROR) << "SCM_RIGHTS message was truncated" + << " cmsg_len:" << cmsg->cmsg_len + << " fd:" << pipe_; + for (unsigned i = 0; i < num_wire_fds; ++i) + HANDLE_EINTR(close(wire_fds[i])); + return false; + } + break; + } + } + } + + // Process messages from input buffer. + const char *p = input_buf_; + const char *end = input_buf_ + input_buf_offset_ + bytes_read; + + // A pointer to an array of |num_fds| file descriptors which includes any + // fds that have spilled over from a previous read. + const int* fds; + unsigned num_fds; + unsigned fds_i = 0; // the index of the first unused descriptor + + if (input_overflow_fds_.empty()) { + fds = wire_fds; + num_fds = num_wire_fds; + } else { + const size_t prev_size = input_overflow_fds_.size(); + input_overflow_fds_.resize(prev_size + num_wire_fds); + memcpy(&input_overflow_fds_[prev_size], wire_fds, + num_wire_fds * sizeof(int)); + fds = &input_overflow_fds_[0]; + num_fds = input_overflow_fds_.size(); + } + + // The data for the message we're currently reading consists of any data + // stored in incoming_message_ followed by data in input_buf_ (followed by + // other messages). + + while (p < end) { + // Try to figure out how big the message is. Size is 0 if we haven't read + // enough of the header to know the size. + uint32_t message_length = 0; + if (incoming_message_.isSome()) { + message_length = incoming_message_.ref().size(); + } else { + message_length = Message::MessageSize(p, end); + } + + if (!message_length) { + // We haven't seen the full message header. + MOZ_ASSERT(incoming_message_.isNothing()); + + // Move everything we have to the start of the buffer. We'll finish + // reading this message when we get more data. For now we leave it in + // input_buf_. + memmove(input_buf_, p, end - p); + input_buf_offset_ = end - p; + + break; + } + + input_buf_offset_ = 0; + + bool partial; + if (incoming_message_.isSome()) { + // We already have some data for this message stored in + // incoming_message_. We want to append the new data there. + Message& m = incoming_message_.ref(); + + // How much data from this message remains to be added to + // incoming_message_? + MOZ_ASSERT(message_length > m.CurrentSize()); + uint32_t remaining = message_length - m.CurrentSize(); + + // How much data from this message is stored in input_buf_? + uint32_t in_buf = std::min(remaining, uint32_t(end - p)); + + m.InputBytes(p, in_buf); + p += in_buf; + + // Are we done reading this message? + partial = in_buf != remaining; + } else { + // How much data from this message is stored in input_buf_? + uint32_t in_buf = std::min(message_length, uint32_t(end - p)); + + incoming_message_.emplace(p, in_buf); + p += in_buf; + + // Are we done reading this message? + partial = in_buf != message_length; + } + + if (partial) { + break; + } + + Message& m = incoming_message_.ref(); + + if (m.header()->num_fds) { + // the message has file descriptors + const char* error = NULL; + if (m.header()->num_fds > num_fds - fds_i) { + // the message has been completely received, but we didn't get + // enough file descriptors. + error = "Message needs unreceived descriptors"; + } + + if (m.header()->num_fds > + FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) { + // There are too many descriptors in this message + error = "Message requires an excessive number of descriptors"; + } + + if (error) { + CHROMIUM_LOG(WARNING) << error + << " channel:" << this + << " message-type:" << m.type() + << " header()->num_fds:" << m.header()->num_fds + << " num_fds:" << num_fds + << " fds_i:" << fds_i; + // close the existing file descriptors so that we don't leak them + for (unsigned i = fds_i; i < num_fds; ++i) + HANDLE_EINTR(close(fds[i])); + input_overflow_fds_.clear(); + // abort the connection + return false; + } + +#if defined(OS_MACOSX) + // Send a message to the other side, indicating that we are now + // responsible for closing the descriptor. + Message *fdAck = new Message(MSG_ROUTING_NONE, + RECEIVED_FDS_MESSAGE_TYPE); + DCHECK(m.fd_cookie() != 0); + fdAck->set_fd_cookie(m.fd_cookie()); + OutputQueuePush(fdAck); +#endif + + m.file_descriptor_set()->SetDescriptors( + &fds[fds_i], m.header()->num_fds); + fds_i += m.header()->num_fds; + } +#ifdef IPC_MESSAGE_DEBUG_EXTRA + DLOG(INFO) << "received message on channel @" << this << + " with type " << m.type(); +#endif + +#ifdef MOZ_TASK_TRACER + AutoSaveCurTraceInfo saveCurTraceInfo; + SetCurTraceInfo(m.header()->source_event_id, + m.header()->parent_task_id, + m.header()->source_event_type); +#endif + + if (m.routing_id() == MSG_ROUTING_NONE && + m.type() == HELLO_MESSAGE_TYPE) { + // The Hello message contains only the process id. + listener_->OnChannelConnected(MessageIterator(m).NextInt()); +#if defined(OS_MACOSX) + } else if (m.routing_id() == MSG_ROUTING_NONE && + m.type() == RECEIVED_FDS_MESSAGE_TYPE) { + DCHECK(m.fd_cookie() != 0); + CloseDescriptors(m.fd_cookie()); +#endif + } else { + listener_->OnMessageReceived(mozilla::Move(m)); + } + + incoming_message_.reset(); + } + + input_overflow_fds_ = std::vector<int>(&fds[fds_i], &fds[num_fds]); + + // When the input data buffer is empty, the overflow fds should be too. If + // this is not the case, we probably have a rogue renderer which is trying + // to fill our descriptor table. + if (incoming_message_.isNothing() && input_buf_offset_ == 0 && !input_overflow_fds_.empty()) { + // We close these descriptors in Close() + return false; + } + } + + return true; +} + +bool Channel::ChannelImpl::ProcessOutgoingMessages() { + DCHECK(!waiting_connect_); // Why are we trying to send messages if there's + // no connection? + is_blocked_on_write_ = false; + + if (output_queue_.empty()) + return true; + + if (pipe_ == -1) + return false; + + // Write out all the messages we can till the write blocks or there are no + // more outgoing messages. + while (!output_queue_.empty()) { +#ifdef MOZ_FAULTY + Singleton<mozilla::ipc::Faulty>::get()->MaybeCollectAndClosePipe(pipe_); +#endif + Message* msg = output_queue_.front(); + + struct msghdr msgh = {0}; + + static const int tmp = CMSG_SPACE(sizeof( + int[FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE])); + char buf[tmp]; + + if (partial_write_iter_.isNothing()) { + Pickle::BufferList::IterImpl iter(msg->Buffers()); + partial_write_iter_.emplace(iter); + } + + if (partial_write_iter_.value().Data() == msg->Buffers().Start() && + !msg->file_descriptor_set()->empty()) { + // This is the first chunk of a message which has descriptors to send + struct cmsghdr *cmsg; + const unsigned num_fds = msg->file_descriptor_set()->size(); + + if (num_fds > FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) { + CHROMIUM_LOG(FATAL) << "Too many file descriptors!"; + // This should not be reached. + return false; + } + + msgh.msg_control = buf; + msgh.msg_controllen = CMSG_SPACE(sizeof(int) * num_fds); + cmsg = CMSG_FIRSTHDR(&msgh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds); + msg->file_descriptor_set()->GetDescriptors( + reinterpret_cast<int*>(CMSG_DATA(cmsg))); + msgh.msg_controllen = cmsg->cmsg_len; + + msg->header()->num_fds = num_fds; +#if defined(OS_MACOSX) + msg->set_fd_cookie(++last_pending_fd_id_); +#endif + } + + struct iovec iov[kMaxIOVecSize]; + size_t iov_count = 0; + size_t amt_to_write = 0; + + // How much of this message have we written so far? + Pickle::BufferList::IterImpl iter = partial_write_iter_.value(); + + // Store the unwritten part of the first segment to write into the iovec. + iov[0].iov_base = const_cast<char*>(iter.Data()); + iov[0].iov_len = iter.RemainingInSegment(); + amt_to_write += iov[0].iov_len; + iter.Advance(msg->Buffers(), iov[0].iov_len); + iov_count++; + + // Store remaining segments to write into iovec. + while (!iter.Done()) { + char* data = iter.Data(); + size_t size = iter.RemainingInSegment(); + + // Don't add more than kMaxIOVecSize to the iovec so that we avoid + // OS-dependent limits. + if (iov_count < kMaxIOVecSize) { + iov[iov_count].iov_base = data; + iov[iov_count].iov_len = size; + iov_count++; + } + amt_to_write += size; + iter.Advance(msg->Buffers(), size); + } + + msgh.msg_iov = iov; + msgh.msg_iovlen = iov_count; + + ssize_t bytes_written = HANDLE_EINTR(sendmsg(pipe_, &msgh, MSG_DONTWAIT)); + +#if !defined(OS_MACOSX) + // On OSX CommitAll gets called later, once we get the RECEIVED_FDS_MESSAGE_TYPE + // message. + if (bytes_written > 0) + msg->file_descriptor_set()->CommitAll(); +#endif + + if (bytes_written < 0) { + switch (errno) { + case EAGAIN: + // Not an error; the sendmsg would have blocked, so return to the + // event loop and try again later. + break; +#if defined(OS_MACOSX) + // (Note: this comment is copied from https://crrev.com/86c3d9ef4fdf6; + // see also bug 1142693 comment #73.) + // + // On OS X if sendmsg() is trying to send fds between processes and + // there isn't enough room in the output buffer to send the fd + // structure over atomically then EMSGSIZE is returned. + // + // EMSGSIZE presents a problem since the system APIs can only call us + // when there's room in the socket buffer and not when there is + // "enough" room. + // + // The current behavior is to return to the event loop when EMSGSIZE + // is received and hopefull service another FD. This is however still + // technically a busy wait since the event loop will call us right + // back until the receiver has read enough data to allow passing the + // FD over atomically. + case EMSGSIZE: + // Because this is likely to result in a busy-wait, we'll try to make + // it easier for the receiver to make progress. + sched_yield(); + break; +#endif + default: + CHROMIUM_LOG(ERROR) << "pipe error: " << strerror(errno); + return false; + } + } + + if (static_cast<size_t>(bytes_written) != amt_to_write) { + // If write() fails with EAGAIN then bytes_written will be -1. + if (bytes_written > 0) { + partial_write_iter_.ref().AdvanceAcrossSegments(msg->Buffers(), bytes_written); + } + + // Tell libevent to call us back once things are unblocked. + is_blocked_on_write_ = true; + MessageLoopForIO::current()->WatchFileDescriptor( + pipe_, + false, // One shot + MessageLoopForIO::WATCH_WRITE, + &write_watcher_, + this); + return true; + } else { + partial_write_iter_.reset(); + +#if defined(OS_MACOSX) + if (!msg->file_descriptor_set()->empty()) + pending_fds_.push_back(PendingDescriptors(msg->fd_cookie(), + msg->file_descriptor_set())); +#endif + + // Message sent OK! +#ifdef IPC_MESSAGE_DEBUG_EXTRA + DLOG(INFO) << "sent message @" << msg << " on channel @" << this << + " with type " << msg->type(); +#endif + OutputQueuePop(); + delete msg; + } + } + return true; +} + +bool Channel::ChannelImpl::Send(Message* message) { +#ifdef IPC_MESSAGE_DEBUG_EXTRA + DLOG(INFO) << "sending message @" << message << " on channel @" << this + << " with type " << message->type() + << " (" << output_queue_.size() << " in queue)"; +#endif + + + // If the channel has been closed, ProcessOutgoingMessages() is never going + // to pop anything off output_queue; output_queue will only get emptied when + // the channel is destructed. We might as well delete message now, instead + // of waiting for the channel to be destructed. + if (closed_) { + if (mozilla::ipc::LoggingEnabled()) { + fprintf(stderr, "Can't send message %s, because this channel is closed.\n", + message->name()); + } + delete message; + return false; + } + + OutputQueuePush(message); + if (!waiting_connect_) { + if (!is_blocked_on_write_) { + if (!ProcessOutgoingMessages()) + return false; + } + } + + return true; +} + +void Channel::ChannelImpl::GetClientFileDescriptorMapping(int *src_fd, + int *dest_fd) const { + DCHECK(mode_ == MODE_SERVER); + *src_fd = client_pipe_; + *dest_fd = kClientChannelFd; +} + +void Channel::ChannelImpl::CloseClientFileDescriptor() { + if (client_pipe_ != -1) { + Singleton<PipeMap>()->Remove(pipe_name_); + HANDLE_EINTR(close(client_pipe_)); + client_pipe_ = -1; + } +} + +// Called by libevent when we can read from th pipe without blocking. +void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) { + if (!waiting_connect_ && fd == pipe_) { + if (!ProcessIncomingMessages()) { + Close(); + listener_->OnChannelError(); + // The OnChannelError() call may delete this, so we need to exit now. + return; + } + } +} + +#if defined(OS_MACOSX) +void Channel::ChannelImpl::CloseDescriptors(uint32_t pending_fd_id) +{ + DCHECK(pending_fd_id != 0); + for (std::list<PendingDescriptors>::iterator i = pending_fds_.begin(); + i != pending_fds_.end(); + i++) { + if ((*i).id == pending_fd_id) { + (*i).fds->CommitAll(); + pending_fds_.erase(i); + return; + } + } + DCHECK(false) << "pending_fd_id not in our list!"; +} +#endif + +void Channel::ChannelImpl::OutputQueuePush(Message* msg) +{ +#ifdef MOZ_TASK_TRACER + // Save the current TaskTracer info into the message header. + GetCurTraceInfo(&msg->header()->source_event_id, + &msg->header()->parent_task_id, + &msg->header()->source_event_type); +#endif + output_queue_.push(msg); + output_queue_length_++; +} + +void Channel::ChannelImpl::OutputQueuePop() +{ + output_queue_.pop(); + output_queue_length_--; +} + +// Called by libevent when we can write to the pipe without blocking. +void Channel::ChannelImpl::OnFileCanWriteWithoutBlocking(int fd) { + if (!ProcessOutgoingMessages()) { + Close(); + listener_->OnChannelError(); + } +} + +void Channel::ChannelImpl::Close() { + // Close can be called multiple times, so we need to make sure we're + // idempotent. + + // Unregister libevent for the listening socket and close it. + server_listen_connection_watcher_.StopWatchingFileDescriptor(); + + if (server_listen_pipe_ != -1) { + HANDLE_EINTR(close(server_listen_pipe_)); + server_listen_pipe_ = -1; + } + + // Unregister libevent for the FIFO and close it. + read_watcher_.StopWatchingFileDescriptor(); + write_watcher_.StopWatchingFileDescriptor(); + if (pipe_ != -1) { + HANDLE_EINTR(close(pipe_)); + pipe_ = -1; + } + if (client_pipe_ != -1) { + Singleton<PipeMap>()->Remove(pipe_name_); + HANDLE_EINTR(close(client_pipe_)); + client_pipe_ = -1; + } + + while (!output_queue_.empty()) { + Message* m = output_queue_.front(); + OutputQueuePop(); + delete m; + } + + // Close any outstanding, received file descriptors + for (std::vector<int>::iterator + i = input_overflow_fds_.begin(); i != input_overflow_fds_.end(); ++i) { + HANDLE_EINTR(close(*i)); + } + input_overflow_fds_.clear(); + +#if defined(OS_MACOSX) + for (std::list<PendingDescriptors>::iterator i = pending_fds_.begin(); + i != pending_fds_.end(); + i++) { + (*i).fds->CommitAll(); + } + pending_fds_.clear(); +#endif + + closed_ = true; +} + +bool Channel::ChannelImpl::Unsound_IsClosed() const +{ + return closed_; +} + +uint32_t Channel::ChannelImpl::Unsound_NumQueuedMessages() const +{ + return output_queue_length_; +} + +//------------------------------------------------------------------------------ +// Channel's methods simply call through to ChannelImpl. +Channel::Channel(const std::wstring& channel_id, Mode mode, + Listener* listener) + : channel_impl_(new ChannelImpl(channel_id, mode, listener)) { + MOZ_COUNT_CTOR(IPC::Channel); +} + +Channel::Channel(int fd, Mode mode, Listener* listener) + : channel_impl_(new ChannelImpl(fd, mode, listener)) { + MOZ_COUNT_CTOR(IPC::Channel); +} + +Channel::~Channel() { + MOZ_COUNT_DTOR(IPC::Channel); + delete channel_impl_; +} + +bool Channel::Connect() { + return channel_impl_->Connect(); +} + +void Channel::Close() { + channel_impl_->Close(); +} + +Channel::Listener* Channel::set_listener(Listener* listener) { + return channel_impl_->set_listener(listener); +} + +bool Channel::Send(Message* message) { + return channel_impl_->Send(message); +} + +void Channel::GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const { + return channel_impl_->GetClientFileDescriptorMapping(src_fd, dest_fd); +} + +void Channel::ResetFileDescriptor(int fd) { + channel_impl_->ResetFileDescriptor(fd); +} + +int Channel::GetFileDescriptor() const { + return channel_impl_->GetFileDescriptor(); +} + +void Channel::CloseClientFileDescriptor() { + channel_impl_->CloseClientFileDescriptor(); +} + +bool Channel::Unsound_IsClosed() const { + return channel_impl_->Unsound_IsClosed(); +} + +uint32_t Channel::Unsound_NumQueuedMessages() const { + return channel_impl_->Unsound_NumQueuedMessages(); +} + +// static +std::wstring Channel::GenerateVerifiedChannelID(const std::wstring& prefix) { + // A random name is sufficient validation on posix systems, so we don't need + // an additional shared secret. + + std::wstring id = prefix; + if (!id.empty()) + id.append(L"."); + + return id.append(GenerateUniqueRandomChannelID()); +} + +} // namespace IPC diff --git a/ipc/chromium/src/chrome/common/ipc_channel_posix.h b/ipc/chromium/src/chrome/common/ipc_channel_posix.h new file mode 100644 index 000000000..1dfa33057 --- /dev/null +++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.h @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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. + +#ifndef CHROME_COMMON_IPC_CHANNEL_POSIX_H_ +#define CHROME_COMMON_IPC_CHANNEL_POSIX_H_ + +#include "chrome/common/ipc_channel.h" + +#include <sys/socket.h> // for CMSG macros + +#include <queue> +#include <string> +#include <vector> +#include <list> + +#include "base/message_loop.h" +#include "base/task.h" +#include "chrome/common/file_descriptor_set_posix.h" + +#include "nsAutoPtr.h" + +#include "mozilla/Maybe.h" + +namespace IPC { + +// An implementation of ChannelImpl for POSIX systems that works via +// socketpairs. See the .cc file for an overview of the implementation. +class Channel::ChannelImpl : public MessageLoopForIO::Watcher { + public: + // Mirror methods of Channel, see ipc_channel.h for description. + ChannelImpl(const std::wstring& channel_id, Mode mode, Listener* listener); + ChannelImpl(int fd, Mode mode, Listener* listener); + ~ChannelImpl() { Close(); } + bool Connect(); + void Close(); + Listener* set_listener(Listener* listener) { + Listener* old = listener_; + listener_ = listener; + return old; + } + bool Send(Message* message); + void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const; + + void ResetFileDescriptor(int fd); + + int GetFileDescriptor() const { + return pipe_; + } + void CloseClientFileDescriptor(); + + // See the comment in ipc_channel.h for info on Unsound_IsClosed() and + // Unsound_NumQueuedMessages(). + bool Unsound_IsClosed() const; + uint32_t Unsound_NumQueuedMessages() const; + + private: + void Init(Mode mode, Listener* listener); + bool CreatePipe(const std::wstring& channel_id, Mode mode); + bool EnqueueHelloMessage(); + + bool ProcessIncomingMessages(); + bool ProcessOutgoingMessages(); + + // MessageLoopForIO::Watcher implementation. + virtual void OnFileCanReadWithoutBlocking(int fd); + virtual void OnFileCanWriteWithoutBlocking(int fd); + +#if defined(OS_MACOSX) + void CloseDescriptors(uint32_t pending_fd_id); +#endif + + void OutputQueuePush(Message* msg); + void OutputQueuePop(); + + Mode mode_; + + // After accepting one client connection on our server socket we want to + // stop listening. + MessageLoopForIO::FileDescriptorWatcher server_listen_connection_watcher_; + MessageLoopForIO::FileDescriptorWatcher read_watcher_; + MessageLoopForIO::FileDescriptorWatcher write_watcher_; + + // Indicates whether we're currently blocked waiting for a write to complete. + bool is_blocked_on_write_; + + // If sending a message blocks then we use this iterator to keep track of + // where in the message we are. It gets reset when the message is finished + // sending. + mozilla::Maybe<Pickle::BufferList::IterImpl> partial_write_iter_; + + int server_listen_pipe_; + int pipe_; + int client_pipe_; // The client end of our socketpair(). + + // The "name" of our pipe. On Windows this is the global identifier for + // the pipe. On POSIX it's used as a key in a local map of file descriptors. + std::string pipe_name_; + + Listener* listener_; + + // Messages to be sent are queued here. + std::queue<Message*> output_queue_; + + // We read from the pipe into this buffer + char input_buf_[Channel::kReadBufferSize]; + size_t input_buf_offset_; + + // We want input_cmsg_buf_ to be big enough to hold + // CMSG_SPACE(Channel::kReadBufferSize) bytes (see the comment below for an + // explanation of where Channel::kReadBufferSize comes from). However, + // CMSG_SPACE is apparently not a constant on Macs, so we can't use it in the + // array size. Consequently, we pick a number here that is at least + // CMSG_SPACE(0) on all platforms. And we assert at runtime, in + // Channel::ChannelImpl::Init, that it's big enough. + enum { + kControlBufferSlopBytes = 32 + }; + + // This is a control message buffer large enough to hold all the file + // descriptors that will be read in when reading Channel::kReadBufferSize + // bytes of data. Message::WriteFileDescriptor always writes one word of + // data for every file descriptor added to the message, so kReadBufferSize + // bytes of data can never be accompanied by more than + // kReadBufferSize / sizeof(int) file descriptors. Since a file descriptor + // takes sizeof(int) bytes, the control buffer must be + // Channel::kReadBufferSize bytes. We add kControlBufferSlopBytes bytes + // for the control header. + char input_cmsg_buf_[Channel::kReadBufferSize + kControlBufferSlopBytes]; + + // Large incoming messages that span multiple pipe buffers get built-up in the + // buffers of this message. + mozilla::Maybe<Message> incoming_message_; + std::vector<int> input_overflow_fds_; + + // In server-mode, we have to wait for the client to connect before we + // can begin reading. We make use of the input_state_ when performing + // the connect operation in overlapped mode. + bool waiting_connect_; + + // This flag is set when processing incoming messages. It is used to + // avoid recursing through ProcessIncomingMessages, which could cause + // problems. TODO(darin): make this unnecessary + bool processing_incoming_; + + // This flag is set after we've closed the channel. + bool closed_; + +#if defined(OS_MACOSX) + struct PendingDescriptors { + uint32_t id; + RefPtr<FileDescriptorSet> fds; + + PendingDescriptors() : id(0) { } + PendingDescriptors(uint32_t id, FileDescriptorSet *fds) + : id(id), + fds(fds) + { } + }; + + std::list<PendingDescriptors> pending_fds_; + + // A generation ID for RECEIVED_FD messages. + uint32_t last_pending_fd_id_; +#endif + + // This variable is updated so it matches output_queue_.size(), except we can + // read output_queue_length_ from any thread (if we're OK getting an + // occasional out-of-date or bogus value). We use output_queue_length_ to + // implement Unsound_NumQueuedMessages. + size_t output_queue_length_; + + ScopedRunnableMethodFactory<ChannelImpl> factory_; + + DISALLOW_COPY_AND_ASSIGN(ChannelImpl); +}; + +} // namespace IPC + +#endif // CHROME_COMMON_IPC_CHANNEL_POSIX_H_ diff --git a/ipc/chromium/src/chrome/common/ipc_channel_win.cc b/ipc/chromium/src/chrome/common/ipc_channel_win.cc new file mode 100644 index 000000000..158fb4df5 --- /dev/null +++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc @@ -0,0 +1,630 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "chrome/common/ipc_channel_win.h" + +#include <windows.h> +#include <sstream> + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/process_util.h" +#include "base/rand_util.h" +#include "base/string_util.h" +#include "base/win_util.h" +#include "chrome/common/ipc_message_utils.h" +#include "mozilla/ipc/ProtocolUtils.h" + +// ChannelImpl is used on the IPC thread, but constructed on a different thread, +// so it has to hold the nsAutoOwningThread as a pointer, and we need a slightly +// different macro. +#ifdef DEBUG +#define ASSERT_OWNINGTHREAD(_class) \ + if (nsAutoOwningThread* owningThread = _mOwningThread.get()) { \ + NS_CheckThreadSafe(owningThread->GetThread(), #_class " not thread-safe"); \ + } +#else +#define ASSERT_OWNINGTHREAD(_class) ((void)0) +#endif + +namespace IPC { +//------------------------------------------------------------------------------ + +Channel::ChannelImpl::State::State(ChannelImpl* channel) : is_pending(false) { + memset(&context.overlapped, 0, sizeof(context.overlapped)); + context.handler = channel; +} + +Channel::ChannelImpl::State::~State() { + COMPILE_ASSERT(!offsetof(Channel::ChannelImpl::State, context), + starts_with_io_context); +} + +//------------------------------------------------------------------------------ + +Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode, + Listener* listener) + : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)), + ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)), + ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)), + shared_secret_(0), + waiting_for_shared_secret_(false) { + Init(mode, listener); + + if (!CreatePipe(channel_id, mode)) { + // The pipe may have been closed already. + CHROMIUM_LOG(WARNING) << "Unable to create pipe named \"" << channel_id << + "\" in " << (mode == 0 ? "server" : "client") << " mode."; + } +} + +Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, + HANDLE server_pipe, + Mode mode, Listener* listener) + : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)), + ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)), + ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)), + shared_secret_(0), + waiting_for_shared_secret_(false) { + Init(mode, listener); + + if (mode == MODE_SERVER) { + // Use the existing handle that was dup'd to us + pipe_ = server_pipe; + EnqueueHelloMessage(); + } else { + // Take the normal init path to connect to the server pipe + CreatePipe(channel_id, mode); + } +} + +void Channel::ChannelImpl::Init(Mode mode, Listener* listener) { + pipe_ = INVALID_HANDLE_VALUE; + listener_ = listener; + waiting_connect_ = (mode == MODE_SERVER); + processing_incoming_ = false; + closed_ = false; + output_queue_length_ = 0; + input_buf_offset_ = 0; +} + +void Channel::ChannelImpl::OutputQueuePush(Message* msg) +{ + output_queue_.push(msg); + output_queue_length_++; +} + +void Channel::ChannelImpl::OutputQueuePop() +{ + output_queue_.pop(); + output_queue_length_--; +} + +HANDLE Channel::ChannelImpl::GetServerPipeHandle() const { + return pipe_; +} + +void Channel::ChannelImpl::Close() { + ASSERT_OWNINGTHREAD(ChannelImpl); + + bool waited = false; + if (input_state_.is_pending || output_state_.is_pending) { + CancelIo(pipe_); + waited = true; + } + + // Closing the handle at this point prevents us from issuing more requests + // form OnIOCompleted(). + if (pipe_ != INVALID_HANDLE_VALUE) { + CloseHandle(pipe_); + pipe_ = INVALID_HANDLE_VALUE; + } + + while (input_state_.is_pending || output_state_.is_pending) { + MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this); + } + + while (!output_queue_.empty()) { + Message* m = output_queue_.front(); + OutputQueuePop(); + delete m; + } + +#ifdef DEBUG + _mOwningThread = nullptr; +#endif + closed_ = true; +} + +bool Channel::ChannelImpl::Send(Message* message) { + ASSERT_OWNINGTHREAD(ChannelImpl); +#ifdef IPC_MESSAGE_DEBUG_EXTRA + DLOG(INFO) << "sending message @" << message << " on channel @" << this + << " with type " << message->type() + << " (" << output_queue_.size() << " in queue)"; +#endif + + + if (closed_) { + if (mozilla::ipc::LoggingEnabled()) { + fprintf(stderr, "Can't send message %s, because this channel is closed.\n", + message->name()); + } + delete message; + return false; + } + + OutputQueuePush(message); + // ensure waiting to write + if (!waiting_connect_) { + if (!output_state_.is_pending) { + if (!ProcessOutgoingMessages(NULL, 0)) + return false; + } + } + + return true; +} + +const std::wstring Channel::ChannelImpl::PipeName( + const std::wstring& channel_id, int32_t* secret) const { + MOZ_ASSERT(secret); + + std::wostringstream ss; + ss << L"\\\\.\\pipe\\chrome."; + + // Prevent the shared secret from ending up in the pipe name. + size_t index = channel_id.find_first_of(L'\\'); + if (index != std::string::npos) { + StringToInt(channel_id.substr(index + 1), secret); + ss << channel_id.substr(0, index - 1); + } else { + // This case is here to support predictable named pipes in tests. + *secret = 0; + ss << channel_id; + } + return ss.str(); +} + +bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id, + Mode mode) { + DCHECK(pipe_ == INVALID_HANDLE_VALUE); + const std::wstring pipe_name = PipeName(channel_id, &shared_secret_); + if (mode == MODE_SERVER) { + waiting_for_shared_secret_ = !!shared_secret_; + pipe_ = CreateNamedPipeW(pipe_name.c_str(), + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | + FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, + 1, // number of pipe instances + // output buffer size (XXX tune) + Channel::kReadBufferSize, + // input buffer size (XXX tune) + Channel::kReadBufferSize, + 5000, // timeout in milliseconds (XXX tune) + NULL); + } else { + pipe_ = CreateFileW(pipe_name.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION | + FILE_FLAG_OVERLAPPED, + NULL); + } + if (pipe_ == INVALID_HANDLE_VALUE) { + // If this process is being closed, the pipe may be gone already. + CHROMIUM_LOG(WARNING) << "failed to create pipe: " << GetLastError(); + return false; + } + + // Create the Hello message to be sent when Connect is called + return EnqueueHelloMessage(); +} + +bool Channel::ChannelImpl::EnqueueHelloMessage() { + mozilla::UniquePtr<Message> m = mozilla::MakeUnique<Message>(MSG_ROUTING_NONE, + HELLO_MESSAGE_TYPE); + + // If we're waiting for our shared secret from the other end's hello message + // then don't give the game away by sending it in ours. + int32_t secret = waiting_for_shared_secret_ ? 0 : shared_secret_; + + // Also, don't send if the value is zero (for IPC backwards compatability). + if (!m->WriteInt(GetCurrentProcessId()) || + (secret && !m->WriteUInt32(secret))) + { + CloseHandle(pipe_); + pipe_ = INVALID_HANDLE_VALUE; + return false; + } + + OutputQueuePush(m.release()); + return true; +} + +bool Channel::ChannelImpl::Connect() { +#ifdef DEBUG + if (!_mOwningThread) { + _mOwningThread = mozilla::MakeUnique<nsAutoOwningThread>(); + } +#endif + + if (pipe_ == INVALID_HANDLE_VALUE) + return false; + + MessageLoopForIO::current()->RegisterIOHandler(pipe_, this); + + // Check to see if there is a client connected to our pipe... + if (waiting_connect_) + ProcessConnection(); + + if (!input_state_.is_pending) { + // Complete setup asynchronously. By not setting input_state_.is_pending + // to true, we indicate to OnIOCompleted that this is the special + // initialization signal. + MessageLoopForIO::current()->PostTask(factory_.NewRunnableMethod( + &Channel::ChannelImpl::OnIOCompleted, &input_state_.context, 0, 0)); + } + + if (!waiting_connect_) + ProcessOutgoingMessages(NULL, 0); + return true; +} + +bool Channel::ChannelImpl::ProcessConnection() { + ASSERT_OWNINGTHREAD(ChannelImpl); + if (input_state_.is_pending) + input_state_.is_pending = false; + + // Do we have a client connected to our pipe? + if (INVALID_HANDLE_VALUE == pipe_) + return false; + + BOOL ok = ConnectNamedPipe(pipe_, &input_state_.context.overlapped); + + DWORD err = GetLastError(); + if (ok) { + // Uhm, the API documentation says that this function should never + // return success when used in overlapped mode. + NOTREACHED(); + return false; + } + + switch (err) { + case ERROR_IO_PENDING: + input_state_.is_pending = true; + break; + case ERROR_PIPE_CONNECTED: + waiting_connect_ = false; + break; + default: + NOTREACHED(); + return false; + } + + return true; +} + +bool Channel::ChannelImpl::ProcessIncomingMessages( + MessageLoopForIO::IOContext* context, + DWORD bytes_read) { + ASSERT_OWNINGTHREAD(ChannelImpl); + if (input_state_.is_pending) { + input_state_.is_pending = false; + DCHECK(context); + + if (!context || !bytes_read) + return false; + } else { + // This happens at channel initialization. + DCHECK(!bytes_read && context == &input_state_.context); + } + + for (;;) { + if (bytes_read == 0) { + if (INVALID_HANDLE_VALUE == pipe_) + return false; + + // Read from pipe... + BOOL ok = ReadFile(pipe_, + input_buf_ + input_buf_offset_, + Channel::kReadBufferSize - input_buf_offset_, + &bytes_read, + &input_state_.context.overlapped); + if (!ok) { + DWORD err = GetLastError(); + if (err == ERROR_IO_PENDING) { + input_state_.is_pending = true; + return true; + } + CHROMIUM_LOG(ERROR) << "pipe error: " << err; + return false; + } + input_state_.is_pending = true; + return true; + } + DCHECK(bytes_read); + + // Process messages from input buffer. + + const char *p = input_buf_; + const char *end = input_buf_ + input_buf_offset_ + bytes_read; + + while (p < end) { + // Try to figure out how big the message is. Size is 0 if we haven't read + // enough of the header to know the size. + uint32_t message_length = 0; + if (incoming_message_.isSome()) { + message_length = incoming_message_.ref().size(); + } else { + message_length = Message::MessageSize(p, end); + } + + if (!message_length) { + // We haven't seen the full message header. + MOZ_ASSERT(incoming_message_.isNothing()); + + // Move everything we have to the start of the buffer. We'll finish + // reading this message when we get more data. For now we leave it in + // input_buf_. + memmove(input_buf_, p, end - p); + input_buf_offset_ = end - p; + + break; + } + + input_buf_offset_ = 0; + + bool partial; + if (incoming_message_.isSome()) { + // We already have some data for this message stored in + // incoming_message_. We want to append the new data there. + Message& m = incoming_message_.ref(); + + // How much data from this message remains to be added to + // incoming_message_? + MOZ_ASSERT(message_length > m.CurrentSize()); + uint32_t remaining = message_length - m.CurrentSize(); + + // How much data from this message is stored in input_buf_? + uint32_t in_buf = std::min(remaining, uint32_t(end - p)); + + m.InputBytes(p, in_buf); + p += in_buf; + + // Are we done reading this message? + partial = in_buf != remaining; + } else { + // How much data from this message is stored in input_buf_? + uint32_t in_buf = std::min(message_length, uint32_t(end - p)); + + incoming_message_.emplace(p, in_buf); + p += in_buf; + + // Are we done reading this message? + partial = in_buf != message_length; + } + + if (partial) { + break; + } + + Message& m = incoming_message_.ref(); + +#ifdef IPC_MESSAGE_DEBUG_EXTRA + DLOG(INFO) << "received message on channel @" << this << + " with type " << m.type(); +#endif + if (m.routing_id() == MSG_ROUTING_NONE && + m.type() == HELLO_MESSAGE_TYPE) { + // The Hello message contains the process id and must include the + // shared secret, if we are waiting for it. + MessageIterator it = MessageIterator(m); + int32_t claimed_pid = it.NextInt(); + if (waiting_for_shared_secret_ && (it.NextInt() != shared_secret_)) { + NOTREACHED(); + // Something went wrong. Abort connection. + Close(); + listener_->OnChannelError(); + return false; + } + waiting_for_shared_secret_ = false; + listener_->OnChannelConnected(claimed_pid); + } else { + listener_->OnMessageReceived(mozilla::Move(m)); + } + + incoming_message_.reset(); + } + + bytes_read = 0; // Get more data. + } + + return true; +} + +bool Channel::ChannelImpl::ProcessOutgoingMessages( + MessageLoopForIO::IOContext* context, + DWORD bytes_written) { + DCHECK(!waiting_connect_); // Why are we trying to send messages if there's + // no connection? + ASSERT_OWNINGTHREAD(ChannelImpl); + + if (output_state_.is_pending) { + DCHECK(context); + output_state_.is_pending = false; + if (!context || bytes_written == 0) { + DWORD err = GetLastError(); + CHROMIUM_LOG(ERROR) << "pipe error: " << err; + return false; + } + // Message was sent. + DCHECK(!output_queue_.empty()); + Message* m = output_queue_.front(); + + MOZ_RELEASE_ASSERT(partial_write_iter_.isSome()); + Pickle::BufferList::IterImpl& iter = partial_write_iter_.ref(); + iter.Advance(m->Buffers(), bytes_written); + if (iter.Done()) { + partial_write_iter_.reset(); + OutputQueuePop(); + delete m; + } + } + + if (output_queue_.empty()) + return true; + + if (INVALID_HANDLE_VALUE == pipe_) + return false; + + // Write to pipe... + Message* m = output_queue_.front(); + + if (partial_write_iter_.isNothing()) { + Pickle::BufferList::IterImpl iter(m->Buffers()); + partial_write_iter_.emplace(iter); + } + + Pickle::BufferList::IterImpl& iter = partial_write_iter_.ref(); + BOOL ok = WriteFile(pipe_, + iter.Data(), + iter.RemainingInSegment(), + &bytes_written, + &output_state_.context.overlapped); + if (!ok) { + DWORD err = GetLastError(); + if (err == ERROR_IO_PENDING) { + output_state_.is_pending = true; + +#ifdef IPC_MESSAGE_DEBUG_EXTRA + DLOG(INFO) << "sent pending message @" << m << " on channel @" << + this << " with type " << m->type(); +#endif + + return true; + } + CHROMIUM_LOG(ERROR) << "pipe error: " << err; + return false; + } + +#ifdef IPC_MESSAGE_DEBUG_EXTRA + DLOG(INFO) << "sent message @" << m << " on channel @" << this << + " with type " << m->type(); +#endif + + output_state_.is_pending = true; + return true; +} + +void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext* context, + DWORD bytes_transfered, DWORD error) { + bool ok; + ASSERT_OWNINGTHREAD(ChannelImpl); + if (context == &input_state_.context) { + if (waiting_connect_) { + if (!ProcessConnection()) + return; + // We may have some messages queued up to send... + if (!output_queue_.empty() && !output_state_.is_pending) + ProcessOutgoingMessages(NULL, 0); + if (input_state_.is_pending) + return; + // else, fall-through and look for incoming messages... + } + // we don't support recursion through OnMessageReceived yet! + DCHECK(!processing_incoming_); + processing_incoming_ = true; + ok = ProcessIncomingMessages(context, bytes_transfered); + processing_incoming_ = false; + } else { + DCHECK(context == &output_state_.context); + ok = ProcessOutgoingMessages(context, bytes_transfered); + } + if (!ok && INVALID_HANDLE_VALUE != pipe_) { + // We don't want to re-enter Close(). + Close(); + listener_->OnChannelError(); + } +} + +bool Channel::ChannelImpl::Unsound_IsClosed() const +{ + return closed_; +} + +uint32_t Channel::ChannelImpl::Unsound_NumQueuedMessages() const +{ + return output_queue_length_; +} + +//------------------------------------------------------------------------------ +// Channel's methods simply call through to ChannelImpl. +Channel::Channel(const std::wstring& channel_id, Mode mode, + Listener* listener) + : channel_impl_(new ChannelImpl(channel_id, mode, listener)) { + MOZ_COUNT_CTOR(IPC::Channel); +} + +Channel::Channel(const std::wstring& channel_id, void* server_pipe, + Mode mode, Listener* listener) + : channel_impl_(new ChannelImpl(channel_id, server_pipe, mode, listener)) { + MOZ_COUNT_CTOR(IPC::Channel); +} + +Channel::~Channel() { + MOZ_COUNT_DTOR(IPC::Channel); + delete channel_impl_; +} + +bool Channel::Connect() { + return channel_impl_->Connect(); +} + +void Channel::Close() { + channel_impl_->Close(); +} + +void* Channel::GetServerPipeHandle() const { + return channel_impl_->GetServerPipeHandle(); +} + +Channel::Listener* Channel::set_listener(Listener* listener) { + return channel_impl_->set_listener(listener); +} + +bool Channel::Send(Message* message) { + return channel_impl_->Send(message); +} + +bool Channel::Unsound_IsClosed() const { + return channel_impl_->Unsound_IsClosed(); +} + +uint32_t Channel::Unsound_NumQueuedMessages() const { + return channel_impl_->Unsound_NumQueuedMessages(); +} + +// static +std::wstring Channel::GenerateVerifiedChannelID(const std::wstring& prefix) { + // Windows pipes can be enumerated by low-privileged processes. So, we + // append a strong random value after the \ character. This value is not + // included in the pipe name, but sent as part of the client hello, to + // prevent hijacking the pipe name to spoof the client. + std::wstring id = prefix; + if (!id.empty()) + id.append(L"."); + int secret; + do { // Guarantee we get a non-zero value. + secret = base::RandInt(0, std::numeric_limits<int>::max()); + } while (secret == 0); + id.append(GenerateUniqueRandomChannelID()); + return id.append(StringPrintf(L"\\%d", secret)); +} + +} // namespace IPC diff --git a/ipc/chromium/src/chrome/common/ipc_channel_win.h b/ipc/chromium/src/chrome/common/ipc_channel_win.h new file mode 100644 index 000000000..1d5cd7aa2 --- /dev/null +++ b/ipc/chromium/src/chrome/common/ipc_channel_win.h @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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. + +#ifndef CHROME_COMMON_IPC_CHANNEL_WIN_H_ +#define CHROME_COMMON_IPC_CHANNEL_WIN_H_ + +#include "chrome/common/ipc_channel.h" + +#include <queue> +#include <string> + +#include "base/message_loop.h" +#include "base/task.h" +#include "nsISupportsImpl.h" + +#include "mozilla/Maybe.h" + +namespace IPC { + +class Channel::ChannelImpl : public MessageLoopForIO::IOHandler { + public: + // Mirror methods of Channel, see ipc_channel.h for description. + ChannelImpl(const std::wstring& channel_id, Mode mode, Listener* listener); + ChannelImpl(const std::wstring& channel_id, HANDLE server_pipe, + Mode mode, Listener* listener); + ~ChannelImpl() { + if (pipe_ != INVALID_HANDLE_VALUE) { + Close(); + } + } + bool Connect(); + void Close(); + HANDLE GetServerPipeHandle() const; + Listener* set_listener(Listener* listener) { + Listener* old = listener_; + listener_ = listener; + return old; + } + bool Send(Message* message); + + // See the comment in ipc_channel.h for info on Unsound_IsClosed() and + // Unsound_NumQueuedMessages(). + bool Unsound_IsClosed() const; + uint32_t Unsound_NumQueuedMessages() const; + + private: + void Init(Mode mode, Listener* listener); + + void OutputQueuePush(Message* msg); + void OutputQueuePop(); + + const std::wstring PipeName(const std::wstring& channel_id, + int32_t* secret) const; + bool CreatePipe(const std::wstring& channel_id, Mode mode); + bool EnqueueHelloMessage(); + + bool ProcessConnection(); + bool ProcessIncomingMessages(MessageLoopForIO::IOContext* context, + DWORD bytes_read); + bool ProcessOutgoingMessages(MessageLoopForIO::IOContext* context, + DWORD bytes_written); + + // MessageLoop::IOHandler implementation. + virtual void OnIOCompleted(MessageLoopForIO::IOContext* context, + DWORD bytes_transfered, DWORD error); + private: + struct State { + explicit State(ChannelImpl* channel); + ~State(); + MessageLoopForIO::IOContext context; + bool is_pending; + }; + + State input_state_; + State output_state_; + + HANDLE pipe_; + + Listener* listener_; + + // Messages to be sent are queued here. + std::queue<Message*> output_queue_; + + // If sending a message blocks then we use this iterator to keep track of + // where in the message we are. It gets reset when the message is finished + // sending. + mozilla::Maybe<Pickle::BufferList::IterImpl> partial_write_iter_; + + // We read from the pipe into this buffer + char input_buf_[Channel::kReadBufferSize]; + size_t input_buf_offset_; + + // Large incoming messages that span multiple pipe buffers get built-up in the + // buffers of this message. + mozilla::Maybe<Message> incoming_message_; + + // In server-mode, we have to wait for the client to connect before we + // can begin reading. We make use of the input_state_ when performing + // the connect operation in overlapped mode. + bool waiting_connect_; + + // This flag is set when processing incoming messages. It is used to + // avoid recursing through ProcessIncomingMessages, which could cause + // problems. TODO(darin): make this unnecessary + bool processing_incoming_; + + // This flag is set after Close() is run on the channel. + bool closed_; + + // This variable is updated so it matches output_queue_.size(), except we can + // read output_queue_length_ from any thread (if we're OK getting an + // occasional out-of-date or bogus value). We use output_queue_length_ to + // implement Unsound_NumQueuedMessages. + size_t output_queue_length_; + + ScopedRunnableMethodFactory<ChannelImpl> factory_; + + // This is a unique per-channel value used to authenticate the client end of + // a connection. If the value is non-zero, the client passes it in the hello + // and the host validates. (We don't send the zero value to preserve IPC + // compatibility with existing clients that don't validate the channel.) + int32_t shared_secret_; + + // In server-mode, we wait for the channel at the other side of the pipe to + // send us back our shared secret, if we are using one. + bool waiting_for_shared_secret_; + +#ifdef DEBUG + mozilla::UniquePtr<nsAutoOwningThread> _mOwningThread; +#endif + + DISALLOW_COPY_AND_ASSIGN(ChannelImpl); +}; + +} // namespace IPC + +#endif // CHROME_COMMON_IPC_CHANNEL_WIN_H_ diff --git a/ipc/chromium/src/chrome/common/ipc_message.cc b/ipc/chromium/src/chrome/common/ipc_message.cc new file mode 100644 index 000000000..b8b3ee353 --- /dev/null +++ b/ipc/chromium/src/chrome/common/ipc_message.cc @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "chrome/common/ipc_message.h" + +#include "base/logging.h" +#include "build/build_config.h" + +#if defined(OS_POSIX) +#include "chrome/common/file_descriptor_set_posix.h" +#endif +#ifdef MOZ_TASK_TRACER +#include "GeckoTaskTracer.h" +#endif + +#include "mozilla/Move.h" + +#ifdef MOZ_TASK_TRACER +using namespace mozilla::tasktracer; +#endif + +namespace IPC { + +//------------------------------------------------------------------------------ + +Message::~Message() { + MOZ_COUNT_DTOR(IPC::Message); +} + +Message::Message() + : Pickle(sizeof(Header)) { + MOZ_COUNT_CTOR(IPC::Message); + header()->routing = header()->type = header()->flags = 0; +#if defined(OS_POSIX) + header()->num_fds = 0; +#endif +#ifdef MOZ_TASK_TRACER + header()->source_event_id = 0; + header()->parent_task_id = 0; + header()->source_event_type = SourceEventType::Unknown; +#endif + InitLoggingVariables(); +} + +Message::Message(int32_t routing_id, msgid_t type, NestedLevel nestedLevel, PriorityValue priority, + MessageCompression compression, const char* const aName) + : Pickle(sizeof(Header)) { + MOZ_COUNT_CTOR(IPC::Message); + header()->routing = routing_id; + header()->type = type; + header()->flags = nestedLevel; + if (priority == HIGH_PRIORITY) + header()->flags |= PRIO_BIT; + if (compression == COMPRESSION_ENABLED) + header()->flags |= COMPRESS_BIT; + else if (compression == COMPRESSION_ALL) + header()->flags |= COMPRESSALL_BIT; +#if defined(OS_POSIX) + header()->num_fds = 0; +#endif + header()->interrupt_remote_stack_depth_guess = static_cast<uint32_t>(-1); + header()->interrupt_local_stack_depth = static_cast<uint32_t>(-1); + header()->seqno = 0; +#if defined(OS_MACOSX) + header()->cookie = 0; +#endif +#ifdef MOZ_TASK_TRACER + header()->source_event_id = 0; + header()->parent_task_id = 0; + header()->source_event_type = SourceEventType::Unknown; +#endif + InitLoggingVariables(aName); +} + +Message::Message(const char* data, int data_len) + : Pickle(sizeof(Header), data, data_len) +{ + MOZ_COUNT_CTOR(IPC::Message); + InitLoggingVariables(); +} + +Message::Message(Message&& other) : Pickle(mozilla::Move(other)) { + MOZ_COUNT_CTOR(IPC::Message); + InitLoggingVariables(other.name_); +#if defined(OS_POSIX) + file_descriptor_set_ = other.file_descriptor_set_.forget(); +#endif +#ifdef MOZ_TASK_TRACER + header()->source_event_id = other.header()->source_event_id; + header()->parent_task_id = other.header()->parent_task_id; + header()->source_event_type = other.header()->source_event_type; +#endif +} + +void Message::InitLoggingVariables(const char* const aName) { + name_ = aName; +} + +Message& Message::operator=(Message&& other) { + *static_cast<Pickle*>(this) = mozilla::Move(other); + InitLoggingVariables(other.name_); +#if defined(OS_POSIX) + file_descriptor_set_.swap(other.file_descriptor_set_); +#endif +#ifdef MOZ_TASK_TRACER + std::swap(header()->source_event_id, other.header()->source_event_id); + std::swap(header()->parent_task_id, other.header()->parent_task_id); + std::swap(header()->source_event_type, other.header()->source_event_type); +#endif + return *this; +} + + +#if defined(OS_POSIX) +bool Message::WriteFileDescriptor(const base::FileDescriptor& descriptor) { + // We write the index of the descriptor so that we don't have to + // keep the current descriptor as extra decoding state when deserialising. + // Also, we rely on each file descriptor being accompanied by sizeof(int) + // bytes of data in the message. See the comment for input_cmsg_buf_. + WriteInt(file_descriptor_set()->size()); + if (descriptor.auto_close) { + return file_descriptor_set()->AddAndAutoClose(descriptor.fd); + } else { + return file_descriptor_set()->Add(descriptor.fd); + } +} + +bool Message::ReadFileDescriptor(PickleIterator* iter, + base::FileDescriptor* descriptor) const { + int descriptor_index; + if (!ReadInt(iter, &descriptor_index)) + return false; + + FileDescriptorSet* file_descriptor_set = file_descriptor_set_.get(); + if (!file_descriptor_set) + return false; + + descriptor->fd = file_descriptor_set->GetDescriptorAt(descriptor_index); + descriptor->auto_close = false; + + return descriptor->fd >= 0; +} + +void Message::EnsureFileDescriptorSet() { + if (file_descriptor_set_.get() == NULL) + file_descriptor_set_ = new FileDescriptorSet; +} + +uint32_t Message::num_fds() const { + return file_descriptor_set() ? file_descriptor_set()->size() : 0; +} + +#endif + +} // namespace IPC diff --git a/ipc/chromium/src/chrome/common/ipc_message.h b/ipc/chromium/src/chrome/common/ipc_message.h new file mode 100644 index 000000000..60cec5f13 --- /dev/null +++ b/ipc/chromium/src/chrome/common/ipc_message.h @@ -0,0 +1,370 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + +#ifndef CHROME_COMMON_IPC_MESSAGE_H__ +#define CHROME_COMMON_IPC_MESSAGE_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/pickle.h" + +#ifdef MOZ_TASK_TRACER +#include "GeckoTaskTracer.h" +#endif + +#if defined(OS_POSIX) +#include "nsAutoPtr.h" +#endif + +namespace base { +struct FileDescriptor; +} + +class FileDescriptorSet; + +namespace IPC { + +//------------------------------------------------------------------------------ + +class Channel; +class Message; +struct LogData; + +class Message : public Pickle { + public: + typedef uint32_t msgid_t; + + enum NestedLevel { + NOT_NESTED = 1, + NESTED_INSIDE_SYNC = 2, + NESTED_INSIDE_CPOW = 3 + }; + + enum PriorityValue { + NORMAL_PRIORITY, + HIGH_PRIORITY, + }; + + enum MessageCompression { + COMPRESSION_NONE, + COMPRESSION_ENABLED, + COMPRESSION_ALL + }; + + virtual ~Message(); + + Message(); + + // Initialize a message with a user-defined type, priority value, and + // destination WebView ID. + Message(int32_t routing_id, + msgid_t type, + NestedLevel nestedLevel = NOT_NESTED, + PriorityValue priority = NORMAL_PRIORITY, + MessageCompression compression = COMPRESSION_NONE, + const char* const name="???"); + + Message(const char* data, int data_len); + + Message(const Message& other) = delete; + Message(Message&& other); + Message& operator=(const Message& other) = delete; + Message& operator=(Message&& other); + + NestedLevel nested_level() const { + return static_cast<NestedLevel>(header()->flags & NESTED_MASK); + } + + void set_nested_level(NestedLevel nestedLevel) { + DCHECK((nestedLevel & ~NESTED_MASK) == 0); + header()->flags = (header()->flags & ~NESTED_MASK) | nestedLevel; + } + + PriorityValue priority() const { + if (header()->flags & PRIO_BIT) { + return HIGH_PRIORITY; + } + return NORMAL_PRIORITY; + } + + void set_priority(PriorityValue prio) { + header()->flags &= ~PRIO_BIT; + if (prio == HIGH_PRIORITY) { + header()->flags |= PRIO_BIT; + } + } + + // True if this is a synchronous message. + bool is_sync() const { + return (header()->flags & SYNC_BIT) != 0; + } + + // True if this is a synchronous message. + bool is_interrupt() const { + return (header()->flags & INTERRUPT_BIT) != 0; + } + + // True if compression is enabled for this message. + MessageCompression compress_type() const { + return (header()->flags & COMPRESS_BIT) ? + COMPRESSION_ENABLED : + (header()->flags & COMPRESSALL_BIT) ? + COMPRESSION_ALL : + COMPRESSION_NONE; + } + + // Set this on a reply to a synchronous message. + void set_reply() { + header()->flags |= REPLY_BIT; + } + + bool is_reply() const { + return (header()->flags & REPLY_BIT) != 0; + } + + // Set this on a reply to a synchronous message to indicate that no receiver + // was found. + void set_reply_error() { + header()->flags |= REPLY_ERROR_BIT; + } + + bool is_reply_error() const { + return (header()->flags & REPLY_ERROR_BIT) != 0; + } + + msgid_t type() const { + return header()->type; + } + + int32_t routing_id() const { + return header()->routing; + } + + void set_routing_id(int32_t new_id) { + header()->routing = new_id; + } + + int32_t transaction_id() const { + return header()->txid; + } + + void set_transaction_id(int32_t txid) { + header()->txid = txid; + } + + uint32_t interrupt_remote_stack_depth_guess() const { + return header()->interrupt_remote_stack_depth_guess; + } + + void set_interrupt_remote_stack_depth_guess(uint32_t depth) { + DCHECK(is_interrupt()); + header()->interrupt_remote_stack_depth_guess = depth; + } + + uint32_t interrupt_local_stack_depth() const { + return header()->interrupt_local_stack_depth; + } + + void set_interrupt_local_stack_depth(uint32_t depth) { + DCHECK(is_interrupt()); + header()->interrupt_local_stack_depth = depth; + } + + int32_t seqno() const { + return header()->seqno; + } + + void set_seqno(int32_t aSeqno) { + header()->seqno = aSeqno; + } + + const char* name() const { + return name_; + } + + void set_name(const char* const aName) { + name_ = aName; + } + +#if defined(OS_POSIX) + uint32_t num_fds() const; +#endif + + template<class T> + static bool Dispatch(const Message* msg, T* obj, void (T::*func)()) { + (obj->*func)(); + return true; + } + + template<class T> + static bool Dispatch(const Message* msg, T* obj, void (T::*func)() const) { + (obj->*func)(); + return true; + } + + template<class T> + static bool Dispatch(const Message* msg, T* obj, + void (T::*func)(const Message&)) { + (obj->*func)(*msg); + return true; + } + + template<class T> + static bool Dispatch(const Message* msg, T* obj, + void (T::*func)(const Message&) const) { + (obj->*func)(*msg); + return true; + } + + // Used for async messages with no parameters. + static void Log(const Message* msg, std::wstring* l) { + } + + // Figure out how big the message starting at range_start is. Returns 0 if + // there's no enough data to determine (i.e., if [range_start, range_end) does + // not contain enough of the message header to know the size). + static uint32_t MessageSize(const char* range_start, const char* range_end) { + return Pickle::MessageSize(sizeof(Header), range_start, range_end); + } + +#if defined(OS_POSIX) + // On POSIX, a message supports reading / writing FileDescriptor objects. + // This is used to pass a file descriptor to the peer of an IPC channel. + + // Add a descriptor to the end of the set. Returns false iff the set is full. + bool WriteFileDescriptor(const base::FileDescriptor& descriptor); + // Get a file descriptor from the message. Returns false on error. + // iter: a Pickle iterator to the current location in the message. + bool ReadFileDescriptor(PickleIterator* iter, base::FileDescriptor* descriptor) const; + +#if defined(OS_MACOSX) + void set_fd_cookie(uint32_t cookie) { + header()->cookie = cookie; + } + uint32_t fd_cookie() const { + return header()->cookie; + } +#endif +#endif + + + friend class Channel; + friend class MessageReplyDeserializer; + friend class SyncMessage; + + void set_sync() { + header()->flags |= SYNC_BIT; + } + + void set_interrupt() { + header()->flags |= INTERRUPT_BIT; + } + +#if !defined(OS_MACOSX) + protected: +#endif + + // flags + enum { + NESTED_MASK = 0x0003, + PRIO_BIT = 0x0004, + SYNC_BIT = 0x0008, + REPLY_BIT = 0x0010, + REPLY_ERROR_BIT = 0x0020, + INTERRUPT_BIT = 0x0040, + COMPRESS_BIT = 0x0080, + COMPRESSALL_BIT = 0x0100, + }; + + struct Header : Pickle::Header { + int32_t routing; // ID of the view that this message is destined for + msgid_t type; // specifies the user-defined message type + uint32_t flags; // specifies control flags for the message +#if defined(OS_POSIX) + uint32_t num_fds; // the number of descriptors included with this message +# if defined(OS_MACOSX) + uint32_t cookie; // cookie to ACK that the descriptors have been read. +# endif +#endif + union { + // For Interrupt messages, a guess at what the *other* side's stack depth is. + uint32_t interrupt_remote_stack_depth_guess; + + // For RPC and Urgent messages, a transaction ID for message ordering. + int32_t txid; + }; + // The actual local stack depth. + uint32_t interrupt_local_stack_depth; + // Sequence number + int32_t seqno; +#ifdef MOZ_TASK_TRACER + uint64_t source_event_id; + uint64_t parent_task_id; + mozilla::tasktracer::SourceEventType source_event_type; +#endif + }; + + Header* header() { + return headerT<Header>(); + } + const Header* header() const { + return headerT<Header>(); + } + + void InitLoggingVariables(const char* const name="???"); + +#if defined(OS_POSIX) + // The set of file descriptors associated with this message. + RefPtr<FileDescriptorSet> file_descriptor_set_; + + // Ensure that a FileDescriptorSet is allocated + void EnsureFileDescriptorSet(); + + FileDescriptorSet* file_descriptor_set() { + EnsureFileDescriptorSet(); + return file_descriptor_set_.get(); + } + const FileDescriptorSet* file_descriptor_set() const { + return file_descriptor_set_.get(); + } +#endif + + const char* name_; + +}; + +class MessageInfo { +public: + typedef uint32_t msgid_t; + + explicit MessageInfo(const Message& aMsg) + : mSeqno(aMsg.seqno()), mType(aMsg.type()) {} + + int32_t seqno() const { return mSeqno; } + msgid_t type() const { return mType; } + +private: + int32_t mSeqno; + msgid_t mType; +}; + +//------------------------------------------------------------------------------ + +} // namespace IPC + +enum SpecialRoutingIDs { + // indicates that we don't have a routing ID yet. + MSG_ROUTING_NONE = kint32min, + + // indicates a general message not sent to a particular tab. + MSG_ROUTING_CONTROL = kint32max +}; + +#define IPC_REPLY_ID 0xFFF0 // Special message id for replies +#define IPC_LOGGING_ID 0xFFF1 // Special message id for logging + +#endif // CHROME_COMMON_IPC_MESSAGE_H__ diff --git a/ipc/chromium/src/chrome/common/ipc_message_utils.h b/ipc/chromium/src/chrome/common/ipc_message_utils.h new file mode 100644 index 000000000..e8ea9ad3a --- /dev/null +++ b/ipc/chromium/src/chrome/common/ipc_message_utils.h @@ -0,0 +1,526 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + +#ifndef CHROME_COMMON_IPC_MESSAGE_UTILS_H_ +#define CHROME_COMMON_IPC_MESSAGE_UTILS_H_ + +#include <string> +#include <vector> +#include <map> + +#include "base/file_path.h" +#include "base/string_util.h" +#include "base/string16.h" +#include "base/time.h" + +#if defined(OS_POSIX) +#include "chrome/common/file_descriptor_set_posix.h" +#endif +#include "chrome/common/ipc_message.h" +#include "chrome/common/transport_dib.h" + +namespace IPC { + +//----------------------------------------------------------------------------- +// An iterator class for reading the fields contained within a Message. + +class MessageIterator { + public: + explicit MessageIterator(const Message& m) : msg_(m), iter_(m) { + } + int NextInt() const { + int val; + if (!msg_.ReadInt(&iter_, &val)) + NOTREACHED(); + return val; + } + intptr_t NextIntPtr() const { + intptr_t val; + if (!msg_.ReadIntPtr(&iter_, &val)) + NOTREACHED(); + return val; + } + const std::string NextString() const { + std::string val; + if (!msg_.ReadString(&iter_, &val)) + NOTREACHED(); + return val; + } + const std::wstring NextWString() const { + std::wstring val; + if (!msg_.ReadWString(&iter_, &val)) + NOTREACHED(); + return val; + } + private: + const Message& msg_; + mutable PickleIterator iter_; +}; + +//----------------------------------------------------------------------------- +// ParamTraits specializations, etc. +// +// The full set of types ParamTraits is specialized upon contains *possibly* +// repeated types: unsigned long may be uint32_t or size_t, unsigned long long +// may be uint64_t or size_t, nsresult may be uint32_t, and so on. You can't +// have ParamTraits<unsigned int> *and* ParamTraits<uint32_t> if unsigned int +// is uint32_t -- that's multiple definitions, and you can only have one. +// +// You could use #ifs and macro conditions to avoid duplicates, but they'd be +// hairy: heavily dependent upon OS and compiler author choices, forced to +// address all conflicts by hand. Happily there's a better way. The basic +// idea looks like this, where T -> U represents T inheriting from U: +// +// class ParamTraits<P> +// | +// --> class ParamTraits1<P> +// | +// --> class ParamTraits2<P> +// | +// --> class ParamTraitsN<P> // or however many levels +// +// The default specialization of ParamTraits{M}<P> is an empty class that +// inherits from ParamTraits{M + 1}<P> (or nothing in the base case). +// +// Now partition the set of parameter types into sets without duplicates. +// Assign each set of types to a level M. Then specialize ParamTraitsM for +// each of those types. A reference to ParamTraits<P> will consist of some +// number of empty classes inheriting in sequence, ending in a non-empty +// ParamTraits{N}<P>. It's okay for the parameter types to be duplicative: +// either name of a type will resolve to the same ParamTraits{N}<P>. +// +// The nice thing is that because templates are instantiated lazily, if we +// indeed have uint32_t == unsigned int, say, with the former in level N and +// the latter in M > N, ParamTraitsM<unsigned int> won't be created (as long as +// nobody uses ParamTraitsM<unsigned int>, but why would you), and no duplicate +// code will be compiled or extra symbols generated. It's as efficient at +// runtime as manually figuring out and avoiding conflicts by #ifs. +// +// The scheme we follow below names the various classes according to the types +// in them, and the number of ParamTraits levels is larger, but otherwise it's +// exactly the above idea. +// + +template <class P> struct ParamTraits; + +template <class P> +static inline void WriteParam(Message* m, const P& p) { + ParamTraits<P>::Write(m, p); +} + +template <class P> +static inline bool WARN_UNUSED_RESULT ReadParam(const Message* m, PickleIterator* iter, + P* p) { + return ParamTraits<P>::Read(m, iter, p); +} + +template <class P> +static inline void LogParam(const P& p, std::wstring* l) { + ParamTraits<P>::Log(p, l); +} + +// Fundamental types. + +template <class P> +struct ParamTraitsFundamental {}; + +template <> +struct ParamTraitsFundamental<bool> { + typedef bool param_type; + static void Write(Message* m, const param_type& p) { + m->WriteBool(p); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadBool(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(p ? L"true" : L"false"); + } +}; + +template <> +struct ParamTraitsFundamental<int> { + typedef int param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadInt(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%d", p)); + } +}; + +template <> +struct ParamTraitsFundamental<long> { + typedef long param_type; + static void Write(Message* m, const param_type& p) { + m->WriteLong(p); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadLong(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%l", p)); + } +}; + +template <> +struct ParamTraitsFundamental<unsigned long> { + typedef unsigned long param_type; + static void Write(Message* m, const param_type& p) { + m->WriteULong(p); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadULong(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%ul", p)); + } +}; + +template <> +struct ParamTraitsFundamental<long long> { + typedef long long param_type; + static void Write(Message* m, const param_type& p) { + m->WriteBytes(&p, sizeof(param_type)); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadBytesInto(iter, r, sizeof(*r)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%ll", p)); + } +}; + +template <> +struct ParamTraitsFundamental<unsigned long long> { + typedef unsigned long long param_type; + static void Write(Message* m, const param_type& p) { + m->WriteBytes(&p, sizeof(param_type)); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadBytesInto(iter, r, sizeof(*r)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%ull", p)); + } +}; + +template <> +struct ParamTraitsFundamental<double> { + typedef double param_type; + static void Write(Message* m, const param_type& p) { + m->WriteDouble(p); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadDouble(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"e", p)); + } +}; + +// Fixed-size <stdint.h> types. + +template <class P> +struct ParamTraitsFixed : ParamTraitsFundamental<P> {}; + +template <> +struct ParamTraitsFixed<int16_t> { + typedef int16_t param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt16(p); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadInt16(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%hd", p)); + } +}; + +template <> +struct ParamTraitsFixed<uint16_t> { + typedef uint16_t param_type; + static void Write(Message* m, const param_type& p) { + m->WriteUInt16(p); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadUInt16(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%hu", p)); + } +}; + +template <> +struct ParamTraitsFixed<uint32_t> { + typedef uint32_t param_type; + static void Write(Message* m, const param_type& p) { + m->WriteUInt32(p); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadUInt32(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%u", p)); + } +}; + +template <> +struct ParamTraitsFixed<int64_t> { + typedef int64_t param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt64(p); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadInt64(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%" PRId64L, p)); + } +}; + +template <> +struct ParamTraitsFixed<uint64_t> { + typedef uint64_t param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt64(static_cast<int64_t>(p)); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadInt64(iter, reinterpret_cast<int64_t*>(r)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%" PRIu64L, p)); + } +}; + +// Other standard C types. + +template <class P> +struct ParamTraitsLibC : ParamTraitsFixed<P> {}; + +template <> +struct ParamTraitsLibC<size_t> { + typedef size_t param_type; + static void Write(Message* m, const param_type& p) { + m->WriteSize(p); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadSize(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%u", p)); + } +}; + +// std::* types. + +template <class P> +struct ParamTraitsStd : ParamTraitsLibC<P> {}; + +template <> +struct ParamTraitsStd<std::string> { + typedef std::string param_type; + static void Write(Message* m, const param_type& p) { + m->WriteString(p); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadString(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(UTF8ToWide(p)); + } +}; + +template <> +struct ParamTraitsStd<std::wstring> { + typedef std::wstring param_type; + static void Write(Message* m, const param_type& p) { + m->WriteWString(p); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadWString(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(p); + } +}; + +template <class K, class V> +struct ParamTraitsStd<std::map<K, V> > { + typedef std::map<K, V> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast<int>(p.size())); + typename param_type::const_iterator iter; + for (iter = p.begin(); iter != p.end(); ++iter) { + WriteParam(m, iter->first); + WriteParam(m, iter->second); + } + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + int size; + if (!ReadParam(m, iter, &size) || size < 0) + return false; + for (int i = 0; i < size; ++i) { + K k; + if (!ReadParam(m, iter, &k)) + return false; + V& value = (*r)[k]; + if (!ReadParam(m, iter, &value)) + return false; + } + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"<std::map>"); + } +}; + +// Windows-specific types. + +template <class P> +struct ParamTraitsWindows : ParamTraitsStd<P> {}; + +#if defined(OS_WIN) +template <> +struct ParamTraitsWindows<HANDLE> { + typedef HANDLE param_type; + static void Write(Message* m, const param_type& p) { + m->WriteIntPtr(reinterpret_cast<intptr_t>(p)); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(intptr_t)); + return m->ReadIntPtr(iter, reinterpret_cast<intptr_t*>(r)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"0x%X", p)); + } +}; + +template <> +struct ParamTraitsWindows<HWND> { + typedef HWND param_type; + static void Write(Message* m, const param_type& p) { + m->WriteIntPtr(reinterpret_cast<intptr_t>(p)); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(intptr_t)); + return m->ReadIntPtr(iter, reinterpret_cast<intptr_t*>(r)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"0x%X", p)); + } +}; +#endif // defined(OS_WIN) + +// Various ipc/chromium types. + +template <class P> +struct ParamTraitsIPC : ParamTraitsWindows<P> {}; + +#if defined(OS_POSIX) +// FileDescriptors may be serialised over IPC channels on POSIX. On the +// receiving side, the FileDescriptor is a valid duplicate of the file +// descriptor which was transmitted: *it is not just a copy of the integer like +// HANDLEs on Windows*. The only exception is if the file descriptor is < 0. In +// this case, the receiving end will see a value of -1. *Zero is a valid file +// descriptor*. +// +// The received file descriptor will have the |auto_close| flag set to true. The +// code which handles the message is responsible for taking ownership of it. +// File descriptors are OS resources and must be closed when no longer needed. +// +// When sending a file descriptor, the file descriptor must be valid at the time +// of transmission. Since transmission is not synchronous, one should consider +// dup()ing any file descriptors to be transmitted and setting the |auto_close| +// flag, which causes the file descriptor to be closed after writing. +template<> +struct ParamTraitsIPC<base::FileDescriptor> { + typedef base::FileDescriptor param_type; + static void Write(Message* m, const param_type& p) { + const bool valid = p.fd >= 0; + WriteParam(m, valid); + + if (valid) { + if (!m->WriteFileDescriptor(p)) { + NOTREACHED() << "Too many file descriptors for one message!"; + } + } + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + bool valid; + if (!ReadParam(m, iter, &valid)) + return false; + + if (!valid) { + r->fd = -1; + r->auto_close = false; + return true; + } + + return m->ReadFileDescriptor(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + if (p.auto_close) { + l->append(StringPrintf(L"FD(%d auto-close)", p.fd)); + } else { + l->append(StringPrintf(L"FD(%d)", p.fd)); + } + } +}; +#endif // defined(OS_POSIX) + +#if defined(OS_WIN) +template<> +struct ParamTraitsIPC<TransportDIB::Id> { + typedef TransportDIB::Id param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.handle); + WriteParam(m, p.sequence_num); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return (ReadParam(m, iter, &r->handle) && + ReadParam(m, iter, &r->sequence_num)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"TransportDIB("); + LogParam(p.handle, l); + l->append(L", "); + LogParam(p.sequence_num, l); + l->append(L")"); + } +}; +#endif + +// Mozilla-specific types. + +template <class P> +struct ParamTraitsMozilla : ParamTraitsIPC<P> {}; + +template <> +struct ParamTraitsMozilla<nsresult> { + typedef nsresult param_type; + static void Write(Message* m, const param_type& p) { + m->WriteUInt32(static_cast<uint32_t>(p)); + } + static bool Read(const Message* m, PickleIterator* iter, param_type* r) { + return m->ReadUInt32(iter, reinterpret_cast<uint32_t*>(r)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%u", static_cast<uint32_t>(p))); + } +}; + +// Finally, ParamTraits itself. + +template <class P> struct ParamTraits : ParamTraitsMozilla<P> {}; + +} // namespace IPC + +#endif // CHROME_COMMON_IPC_MESSAGE_UTILS_H_ diff --git a/ipc/chromium/src/chrome/common/mach_ipc_mac.h b/ipc/chromium/src/chrome/common/mach_ipc_mac.h new file mode 100644 index 000000000..b44327765 --- /dev/null +++ b/ipc/chromium/src/chrome/common/mach_ipc_mac.h @@ -0,0 +1,313 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + +#ifndef BASE_MACH_IPC_MAC_H_ +#define BASE_MACH_IPC_MAC_H_ + +#include <mach/mach.h> +#include <mach/message.h> +#include <servers/bootstrap.h> +#include <sys/types.h> + +#include "base/basictypes.h" + +//============================================================================== +// DISCUSSION: +// +// The three main classes of interest are +// +// MachMessage: a wrapper for a mach message of the following form +// mach_msg_header_t +// mach_msg_body_t +// optional descriptors +// optional extra message data +// +// MachReceiveMessage and MachSendMessage subclass MachMessage +// and are used instead of MachMessage which is an abstract base class +// +// ReceivePort: +// Represents a mach port for which we have receive rights +// +// MachPortSender: +// Represents a mach port for which we have send rights +// +// Here's an example to receive a message on a server port: +// +// // This creates our named server port +// ReceivePort receivePort("com.Google.MyService"); +// +// MachReceiveMessage message; +// kern_return_t result = receivePort.WaitForMessage(&message, 0); +// +// if (result == KERN_SUCCESS && message.GetMessageID() == 57) { +// mach_port_t task = message.GetTranslatedPort(0); +// mach_port_t thread = message.GetTranslatedPort(1); +// +// char *messageString = message.GetData(); +// +// printf("message string = %s\n", messageString); +// } +// +// Here is an example of using these classes to send a message to this port: +// +// // send to already named port +// MachPortSender sender("com.Google.MyService"); +// MachSendMessage message(57); // our message ID is 57 +// +// // add some ports to be translated for us +// message.AddDescriptor(mach_task_self()); // our task +// message.AddDescriptor(mach_thread_self()); // this thread +// +// char messageString[] = "Hello server!\n"; +// message.SetData(messageString, strlen(messageString)+1); +// // timeout 1000ms +// kern_return_t result = sender.SendMessage(message, 1000); +// + +#define PRINT_MACH_RESULT(result_, message_) \ + printf(message_" %s (%d)\n", mach_error_string(result_), result_ ); + +//============================================================================== +// A wrapper class for mach_msg_port_descriptor_t (with same memory layout) +// with convenient constructors and accessors +class MachMsgPortDescriptor : public mach_msg_port_descriptor_t { + public: + // General-purpose constructor + MachMsgPortDescriptor(mach_port_t in_name, + mach_msg_type_name_t in_disposition) { + name = in_name; + pad1 = 0; + pad2 = 0; + disposition = in_disposition; + type = MACH_MSG_PORT_DESCRIPTOR; + } + + // For passing send rights to a port + explicit MachMsgPortDescriptor(mach_port_t in_name) { + name = in_name; + pad1 = 0; + pad2 = 0; + disposition = MACH_MSG_TYPE_PORT_SEND; + type = MACH_MSG_PORT_DESCRIPTOR; + } + + // Copy constructor + MachMsgPortDescriptor(const MachMsgPortDescriptor& desc) { + name = desc.name; + pad1 = desc.pad1; + pad2 = desc.pad2; + disposition = desc.disposition; + type = desc.type; + } + + mach_port_t GetMachPort() const { + return name; + } + + mach_msg_type_name_t GetDisposition() const { + return disposition; + } + + // For convenience + operator mach_port_t() const { + return GetMachPort(); + } +}; + +//============================================================================== +// MachMessage: a wrapper for a mach message +// (mach_msg_header_t, mach_msg_body_t, extra data) +// +// This considerably simplifies the construction of a message for sending +// and the getting at relevant data and descriptors for the receiver. +// +// This class can be initialized using external storage of an arbitrary size +// or it can manage storage internally. +// 1. If storage is allocated internally, the combined size of the descriptors +// plus data must be less than 1024. But as a benefit no memory allocation is +// necessary. +// 2. For external storage, a buffer of at least EmptyMessageSize() must be +// provided. +// +// A MachMessage object is used by ReceivePort::WaitForMessage +// and MachPortSender::SendMessage +// +class MachMessage { + public: + + virtual ~MachMessage(); + + // The receiver of the message can retrieve the raw data this way + u_int8_t *GetData() { + return GetDataLength() > 0 ? GetDataPacket()->data : NULL; + } + + u_int32_t GetDataLength(); + + // The message ID may be used as a code identifying the type of message + void SetMessageID(int32_t message_id); + + int32_t GetMessageID(); + + // Adds a descriptor (typically a mach port) to be translated + // returns true if successful, otherwise not enough space + bool AddDescriptor(const MachMsgPortDescriptor &desc); + + int GetDescriptorCount() const { + return storage_->body.msgh_descriptor_count; + } + MachMsgPortDescriptor *GetDescriptor(int n); + + // Convenience method which gets the mach port described by the descriptor + mach_port_t GetTranslatedPort(int n); + + // A simple message is one with no descriptors + bool IsSimpleMessage() const { return GetDescriptorCount() == 0; } + + // Sets raw data for the message (returns false if not enough space) + bool SetData(const void* data, int32_t data_length); + + protected: + // Consider this an abstract base class - must create an actual instance + // of MachReceiveMessage or MachSendMessage + MachMessage(); + + // Constructor for use with preallocate storage. + // storage_length must be >= EmptyMessageSize() + MachMessage(void *storage, size_t storage_length); + + friend class ReceivePort; + friend class MachPortSender; + + // Represents raw data in our message + struct MessageDataPacket { + int32_t id; // little-endian + int32_t data_length; // little-endian + u_int8_t data[1]; // actual size limited by storage_length_bytes_ + }; + + MessageDataPacket* GetDataPacket(); + + void SetDescriptorCount(int n); + void SetDescriptor(int n, const MachMsgPortDescriptor &desc); + + // Returns total message size setting msgh_size in the header to this value + int CalculateSize(); + + // Returns total storage size that this object can grow to, this is inclusive + // of the mach header. + size_t MaxSize() const { return storage_length_bytes_; } + + protected: + mach_msg_header_t *Head() { return &(storage_->head); } + + private: + struct MachMessageData { + mach_msg_header_t head; + mach_msg_body_t body; + // descriptors and data may be embedded here. + u_int8_t padding[1024]; + }; + + // kEmptyMessageSize needs to have the definition of MachMessageData before + // it. + public: + // The size of an empty message with no data. + static const size_t kEmptyMessageSize = sizeof(mach_msg_header_t) + + sizeof(mach_msg_body_t) + + sizeof(MessageDataPacket); + + private: + MachMessageData *storage_; + size_t storage_length_bytes_; + bool own_storage_; // Is storage owned by this object? +}; + +//============================================================================== +// MachReceiveMessage and MachSendMessage are useful to separate the idea +// of a mach message being sent and being received, and adds increased type +// safety: +// ReceivePort::WaitForMessage() only accepts a MachReceiveMessage +// MachPortSender::SendMessage() only accepts a MachSendMessage + +//============================================================================== +class MachReceiveMessage : public MachMessage { + public: + MachReceiveMessage() : MachMessage() {} + MachReceiveMessage(void *storage, size_t storage_length) + : MachMessage(storage, storage_length) {} + + private: + DISALLOW_COPY_AND_ASSIGN(MachReceiveMessage); +}; + +//============================================================================== +class MachSendMessage : public MachMessage { + public: + explicit MachSendMessage(int32_t message_id); + MachSendMessage(void *storage, size_t storage_length, int32_t message_id); + + private: + void Initialize(int32_t message_id); + + DISALLOW_COPY_AND_ASSIGN(MachSendMessage); +}; + +//============================================================================== +// Represents a mach port for which we have receive rights +class ReceivePort { + public: + // Creates a new mach port for receiving messages and registers a name for it + explicit ReceivePort(const char *receive_port_name); + + // Given an already existing mach port, use it. We take ownership of the + // port and deallocate it in our destructor. + explicit ReceivePort(mach_port_t receive_port); + + // Create a new mach port for receiving messages + ReceivePort(); + + ~ReceivePort(); + + // Waits on the mach port until message received or timeout + kern_return_t WaitForMessage(MachReceiveMessage *out_message, + mach_msg_timeout_t timeout); + + kern_return_t SendMessageToSelf(MachSendMessage& msg, + mach_msg_timeout_t timeout); + + // The underlying mach port that we wrap + mach_port_t GetPort() const { return port_; } + + private: + mach_port_t port_; + kern_return_t init_result_; + + DISALLOW_COPY_AND_ASSIGN(ReceivePort); +}; + +//============================================================================== +// Represents a mach port for which we have send rights +class MachPortSender { + public: + // get a port with send rights corresponding to a named registered service + explicit MachPortSender(const char *receive_port_name); + + + // Given an already existing mach port, use it. + explicit MachPortSender(mach_port_t send_port); + + kern_return_t SendMessage(MachSendMessage &message, + mach_msg_timeout_t timeout); + + private: + mach_port_t send_port_; + kern_return_t init_result_; + + DISALLOW_COPY_AND_ASSIGN(MachPortSender); +}; + +#endif // BASE_MACH_IPC_MAC_H_ diff --git a/ipc/chromium/src/chrome/common/mach_ipc_mac.mm b/ipc/chromium/src/chrome/common/mach_ipc_mac.mm new file mode 100644 index 000000000..aa1813094 --- /dev/null +++ b/ipc/chromium/src/chrome/common/mach_ipc_mac.mm @@ -0,0 +1,351 @@ +// Copyright (c) 2006-2008 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 "chrome/common/mach_ipc_mac.h" + +#import <Foundation/Foundation.h> + +#include <stdio.h> +#include "base/logging.h" + +//============================================================================== +MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() { + Initialize(message_id); +} + +MachSendMessage::MachSendMessage(void *storage, size_t storage_length, + int32_t message_id) + : MachMessage(storage, storage_length) { + Initialize(message_id); +} + +void MachSendMessage::Initialize(int32_t message_id) { + Head()->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); + + // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage() + Head()->msgh_local_port = MACH_PORT_NULL; + Head()->msgh_reserved = 0; + Head()->msgh_id = 0; + + SetDescriptorCount(0); // start out with no descriptors + + SetMessageID(message_id); + SetData(NULL, 0); // client may add data later +} + +//============================================================================== +MachMessage::MachMessage() + : storage_(new MachMessageData), // Allocate storage_ ourselves + storage_length_bytes_(sizeof(MachMessageData)), + own_storage_(true) { + memset(storage_, 0, storage_length_bytes_); +} + +//============================================================================== +MachMessage::MachMessage(void *storage, size_t storage_length) + : storage_(static_cast<MachMessageData*>(storage)), + storage_length_bytes_(storage_length), + own_storage_(false) { + DCHECK(storage); + DCHECK(storage_length >= kEmptyMessageSize); +} + +//============================================================================== +MachMessage::~MachMessage() { + if (own_storage_) { + delete storage_; + storage_ = NULL; + } +} + + +u_int32_t MachMessage::GetDataLength() { + return EndianU32_LtoN(GetDataPacket()->data_length); +} + + // The message ID may be used as a code identifying the type of message +void MachMessage::SetMessageID(int32_t message_id) { + GetDataPacket()->id = EndianU32_NtoL(message_id); +} + +int32_t MachMessage::GetMessageID() { + return EndianU32_LtoN(GetDataPacket()->id); +} + + +//============================================================================== +// returns true if successful +bool MachMessage::SetData(const void* data, + int32_t data_length) { + // Enforce the fact that it's only safe to call this method once on a + // message. + DCHECK(GetDataPacket()->data_length == 0); + + // first check to make sure we have enough space + int size = CalculateSize(); + int new_size = size + data_length; + + if ((unsigned)new_size > storage_length_bytes_) { + return false; // not enough space + } + + GetDataPacket()->data_length = EndianU32_NtoL(data_length); + if (data) memcpy(GetDataPacket()->data, data, data_length); + + // Update the Mach header with the new aligned size of the message. + CalculateSize(); + + return true; +} + +//============================================================================== +// calculates and returns the total size of the message +// Currently, the entire message MUST fit inside of the MachMessage +// messsage size <= EmptyMessageSize() +int MachMessage::CalculateSize() { + int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t); + + // add space for MessageDataPacket + int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3; + size += 2*sizeof(int32_t) + alignedDataLength; + + // add space for descriptors + size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor); + + Head()->msgh_size = size; + + return size; +} + +//============================================================================== +MachMessage::MessageDataPacket *MachMessage::GetDataPacket() { + int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount(); + MessageDataPacket *packet = + reinterpret_cast<MessageDataPacket*>(storage_->padding + desc_size); + + return packet; +} + +//============================================================================== +void MachMessage::SetDescriptor(int n, + const MachMsgPortDescriptor &desc) { + MachMsgPortDescriptor *desc_array = + reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding); + desc_array[n] = desc; +} + +//============================================================================== +// returns true if successful otherwise there was not enough space +bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) { + // first check to make sure we have enough space + int size = CalculateSize(); + int new_size = size + sizeof(MachMsgPortDescriptor); + + if ((unsigned)new_size > storage_length_bytes_) { + return false; // not enough space + } + + // unfortunately, we need to move the data to allow space for the + // new descriptor + u_int8_t *p = reinterpret_cast<u_int8_t*>(GetDataPacket()); + bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t)); + + SetDescriptor(GetDescriptorCount(), desc); + SetDescriptorCount(GetDescriptorCount() + 1); + + CalculateSize(); + + return true; +} + +//============================================================================== +void MachMessage::SetDescriptorCount(int n) { + storage_->body.msgh_descriptor_count = n; + + if (n > 0) { + Head()->msgh_bits |= MACH_MSGH_BITS_COMPLEX; + } else { + Head()->msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; + } +} + +//============================================================================== +MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) { + if (n < GetDescriptorCount()) { + MachMsgPortDescriptor *desc = + reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding); + return desc + n; + } + + return nil; +} + +//============================================================================== +mach_port_t MachMessage::GetTranslatedPort(int n) { + if (n < GetDescriptorCount()) { + return GetDescriptor(n)->GetMachPort(); + } + return MACH_PORT_NULL; +} + +#pragma mark - + +//============================================================================== +// create a new mach port for receiving messages and register a name for it +ReceivePort::ReceivePort(const char *receive_port_name) { + mach_port_t current_task = mach_task_self(); + + init_result_ = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &port_); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = mach_port_insert_right(current_task, + port_, + port_, + MACH_MSG_TYPE_MAKE_SEND); + + if (init_result_ != KERN_SUCCESS) + return; + + NSPort *ns_port = [NSMachPort portWithMachPort:port_]; + NSString *port_name = [NSString stringWithUTF8String:receive_port_name]; + [[NSMachBootstrapServer sharedInstance] registerPort:ns_port name:port_name]; +} + +//============================================================================== +// create a new mach port for receiving messages +ReceivePort::ReceivePort() { + mach_port_t current_task = mach_task_self(); + + init_result_ = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &port_); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = mach_port_insert_right(current_task, + port_, + port_, + MACH_MSG_TYPE_MAKE_SEND); +} + +//============================================================================== +// Given an already existing mach port, use it. We take ownership of the +// port and deallocate it in our destructor. +ReceivePort::ReceivePort(mach_port_t receive_port) + : port_(receive_port), + init_result_(KERN_SUCCESS) { +} + +//============================================================================== +ReceivePort::~ReceivePort() { + if (init_result_ == KERN_SUCCESS) + mach_port_deallocate(mach_task_self(), port_); +} + +//============================================================================== +kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message, + mach_msg_timeout_t timeout) { + if (!out_message) { + return KERN_INVALID_ARGUMENT; + } + + // return any error condition encountered in constructor + if (init_result_ != KERN_SUCCESS) + return init_result_; + + out_message->Head()->msgh_bits = 0; + out_message->Head()->msgh_local_port = port_; + out_message->Head()->msgh_remote_port = MACH_PORT_NULL; + out_message->Head()->msgh_reserved = 0; + out_message->Head()->msgh_id = 0; + + kern_return_t result = mach_msg(out_message->Head(), + MACH_RCV_MSG | (timeout == MACH_MSG_TIMEOUT_NONE ? 0 : MACH_RCV_TIMEOUT), + 0, + out_message->MaxSize(), + port_, + timeout, // timeout in ms + MACH_PORT_NULL); + + return result; +} + +//============================================================================== +// send a message to this port +kern_return_t ReceivePort::SendMessageToSelf(MachSendMessage& message, mach_msg_timeout_t timeout) { + if (message.Head()->msgh_size == 0) { + NOTREACHED(); + return KERN_INVALID_VALUE; // just for safety -- never should occur + }; + + if (init_result_ != KERN_SUCCESS) + return init_result_; + + message.Head()->msgh_remote_port = port_; + message.Head()->msgh_bits + = MACH_MSGH_BITS (MACH_MSG_TYPE_MAKE_SEND, + MACH_MSG_TYPE_MAKE_SEND_ONCE); + kern_return_t result = mach_msg(message.Head(), + MACH_SEND_MSG | (timeout == MACH_MSG_TIMEOUT_NONE ? 0 : MACH_SEND_TIMEOUT), + message.Head()->msgh_size, + 0, + MACH_PORT_NULL, + timeout, // timeout in ms + MACH_PORT_NULL); + + return result; + +} + + +#pragma mark - + +//============================================================================== +// get a port with send rights corresponding to a named registered service +MachPortSender::MachPortSender(const char *receive_port_name) { + mach_port_t bootstrap_port = 0; + init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = bootstrap_look_up(bootstrap_port, + const_cast<char*>(receive_port_name), + &send_port_); +} + +//============================================================================== +MachPortSender::MachPortSender(mach_port_t send_port) + : send_port_(send_port), + init_result_(KERN_SUCCESS) { +} + +//============================================================================== +kern_return_t MachPortSender::SendMessage(MachSendMessage &message, + mach_msg_timeout_t timeout) { + if (message.Head()->msgh_size == 0) { + NOTREACHED(); + return KERN_INVALID_VALUE; // just for safety -- never should occur + }; + + if (init_result_ != KERN_SUCCESS) + return init_result_; + + message.Head()->msgh_remote_port = send_port_; + + kern_return_t result = mach_msg(message.Head(), + MACH_SEND_MSG | (timeout == MACH_MSG_TIMEOUT_NONE ? 0 : MACH_SEND_TIMEOUT), + message.Head()->msgh_size, + 0, + MACH_PORT_NULL, + timeout, // timeout in ms + MACH_PORT_NULL); + + return result; +} diff --git a/ipc/chromium/src/chrome/common/mach_message_source_mac.cc b/ipc/chromium/src/chrome/common/mach_message_source_mac.cc new file mode 100644 index 000000000..7ab2b6074 --- /dev/null +++ b/ipc/chromium/src/chrome/common/mach_message_source_mac.cc @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 "chrome/common/mach_message_source_mac.h" + +#include "base/logging.h" + +MachMessageSource::MachMessageSource(mach_port_t port, + MachPortListener* msg_listener, + bool* success) { + DCHECK(msg_listener); + DCHECK(success); + DCHECK(port != MACH_PORT_NULL); + + CFMachPortContext port_context = {0}; + port_context.info = msg_listener; + + scoped_cftyperef<CFMachPortRef> cf_mach_port_ref( + CFMachPortCreateWithPort(kCFAllocatorDefault, + port, + MachMessageSource::OnReceiveMachMessage, + &port_context, + NULL)); + + if (cf_mach_port_ref.get() == NULL) { + CHROMIUM_LOG(WARNING) << "CFMachPortCreate failed"; + *success = false; + return; + } + + // Create a RL source. + machport_runloop_ref_.reset( + CFMachPortCreateRunLoopSource(kCFAllocatorDefault, + cf_mach_port_ref.get(), + 0)); + + if (machport_runloop_ref_.get() == NULL) { + CHROMIUM_LOG(WARNING) << "CFMachPortCreateRunLoopSource failed"; + *success = false; + return; + } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), + machport_runloop_ref_.get(), + kCFRunLoopCommonModes); + *success = true; +} + +MachMessageSource::~MachMessageSource() { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), + machport_runloop_ref_.get(), + kCFRunLoopCommonModes); +} + +// static +void MachMessageSource::OnReceiveMachMessage(CFMachPortRef port, void* msg, + CFIndex size, void* closure) { + MachPortListener *msg_listener = static_cast<MachPortListener*>(closure); + size_t msg_size = (size < 0) ? 0 : static_cast<size_t>(size); + DCHECK(msg && msg_size > 0); // this should never happen! + + if (msg_listener && msg && msg_size > 0) { + msg_listener->OnMachMessageReceived(msg, msg_size); + } +} diff --git a/ipc/chromium/src/chrome/common/mach_message_source_mac.h b/ipc/chromium/src/chrome/common/mach_message_source_mac.h new file mode 100644 index 000000000..57e8461ca --- /dev/null +++ b/ipc/chromium/src/chrome/common/mach_message_source_mac.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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. + +#ifndef CHROME_COMMON_MACH_MESSAGE_SOURCE_MAC_H_ +#define CHROME_COMMON_MACH_MESSAGE_SOURCE_MAC_H_ + +#include <CoreServices/CoreServices.h> + +#include "base/scoped_cftyperef.h" + +// Handles registering and cleaning up after a CFRunloopSource for a Mach port. +// Messages received on the port are piped through to a delegate. +// +// Example: +// class MyListener : public MachMessageSource::MachPortListener { +// public: +// void OnMachMessageReceived(void* mach_msg, size_t size) { +// printf("received message on Mach port\n"); +// } +// }; +// +// mach_port_t a_port = ...; +// MyListener listener; +// bool success = false; +// MachMessageSource message_source(port, listener, &success); +// +// if (!success) { +// exit(1); // Couldn't register mach runloop source. +// } +// +// CFRunLoopRun(); // Process messages on runloop... +class MachMessageSource { + public: + // Classes that want to listen on a Mach port can implement + // OnMachMessageReceived, |mach_msg| is a pointer to the raw message data and + // |size| is the buffer size; + class MachPortListener { + public: + virtual void OnMachMessageReceived(void* mach_msg, size_t size) = 0; + }; + + // |listener| is a week reference passed to CF, it needs to remain in + // existence till this object is destroeyd. + MachMessageSource(mach_port_t port, + MachPortListener* listener, + bool* success); + ~MachMessageSource(); + + private: + // Called by CF when a new message arrives on the Mach port. + static void OnReceiveMachMessage(CFMachPortRef port, void* msg, CFIndex size, + void* closure); + + scoped_cftyperef<CFRunLoopSourceRef> machport_runloop_ref_; + DISALLOW_COPY_AND_ASSIGN(MachMessageSource); +}; + +#endif // CHROME_COMMON_MACH_MESSAGE_SOURCE_MAC_H_ diff --git a/ipc/chromium/src/chrome/common/process_watcher.h b/ipc/chromium/src/chrome/common/process_watcher.h new file mode 100644 index 000000000..5bc6c02b5 --- /dev/null +++ b/ipc/chromium/src/chrome/common/process_watcher.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + +#ifndef CHROME_COMMON_PROCESS_WATCHER_H_ +#define CHROME_COMMON_PROCESS_WATCHER_H_ + +#include "base/basictypes.h" +#include "base/process_util.h" + +class ProcessWatcher { + public: + // This method ensures that the specified process eventually terminates, and + // then it closes the given process handle. + // + // It assumes that the process has already been signalled to exit, and it + // begins by waiting a small amount of time for it to exit. If the process + // does not appear to have exited, then this function starts to become + // aggressive about ensuring that the process terminates. + // + // This method does not block the calling thread. + // + // NOTE: The process handle must have been opened with the PROCESS_TERMINATE + // and SYNCHRONIZE permissions. + // + static void EnsureProcessTerminated(base::ProcessHandle process_handle + , bool force=true + ); + + private: + // Do not instantiate this class. + ProcessWatcher(); + + DISALLOW_COPY_AND_ASSIGN(ProcessWatcher); +}; + +#endif // CHROME_COMMON_PROCESS_WATCHER_H_ diff --git a/ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc b/ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc new file mode 100644 index 000000000..5abe143c1 --- /dev/null +++ b/ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <errno.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include "base/eintr_wrapper.h" +#include "base/message_loop.h" +#include "base/process_util.h" + +#include "chrome/common/process_watcher.h" + +// Maximum amount of time (in milliseconds) to wait for the process to exit. +// XXX/cjones: fairly arbitrary, chosen to match process_watcher_win.cc +static const int kMaxWaitMs = 2000; + +namespace { + +bool +IsProcessDead(pid_t process) +{ + bool exited = false; + // don't care if the process crashed, just if it exited + base::DidProcessCrash(&exited, process); + return exited; +} + + +class ChildReaper : public base::MessagePumpLibevent::SignalEvent, + public base::MessagePumpLibevent::SignalWatcher +{ +public: + explicit ChildReaper(pid_t process) : process_(process) + { + } + + virtual ~ChildReaper() + { + // subclasses should have cleaned up |process_| already + DCHECK(!process_); + + // StopCatching() is implicit + } + + // @override + virtual void OnSignal(int sig) + { + DCHECK(SIGCHLD == sig); + DCHECK(process_); + + // this may be the SIGCHLD for a process other than |process_| + if (IsProcessDead(process_)) { + process_ = 0; + StopCatching(); + } + } + +protected: + void WaitForChildExit() + { + DCHECK(process_); + HANDLE_EINTR(waitpid(process_, NULL, 0)); + } + + pid_t process_; + +private: + DISALLOW_EVIL_CONSTRUCTORS(ChildReaper); +}; + + +// Fear the reaper +class ChildGrimReaper : public ChildReaper, + public mozilla::Runnable +{ +public: + explicit ChildGrimReaper(pid_t process) : ChildReaper(process) + { + } + + virtual ~ChildGrimReaper() + { + if (process_) + KillProcess(); + } + + NS_IMETHOD Run() override + { + // we may have already been signaled by the time this runs + if (process_) + KillProcess(); + + return NS_OK; + } + +private: + void KillProcess() + { + DCHECK(process_); + + if (IsProcessDead(process_)) { + process_ = 0; + return; + } + + if (0 == kill(process_, SIGKILL)) { + // XXX this will block for whatever amount of time it takes the + // XXX OS to tear down the process's resources. might need to + // XXX rethink this if it proves expensive + WaitForChildExit(); + } + else { + CHROMIUM_LOG(ERROR) << "Failed to deliver SIGKILL to " << process_ << "!" + << "("<< errno << ")."; + } + process_ = 0; + } + + DISALLOW_EVIL_CONSTRUCTORS(ChildGrimReaper); +}; + + +class ChildLaxReaper : public ChildReaper, + public MessageLoop::DestructionObserver +{ +public: + explicit ChildLaxReaper(pid_t process) : ChildReaper(process) + { + } + + virtual ~ChildLaxReaper() + { + // WillDestroyCurrentMessageLoop() should have reaped process_ already + DCHECK(!process_); + } + + // @override + virtual void OnSignal(int sig) + { + ChildReaper::OnSignal(sig); + + if (!process_) { + MessageLoop::current()->RemoveDestructionObserver(this); + delete this; + } + } + + // @override + virtual void WillDestroyCurrentMessageLoop() + { + DCHECK(process_); + + WaitForChildExit(); + process_ = 0; + + // XXX don't think this is necessary, since destruction can only + // be observed once, but can't hurt + MessageLoop::current()->RemoveDestructionObserver(this); + delete this; + } + +private: + DISALLOW_EVIL_CONSTRUCTORS(ChildLaxReaper); +}; + +} // namespace <anon> + + +/** + * Do everything possible to ensure that |process| has been reaped + * before this process exits. + * + * |grim| decides how strict to be with the child's shutdown. + * + * | child exit timeout | upon parent shutdown: + * +--------------------+---------------------------------- + * force=true | 2 seconds | kill(child, SIGKILL) + * force=false | infinite | waitpid(child) + * + * If a child process doesn't shut down properly, and |grim=false| + * used, then the parent will wait on the child forever. So, + * |force=false| is expected to be used when an external entity can be + * responsible for terminating hung processes, e.g. automated test + * harnesses. + */ +void +ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process, + bool force) +{ + DCHECK(process != base::GetCurrentProcId()); + DCHECK(process > 0); + + if (IsProcessDead(process)) + return; + + MessageLoopForIO* loop = MessageLoopForIO::current(); + if (force) { + RefPtr<ChildGrimReaper> reaper = new ChildGrimReaper(process); + + loop->CatchSignal(SIGCHLD, reaper, reaper); + // |loop| takes ownership of |reaper| + loop->PostDelayedTask(reaper.forget(), kMaxWaitMs); + } else { + ChildLaxReaper* reaper = new ChildLaxReaper(process); + + loop->CatchSignal(SIGCHLD, reaper, reaper); + // |reaper| destroys itself after destruction notification + loop->AddDestructionObserver(reaper); + } +} diff --git a/ipc/chromium/src/chrome/common/process_watcher_win.cc b/ipc/chromium/src/chrome/common/process_watcher_win.cc new file mode 100644 index 000000000..fccb83999 --- /dev/null +++ b/ipc/chromium/src/chrome/common/process_watcher_win.cc @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "chrome/common/process_watcher.h" + +#include "base/message_loop.h" +#include "base/object_watcher.h" +#include "base/sys_info.h" + +// Maximum amount of time (in milliseconds) to wait for the process to exit. +static const int kWaitInterval = 2000; + +namespace { + +class ChildReaper : public mozilla::Runnable, + public base::ObjectWatcher::Delegate, + public MessageLoop::DestructionObserver { + public: + explicit ChildReaper(base::ProcessHandle process, bool force) + : process_(process), force_(force) { + watcher_.StartWatching(process_, this); + } + + virtual ~ChildReaper() { + if (process_) { + KillProcess(); + DCHECK(!process_) << "Make sure to close the handle."; + } + } + + // MessageLoop::DestructionObserver ----------------------------------------- + + virtual void WillDestroyCurrentMessageLoop() + { + MOZ_ASSERT(!force_); + if (process_) { + WaitForSingleObject(process_, INFINITE); + base::CloseProcessHandle(process_); + process_ = 0; + + MessageLoop::current()->RemoveDestructionObserver(this); + delete this; + } + } + + // Task --------------------------------------------------------------------- + + NS_IMETHOD Run() override { + MOZ_ASSERT(force_); + if (process_) { + KillProcess(); + } + return NS_OK; + } + + // MessageLoop::Watcher ----------------------------------------------------- + + virtual void OnObjectSignaled(HANDLE object) { + // When we're called from KillProcess, the ObjectWatcher may still be + // watching. the process handle, so make sure it has stopped. + watcher_.StopWatching(); + + base::CloseProcessHandle(process_); + process_ = 0; + + if (!force_) { + MessageLoop::current()->RemoveDestructionObserver(this); + delete this; + } + } + + private: + void KillProcess() { + MOZ_ASSERT(force_); + + // OK, time to get frisky. We don't actually care when the process + // terminates. We just care that it eventually terminates, and that's what + // TerminateProcess should do for us. Don't check for the result code since + // it fails quite often. This should be investigated eventually. + TerminateProcess(process_, base::PROCESS_END_PROCESS_WAS_HUNG); + + // Now, just cleanup as if the process exited normally. + OnObjectSignaled(process_); + } + + // The process that we are watching. + base::ProcessHandle process_; + + base::ObjectWatcher watcher_; + + bool force_; + + DISALLOW_EVIL_CONSTRUCTORS(ChildReaper); +}; + +} // namespace + +// static +void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process, bool force) { + DCHECK(process != GetCurrentProcess()); + + // If already signaled, then we are done! + if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) { + base::CloseProcessHandle(process); + return; + } + + MessageLoopForIO* loop = MessageLoopForIO::current(); + if (force) { + RefPtr<mozilla::Runnable> task = new ChildReaper(process, force); + loop->PostDelayedTask(task.forget(), kWaitInterval); + } else { + loop->AddDestructionObserver(new ChildReaper(process, force)); + } +} diff --git a/ipc/chromium/src/chrome/common/transport_dib.h b/ipc/chromium/src/chrome/common/transport_dib.h new file mode 100644 index 000000000..b1e5c0fab --- /dev/null +++ b/ipc/chromium/src/chrome/common/transport_dib.h @@ -0,0 +1,126 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2009 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. + +#ifndef CHROME_COMMON_TRANSPORT_DIB_H_ +#define CHROME_COMMON_TRANSPORT_DIB_H_ + +#include "base/basictypes.h" + +#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_BSD) +#include "base/shared_memory.h" +#endif + +#if defined(OS_WIN) +#include <windows.h> +#elif defined(OS_LINUX) +#include "chrome/common/x11_util.h" +#endif + +// ----------------------------------------------------------------------------- +// A TransportDIB is a block of memory that is used to transport pixels +// between processes: from the renderer process to the browser, and +// between renderer and plugin processes. +// ----------------------------------------------------------------------------- +class TransportDIB { + public: + ~TransportDIB(); + + // Two typedefs are defined. A Handle is the type which can be sent over + // the wire so that the remote side can map the transport DIB. The Id typedef + // is sufficient to identify the transport DIB when you know that the remote + // side already may have it mapped. +#if defined(OS_WIN) + typedef HANDLE Handle; + // On Windows, the Id type includes a sequence number (epoch) to solve an ABA + // issue: + // 1) Process A creates a transport DIB with HANDLE=1 and sends to B. + // 2) Process B maps the transport DIB and caches 1 -> DIB. + // 3) Process A closes the transport DIB and creates a new one. The new DIB + // is also assigned HANDLE=1. + // 4) Process A sends the Handle to B, but B incorrectly believes that it + // already has it cached. + struct HandleAndSequenceNum { + HandleAndSequenceNum() + : handle(NULL), + sequence_num(0) { + } + + HandleAndSequenceNum(HANDLE h, uint32_t seq_num) + : handle(h), + sequence_num(seq_num) { + } + + bool operator< (const HandleAndSequenceNum& other) const { + // Use the lexicographic order on the tuple <handle, sequence_num>. + if (other.handle != handle) + return other.handle < handle; + return other.sequence_num < sequence_num; + } + + HANDLE handle; + uint32_t sequence_num; + }; + typedef HandleAndSequenceNum Id; +#elif defined(OS_MACOSX) || defined(OS_BSD) + typedef base::SharedMemoryHandle Handle; + // On Mac, the inode number of the backing file is used as an id. + typedef base::SharedMemoryId Id; +#elif defined(OS_LINUX) + typedef int Handle; // These two ints are SysV IPC shared memory keys + typedef int Id; +#endif + + // Create a new TransportDIB + // size: the minimum size, in bytes + // epoch: Windows only: a global counter. See comment above. + // returns: NULL on failure + static TransportDIB* Create(size_t size, uint32_t sequence_num); + + // Map the referenced transport DIB. Returns NULL on failure. + static TransportDIB* Map(Handle transport_dib); + + // Return a pointer to the shared memory + void* memory() const; + + // Return the maximum size of the shared memory. This is not the amount of + // data which is valid, you have to know that via other means, this is simply + // the maximum amount that /could/ be valid. + size_t size() const { return size_; } + + // Return the identifier which can be used to refer to this shared memory + // on the wire. + Id id() const; + + // Return a handle to the underlying shared memory. This can be sent over the + // wire to give this transport DIB to another process. + Handle handle() const; + +#if defined(OS_LINUX) + // Map the shared memory into the X server and return an id for the shared + // segment. + XID MapToX(Display* connection); +#endif + + private: + TransportDIB(); +#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_BSD) + explicit TransportDIB(base::SharedMemoryHandle dib); + base::SharedMemory shared_memory_; +#elif defined(OS_LINUX) + int key_; // SysV shared memory id + void* address_; // mapped address + XID x_shm_; // X id for the shared segment + Display* display_; // connection to the X server +#endif +#ifdef OS_WIN + uint32_t sequence_num_; +#endif + size_t size_; // length, in bytes +}; + +class MessageLoop; + +#endif // CHROME_COMMON_TRANSPORT_DIB_H_ diff --git a/ipc/chromium/src/chrome/common/transport_dib_mac.cc b/ipc/chromium/src/chrome/common/transport_dib_mac.cc new file mode 100644 index 000000000..feb78b207 --- /dev/null +++ b/ipc/chromium/src/chrome/common/transport_dib_mac.cc @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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 "chrome/common/transport_dib.h" + +#include <unistd.h> +#include <sys/stat.h> + +#include "base/eintr_wrapper.h" +#include "base/shared_memory.h" + +TransportDIB::TransportDIB() + : size_(0) { +} + +TransportDIB::TransportDIB(TransportDIB::Handle dib) + : shared_memory_(dib, false /* read write */), + size_(0) { +} + +TransportDIB::~TransportDIB() { +} + +// static +TransportDIB* TransportDIB::Create(size_t size, uint32_t sequence_num) { + TransportDIB* dib = new TransportDIB; + if (!dib->shared_memory_.Create("", false /* read write */, + false /* do not open existing */, size)) { + delete dib; + return NULL; + } + + dib->size_ = size; + return dib; +} + +// static +TransportDIB* TransportDIB::Map(TransportDIB::Handle handle) { + TransportDIB* dib = new TransportDIB(handle); + struct stat st; + fstat(handle.fd, &st); + + if (!dib->shared_memory_.Map(st.st_size)) { + delete dib; + HANDLE_EINTR(close(handle.fd)); + return nullptr; + } + + dib->size_ = st.st_size; + + return dib; +} + +void* TransportDIB::memory() const { + return shared_memory_.memory(); +} + +TransportDIB::Id TransportDIB::id() const { + return shared_memory_.id(); +} + +TransportDIB::Handle TransportDIB::handle() const { + return shared_memory_.handle(); +} diff --git a/ipc/chromium/src/chrome/common/transport_dib_win.cc b/ipc/chromium/src/chrome/common/transport_dib_win.cc new file mode 100644 index 000000000..3ab789847 --- /dev/null +++ b/ipc/chromium/src/chrome/common/transport_dib_win.cc @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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 <limits> +#include <windows.h> + +#include "base/logging.h" +#include "base/sys_info.h" +#include "chrome/common/transport_dib.h" + +TransportDIB::TransportDIB() { +} + +TransportDIB::~TransportDIB() { +} + +TransportDIB::TransportDIB(HANDLE handle) + : shared_memory_(handle, false /* read write */) { +} + +// static +TransportDIB* TransportDIB::Create(size_t size, uint32_t sequence_num) { + size_t allocation_granularity = base::SysInfo::VMAllocationGranularity(); + size = size / allocation_granularity + 1; + size = size * allocation_granularity; + + TransportDIB* dib = new TransportDIB; + + if (!dib->shared_memory_.Create("", false /* read write */, + true /* open existing */, size)) { + delete dib; + return NULL; + } + + dib->size_ = size; + dib->sequence_num_ = sequence_num; + + return dib; +} + +// static +TransportDIB* TransportDIB::Map(TransportDIB::Handle handle) { + TransportDIB* dib = new TransportDIB(handle); + if (!dib->shared_memory_.Map(0 /* map whole shared memory segment */)) { + CHROMIUM_LOG(ERROR) << "Failed to map transport DIB" + << " handle:" << handle + << " error:" << GetLastError(); + delete dib; + return NULL; + } + + // There doesn't seem to be any way to find the size of the shared memory + // region! GetFileSize indicates that the handle is invalid. Thus, we + // conservatively set the size to the maximum and hope that the renderer + // isn't about to ask us to read off the end of the array. + dib->size_ = std::numeric_limits<size_t>::max(); + + return dib; +} + +void* TransportDIB::memory() const { + return shared_memory_.memory(); +} + +TransportDIB::Handle TransportDIB::handle() const { + return shared_memory_.handle(); +} + +TransportDIB::Id TransportDIB::id() const { + return Id(shared_memory_.handle(), sequence_num_); +} diff --git a/ipc/chromium/src/chrome/common/x11_util.h b/ipc/chromium/src/chrome/common/x11_util.h new file mode 100644 index 000000000..688178356 --- /dev/null +++ b/ipc/chromium/src/chrome/common/x11_util.h @@ -0,0 +1,19 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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. + +#ifndef CHROME_COMMON_X11_UTIL_H_ +#define CHROME_COMMON_X11_UTIL_H_ + +// This file declares utility functions for X11 (Linux only). +// +// These functions do not require the Xlib headers to be included (which is why +// we use a void* for Visual*). The Xlib headers are highly polluting so we try +// hard to limit their spread into the rest of the code. + +typedef unsigned long XID; +typedef struct _XDisplay Display; + +#endif // CHROME_COMMON_X11_UTIL_H_ |