diff options
Diffstat (limited to 'ipc')
44 files changed, 9076 insertions, 0 deletions
diff --git a/ipc/OWNERS b/ipc/OWNERS new file mode 100644 index 000000000000..fc187f8ba4f1 --- /dev/null +++ b/ipc/OWNERS @@ -0,0 +1,7 @@ +agl@chromium.org +cpu@chromium.org +darin@chromium.org +dmaclach@chromium.org +jam@chromium.org +jeremy@chromium.org +tsepez@chromium.org diff --git a/ipc/file_descriptor_set_posix.cc b/ipc/file_descriptor_set_posix.cc new file mode 100644 index 000000000000..7f17322de47b --- /dev/null +++ b/ipc/file_descriptor_set_posix.cc @@ -0,0 +1,136 @@ +// 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 "ipc/file_descriptor_set_posix.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include "base/eintr_wrapper.h" +#include "base/logging.h" + +FileDescriptorSet::FileDescriptorSet() + : consumed_descriptor_highwater_(0) { +} + +FileDescriptorSet::~FileDescriptorSet() { + if (consumed_descriptor_highwater_ == descriptors_.size()) + return; + + 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) + if (HANDLE_EINTR(close(descriptors_[i].fd)) < 0) + PLOG(ERROR) << "close"; + } +} + +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; + } +} + +bool FileDescriptorSet::ContainsDirectoryDescriptor() const { + struct stat st; + + for (std::vector<base::FileDescriptor>::const_iterator + i = descriptors_.begin(); i != descriptors_.end(); ++i) { + if (fstat(i->fd, &st) == 0 && S_ISDIR(st.st_mode)) + return true; + } + + return false; +} + +void FileDescriptorSet::CommitAll() { + for (std::vector<base::FileDescriptor>::iterator + i = descriptors_.begin(); i != descriptors_.end(); ++i) { + if (i->auto_close) + if (HANDLE_EINTR(close(i->fd)) < 0) + PLOG(ERROR) << "close"; + } + 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/file_descriptor_set_posix.h b/ipc/file_descriptor_set_posix.h new file mode 100644 index 000000000000..1554c38510d2 --- /dev/null +++ b/ipc/file_descriptor_set_posix.h @@ -0,0 +1,117 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_FILE_DESCRIPTOR_SET_POSIX_H_ +#define IPC_FILE_DESCRIPTOR_SET_POSIX_H_ +#pragma once + +#include <vector> + +#include "base/basictypes.h" +#include "base/file_descriptor_posix.h" +#include "base/memory/ref_counted.h" +#include "ipc/ipc_export.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 IPC_EXPORT FileDescriptorSet + : public base::RefCountedThreadSafe<FileDescriptorSet> { + public: + FileDescriptorSet(); + + // This is the maximum number of descriptors per message. We need to know this + // because the control message kernel interface has to be given a buffer which + // is large enough to store all the descriptor numbers. Otherwise the kernel + // tells us that it truncated the control data and the extra descriptors are + // lost. + // + // In debugging mode, it's a fatal error to try and add more than this number + // of descriptors to a FileDescriptorSet. + enum { + MAX_DESCRIPTORS_PER_MESSAGE = 5, + }; + + // --------------------------------------------------------------------------- + // 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(); + // Returns true if any contained file descriptors appear to be handles to a + // directory. + bool ContainsDirectoryDescriptor() const; + + // --------------------------------------------------------------------------- + + + // --------------------------------------------------------------------------- + // 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: + friend class base::RefCountedThreadSafe<FileDescriptorSet>; + + ~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 // IPC_FILE_DESCRIPTOR_SET_POSIX_H_ diff --git a/ipc/file_descriptor_set_posix_unittest.cc b/ipc/file_descriptor_set_posix_unittest.cc new file mode 100644 index 000000000000..5fc8c50fc9ed --- /dev/null +++ b/ipc/file_descriptor_set_posix_unittest.cc @@ -0,0 +1,179 @@ +// 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. + +// This test is POSIX only. + +#include "ipc/file_descriptor_set_posix.h" + +#include <unistd.h> +#include <fcntl.h> + +#include "base/basictypes.h" +#include "base/eintr_wrapper.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// Get a safe file descriptor for test purposes. +int GetSafeFd() { + return open("/dev/null", O_RDONLY); +} + +// Returns true if fd was already closed. Closes fd if not closed. +bool VerifyClosed(int fd) { + const int duped = dup(fd); + if (duped != -1) { + EXPECT_NE(HANDLE_EINTR(close(duped)), -1); + EXPECT_NE(HANDLE_EINTR(close(fd)), -1); + return false; + } + return true; +} + +// The FileDescriptorSet will try and close some of the descriptor numbers +// which we given it. This is the base descriptor value. It's great enough such +// that no real descriptor will accidently be closed. +static const int kFDBase = 50000; + +TEST(FileDescriptorSet, BasicAdd) { + scoped_refptr<FileDescriptorSet> set(new FileDescriptorSet); + + ASSERT_EQ(set->size(), 0u); + ASSERT_TRUE(set->empty()); + ASSERT_TRUE(set->Add(kFDBase)); + ASSERT_EQ(set->size(), 1u); + ASSERT_TRUE(!set->empty()); + + // Empties the set and stops a warning about deleting a set with unconsumed + // descriptors + set->CommitAll(); +} + +TEST(FileDescriptorSet, BasicAddAndClose) { + scoped_refptr<FileDescriptorSet> set(new FileDescriptorSet); + + ASSERT_EQ(set->size(), 0u); + ASSERT_TRUE(set->empty()); + const int fd = GetSafeFd(); + ASSERT_TRUE(set->AddAndAutoClose(fd)); + ASSERT_EQ(set->size(), 1u); + ASSERT_TRUE(!set->empty()); + + set->CommitAll(); + + ASSERT_TRUE(VerifyClosed(fd)); +} +TEST(FileDescriptorSet, MaxSize) { + scoped_refptr<FileDescriptorSet> set(new FileDescriptorSet); + + for (unsigned i = 0; + i < FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE; ++i) { + ASSERT_TRUE(set->Add(kFDBase + 1 + i)); + } + + ASSERT_TRUE(!set->Add(kFDBase)); + + set->CommitAll(); +} + +TEST(FileDescriptorSet, SetDescriptors) { + scoped_refptr<FileDescriptorSet> set(new FileDescriptorSet); + + ASSERT_TRUE(set->empty()); + set->SetDescriptors(NULL, 0); + ASSERT_TRUE(set->empty()); + + const int fd = GetSafeFd(); + static const int fds[] = {fd}; + set->SetDescriptors(fds, 1); + ASSERT_TRUE(!set->empty()); + ASSERT_EQ(set->size(), 1u); + + set->CommitAll(); + + ASSERT_TRUE(VerifyClosed(fd)); +} + +TEST(FileDescriptorSet, GetDescriptors) { + scoped_refptr<FileDescriptorSet> set(new FileDescriptorSet); + + set->GetDescriptors(NULL); + ASSERT_TRUE(set->Add(kFDBase)); + + int fds[1]; + fds[0] = 0; + set->GetDescriptors(fds); + ASSERT_EQ(fds[0], kFDBase); + set->CommitAll(); + ASSERT_TRUE(set->empty()); +} + +TEST(FileDescriptorSet, WalkInOrder) { + scoped_refptr<FileDescriptorSet> set(new FileDescriptorSet); + + ASSERT_TRUE(set->Add(kFDBase)); + ASSERT_TRUE(set->Add(kFDBase + 1)); + ASSERT_TRUE(set->Add(kFDBase + 2)); + + ASSERT_EQ(set->GetDescriptorAt(0), kFDBase); + ASSERT_EQ(set->GetDescriptorAt(1), kFDBase + 1); + ASSERT_EQ(set->GetDescriptorAt(2), kFDBase + 2); + + set->CommitAll(); +} + +TEST(FileDescriptorSet, WalkWrongOrder) { + scoped_refptr<FileDescriptorSet> set(new FileDescriptorSet); + + ASSERT_TRUE(set->Add(kFDBase)); + ASSERT_TRUE(set->Add(kFDBase + 1)); + ASSERT_TRUE(set->Add(kFDBase + 2)); + + ASSERT_EQ(set->GetDescriptorAt(0), kFDBase); + ASSERT_EQ(set->GetDescriptorAt(2), -1); + + set->CommitAll(); +} + +TEST(FileDescriptorSet, WalkCycle) { + scoped_refptr<FileDescriptorSet> set(new FileDescriptorSet); + + ASSERT_TRUE(set->Add(kFDBase)); + ASSERT_TRUE(set->Add(kFDBase + 1)); + ASSERT_TRUE(set->Add(kFDBase + 2)); + + ASSERT_EQ(set->GetDescriptorAt(0), kFDBase); + ASSERT_EQ(set->GetDescriptorAt(1), kFDBase + 1); + ASSERT_EQ(set->GetDescriptorAt(2), kFDBase + 2); + ASSERT_EQ(set->GetDescriptorAt(0), kFDBase); + ASSERT_EQ(set->GetDescriptorAt(1), kFDBase + 1); + ASSERT_EQ(set->GetDescriptorAt(2), kFDBase + 2); + ASSERT_EQ(set->GetDescriptorAt(0), kFDBase); + ASSERT_EQ(set->GetDescriptorAt(1), kFDBase + 1); + ASSERT_EQ(set->GetDescriptorAt(2), kFDBase + 2); + + set->CommitAll(); +} + +TEST(FileDescriptorSet, DontClose) { + scoped_refptr<FileDescriptorSet> set(new FileDescriptorSet); + + const int fd = GetSafeFd(); + ASSERT_TRUE(set->Add(fd)); + set->CommitAll(); + + ASSERT_FALSE(VerifyClosed(fd)); +} + +TEST(FileDescriptorSet, DoClose) { + scoped_refptr<FileDescriptorSet> set(new FileDescriptorSet); + + const int fd = GetSafeFd(); + ASSERT_TRUE(set->AddAndAutoClose(fd)); + set->CommitAll(); + + ASSERT_TRUE(VerifyClosed(fd)); +} + +} // namespace diff --git a/ipc/ipc.gyp b/ipc/ipc.gyp new file mode 100644 index 000000000000..ab57cd9b4dd2 --- /dev/null +++ b/ipc/ipc.gyp @@ -0,0 +1,90 @@ +# 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. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'target_defaults': { + 'sources/': [ + ['exclude', '/win/'], + ['exclude', '_(posix|win)(_unittest)?\\.(cc|mm?)$'], + ['exclude', '/win_[^/]*\\.cc$'], + ], + 'conditions': [ + ['os_posix == 1 and OS != "mac"', {'sources/': [ + ['include', '_posix(_unittest)?\\.cc$'], + ]}], + ['OS=="mac"', {'sources/': [ + ['include', '_posix(_unittest)?\\.(cc|mm?)$'], + ]}], + ['OS=="win"', {'sources/': [ + ['include', '_win(_unittest)?\\.cc$'], + ['include', '/win/'], + ['include', '/win_[^/]*\\.cc$'], + ]}], + ], + }, + 'includes': [ + 'ipc.gypi', + ], + 'targets': [ + { + 'target_name': 'ipc_tests', + 'type': 'executable', + 'dependencies': [ + 'ipc', + '../base/base.gyp:base', + '../base/base.gyp:base_i18n', + '../base/base.gyp:test_support_base', + '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + '../testing/gtest.gyp:gtest', + ], + 'include_dirs': [ + '..' + ], + 'sources': [ + 'file_descriptor_set_posix_unittest.cc', + 'ipc_channel_posix_unittest.cc', + 'ipc_fuzzing_tests.cc', + 'ipc_message_unittest.cc', + 'ipc_send_fds_test.cc', + 'ipc_sync_channel_unittest.cc', + 'ipc_sync_message_unittest.cc', + 'ipc_sync_message_unittest.h', + 'ipc_tests.cc', + 'ipc_tests.h', + 'sync_socket_unittest.cc', + ], + 'conditions': [ + ['toolkit_uses_gtk == 1', { + 'dependencies': [ + '../build/linux/system.gyp:gtk', + ], + }], + ['os_posix == 1 and OS != "mac"', { + 'conditions': [ + ['linux_use_tcmalloc==1', { + 'dependencies': [ + '../base/allocator/allocator.gyp:allocator', + ], + }], + ], + }] + ], + }, + { + 'target_name': 'test_support_ipc', + 'type': 'static_library', + 'dependencies': [ + 'ipc', + '../base/base.gyp:base', + ], + 'sources': [ + 'ipc_test_sink.cc', + 'ipc_test_sink.h', + ], + }, + ], +} diff --git a/ipc/ipc.gypi b/ipc/ipc.gypi new file mode 100644 index 000000000000..7791934eabe0 --- /dev/null +++ b/ipc/ipc.gypi @@ -0,0 +1,109 @@ +# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'target_defaults': { + 'variables': { + 'ipc_target': 0, + }, + 'target_conditions': [ + # This part is shared between the targets defined below. + ['ipc_target==1', { + 'sources': [ + 'file_descriptor_set_posix.cc', + 'file_descriptor_set_posix.h', + 'ipc_channel.h', + 'ipc_channel_handle.h', + 'ipc_channel_posix.cc', + 'ipc_channel_posix.h', + 'ipc_channel_proxy.cc', + 'ipc_channel_proxy.h', + 'ipc_channel_win.cc', + 'ipc_channel_win.h', + 'ipc_descriptors.h', + 'ipc_export.h', + 'ipc_logging.cc', + 'ipc_logging.h', + 'ipc_message.cc', + 'ipc_message.h', + 'ipc_message_macros.h', + 'ipc_message_utils.cc', + 'ipc_message_utils.h', + 'ipc_param_traits.h', + 'ipc_platform_file.cc', + 'ipc_platform_file.h', + 'ipc_switches.cc', + 'ipc_switches.h', + 'ipc_sync_channel.cc', + 'ipc_sync_channel.h', + 'ipc_sync_message.cc', + 'ipc_sync_message.h', + 'ipc_sync_message_filter.cc', + 'ipc_sync_message_filter.h', + 'param_traits_log_macros.h', + 'param_traits_macros.h', + 'param_traits_read_macros.h', + 'param_traits_write_macros.h', + 'struct_constructor_macros.h', + 'struct_destructor_macros.h', + ], + 'defines': [ + 'IPC_IMPLEMENTATION', + ], + 'include_dirs': [ + '..', + ], + }], + ], + }, + 'targets': [ + { + 'target_name': 'ipc', + 'type': '<(component)', + 'variables': { + 'ipc_target': 1, + }, + 'dependencies': [ + '../base/base.gyp:base', + '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + ], + # TODO(gregoryd): direct_dependent_settings should be shared with the + # 64-bit target, but it doesn't work due to a bug in gyp + 'direct_dependent_settings': { + 'include_dirs': [ + '..', + ], + }, + }, + ], + 'conditions': [ + ['OS=="win"', { + 'targets': [ + { + 'target_name': 'ipc_win64', + 'type': '<(component)', + 'variables': { + 'ipc_target': 1, + }, + 'dependencies': [ + '../base/base.gyp:base_nacl_win64', + '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations_win64', + ], + # TODO(gregoryd): direct_dependent_settings should be shared with the + # 32-bit target, but it doesn't work due to a bug in gyp + 'direct_dependent_settings': { + 'include_dirs': [ + '..', + ], + }, + 'configurations': { + 'Common_Base': { + 'msvs_target_platform': 'x64', + }, + }, + }, + ], + }], + ], +} diff --git a/ipc/ipc_channel.h b/ipc/ipc_channel.h new file mode 100644 index 000000000000..cd57b4b89548 --- /dev/null +++ b/ipc/ipc_channel.h @@ -0,0 +1,204 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_IPC_CHANNEL_H_ +#define IPC_IPC_CHANNEL_H_ +#pragma once + +#include "base/compiler_specific.h" +#include "ipc/ipc_channel_handle.h" +#include "ipc/ipc_message.h" + +namespace IPC { + +//------------------------------------------------------------------------------ +// See +// http://www.chromium.org/developers/design-documents/inter-process-communication +// for overview of IPC in Chromium. + +// Channels are implemented using named pipes on Windows, and +// socket pairs (or in some special cases unix domain sockets) on POSIX. +// On Windows we access pipes in various processes by name. +// On POSIX we pass file descriptors to child processes and assign names to them +// in a lookup table. +// In general on POSIX we do not use unix domain sockets due to security +// concerns and the fact that they can leave garbage around the file system +// (MacOS does not support abstract named unix domain sockets). +// You can use unix domain sockets if you like on POSIX by constructing the +// the channel with the mode set to one of the NAMED modes. NAMED modes are +// currently used by automation and service processes. + +class IPC_EXPORT Channel : public Message::Sender { + // Security tests need access to the pipe handle. + friend class ChannelTest; + + public: + // Implemented by consumers of a Channel to receive messages. + class IPC_EXPORT Listener { + public: + virtual ~Listener() {} + + // Called when a message is received. Returns true iff the message was + // handled. + virtual bool OnMessageReceived(const Message& message) = 0; + + // Called when the channel is connected and we have received the internal + // Hello message from the peer. + virtual void OnChannelConnected(int32 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 defined(OS_POSIX) + // Called on the server side when a channel that listens for connections + // denies an attempt to connect. + virtual void OnChannelDenied() {} + + // Called on the server side when a channel that listens for connections + // has an error that causes the listening channel to close. + virtual void OnChannelListenError() {} +#endif // OS_POSIX + }; + + // Flags to test modes + enum ModeFlags { + MODE_NO_FLAG = 0x0, + MODE_SERVER_FLAG = 0x1, + MODE_CLIENT_FLAG = 0x2, + MODE_NAMED_FLAG = 0x4, +#if defined(OS_POSIX) + MODE_OPEN_ACCESS_FLAG = 0x8, // Don't restrict access based on client UID. +#endif + }; + + // Some Standard Modes + enum Mode { + MODE_NONE = MODE_NO_FLAG, + MODE_SERVER = MODE_SERVER_FLAG, + MODE_CLIENT = MODE_CLIENT_FLAG, + // Channels on Windows are named by default and accessible from other + // processes. On POSIX channels are anonymous by default and not accessible + // from other processes. Named channels work via named unix domain sockets. + // On Windows MODE_NAMED_SERVER is equivalent to MODE_SERVER and + // MODE_NAMED_CLIENT is equivalent to MODE_CLIENT. + MODE_NAMED_SERVER = MODE_SERVER_FLAG | MODE_NAMED_FLAG, + MODE_NAMED_CLIENT = MODE_CLIENT_FLAG | MODE_NAMED_FLAG, +#if defined(OS_POSIX) + // An "open" named server accepts connections from ANY client. + // The caller must then implement their own access-control based on the + // client process' user Id. + MODE_OPEN_NAMED_SERVER = MODE_OPEN_ACCESS_FLAG | MODE_SERVER_FLAG | + MODE_NAMED_FLAG +#endif + }; + + enum { + // The maximum message size in bytes. Attempting to receive a + // message of this size or bigger results in a channel error. + kMaximumMessageSize = 128 * 1024 * 1024, + + // Ammount of data to read at once from the pipe. + kReadBufferSize = 4 * 1024 + }; + + // Initialize a Channel. + // + // |channel_handle| identifies the communication Channel. For POSIX, if + // the file descriptor in the channel handle is != -1, the channel takes + // ownership of the file descriptor and will close it appropriately, otherwise + // it will create a new descriptor internally. + // |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 IPC::ChannelHandle &channel_handle, Mode mode, + Listener* listener); + + virtual ~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() WARN_UNUSED_RESULT; + + // Close this Channel explicitly. May be called multiple times. + // On POSIX calling close on an IPC channel that listens for connections will + // cause it to close any accepted connections, and it will stop listening for + // new connections. If you just want to close the currently accepted + // connection and listen for new ones, use ResetToAcceptingConnectionState. + void Close(); + + // Modify the Channel's listener. + void 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. + virtual bool Send(Message* message); + +#if defined(OS_POSIX) && !defined(OS_NACL) + // On POSIX an IPC::Channel wraps a socketpair(), this method returns the + // FD # for the client end of the socket. + // This method may only be called on the server side of a channel. + int GetClientFileDescriptor() const; + + // On POSIX an IPC::Channel can either wrap an established socket, or it + // can wrap a socket that is listening for connections. Currently an + // IPC::Channel that listens for connections can only accept one connection + // at a time. + + // Returns true if the channel supports listening for connections. + bool AcceptsConnections() const; + + // Returns true if the channel supports listening for connections and is + // currently connected. + bool HasAcceptedConnection() const; + + // Returns true if the peer process' effective user id can be determined, in + // which case the supplied client_euid is updated with it. + bool GetClientEuid(uid_t* client_euid) const; + + // Closes any currently connected socket, and returns to a listening state + // for more connections. + void ResetToAcceptingConnectionState(); +#endif // defined(OS_POSIX) && !defined(OS_NACL) + + // Returns true if a named server channel is initialized on the given channel + // ID. Even if true, the server may have already accepted a connection. + static bool IsNamedServerInitialized(const std::string& channel_id); + + protected: + // Used in Chrome by the TestSink to provide a dummy channel implementation + // for testing. TestSink overrides the "interesting" functions in Channel so + // no actual implementation is needed. This will cause un-overridden calls to + // segfault. Do not use outside of test code! + Channel() : channel_impl_(0) { } + + private: + // PIMPL to which all channel calls are delegated. + class ChannelImpl; + ChannelImpl *channel_impl_; + + // 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). + enum { + HELLO_MESSAGE_TYPE = kuint16max // Maximum value of message type (uint16), + // to avoid conflicting with normal + // message types, which are enumeration + // constants starting from 0. + }; +}; + +} // namespace IPC + +#endif // IPC_IPC_CHANNEL_H_ diff --git a/ipc/ipc_channel_handle.h b/ipc/ipc_channel_handle.h new file mode 100644 index 000000000000..ba034cca23b8 --- /dev/null +++ b/ipc/ipc_channel_handle.h @@ -0,0 +1,52 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_IPC_CHANNEL_HANDLE_H_ +#define IPC_IPC_CHANNEL_HANDLE_H_ +#pragma once + +#include <string> + +#include "build/build_config.h" + +#if defined(OS_POSIX) +#include "base/file_descriptor_posix.h" +#endif + +// On Windows, any process can create an IPC channel and others can fetch +// it by name. We pass around the channel names over IPC. +// On POSIX, we instead pass around handles to channel endpoints via IPC. +// When it's time to IPC a new channel endpoint around, we send both the +// channel name as well as a base::FileDescriptor, which is itself a special +// type that knows how to copy a socket endpoint over IPC. +// +// In sum, when passing a handle to a channel over IPC, use this data structure +// to work on both Windows and POSIX. + +namespace IPC { + +struct ChannelHandle { + // Note that serialization for this object is defined in the ParamTraits + // template specialization in ipc_message_utils.h. + ChannelHandle() {} + // The name that is passed in should be an absolute path for Posix. + // Otherwise there may be a problem in IPC communication between + // processes with different working directories. + ChannelHandle(const std::string& n) : name(n) {} + ChannelHandle(const char* n) : name(n) {} +#if defined(OS_POSIX) + ChannelHandle(const std::string& n, const base::FileDescriptor& s) + : name(n), socket(s) {} +#endif // defined(OS_POSIX) + + std::string name; +#if defined(OS_POSIX) + base::FileDescriptor socket; +#endif // defined(OS_POSIX) + +}; + +} // namespace IPC + +#endif // IPC_IPC_CHANNEL_HANDLE_H_ diff --git a/ipc/ipc_channel_posix.cc b/ipc/ipc_channel_posix.cc new file mode 100644 index 000000000000..c358d35c6519 --- /dev/null +++ b/ipc/ipc_channel_posix.cc @@ -0,0 +1,1217 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ipc/ipc_channel_posix.h" + +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> + +#include <string> +#include <map> + +#include "base/command_line.h" +#include "base/eintr_wrapper.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/global_descriptors_posix.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "base/process_util.h" +#include "base/stl_util.h" +#include "base/string_util.h" +#include "base/synchronization/lock.h" +#include "ipc/ipc_descriptors.h" +#include "ipc/ipc_switches.h" +#include "ipc/file_descriptor_set_posix.h" +#include "ipc/ipc_logging.h" +#include "ipc/ipc_message_utils.h" + +namespace IPC { + +// IPC channels on Windows use named pipes (CreateNamedPipe()) with +// channel ids as the pipe names. Channels on POSIX use sockets as +// pipes These don't quite line up. +// +// When creating a child subprocess we use a socket pair and the parent side of +// the fork arranges it such that the initial control channel ends up on the +// magic file descriptor kPrimaryIPCChannel in the child. Future +// connections (file descriptors) can then be passed via that +// connection via sendmsg(). +// +// A POSIX IPC channel can also be set up as a server for a bound UNIX domain +// socket, and will handle multiple connect and disconnect sequences. Currently +// it is limited to one connection at a time. + +//------------------------------------------------------------------------------ +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 (@kPrimaryIPCChannel). 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 (@kPrimaryIPCChannel). 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: + static PipeMap* GetInstance() { + return Singleton<PipeMap>::get(); + } + + ~PipeMap() { + // Shouldn't have left over pipes. + DCHECK(map_.empty()); + } + + // Lookup a given channel id. Return -1 if not found. + int Lookup(const std::string& channel_id) { + base::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 RemoveAndClose(const std::string& channel_id) { + base::AutoLock locked(lock_); + + ChannelToFDMap::iterator i = map_.find(channel_id); + if (i != map_.end()) { + if (HANDLE_EINTR(close(i->second)) < 0) + PLOG(ERROR) << "close " << channel_id; + 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) { + base::AutoLock locked(lock_); + DCHECK_NE(-1, fd); + + ChannelToFDMap::const_iterator i = map_.find(channel_id); + CHECK(i == map_.end()) << "Creating second IPC server (fd " << fd << ") " + << "for '" << channel_id << "' while first " + << "(fd " << i->second << ") still exists"; + map_[channel_id] = fd; + } + + private: + base::Lock lock_; + typedef std::map<std::string, int> ChannelToFDMap; + ChannelToFDMap map_; + + friend struct DefaultSingletonTraits<PipeMap>; +}; + +//------------------------------------------------------------------------------ +// Verify that kMaxPipeNameLength is a decent size. +COMPILE_ASSERT(sizeof(((sockaddr_un*)0)->sun_path) >= kMaxPipeNameLength, + BAD_SUN_PATH_LENGTH); + +// Creates a unix domain socket bound to the specified name that is listening +// for connections. +bool CreateServerUnixDomainSocket(const std::string& pipe_name, + int* server_listen_fd) { + DCHECK(server_listen_fd); + DCHECK_GT(pipe_name.length(), 0u); + DCHECK_LT(pipe_name.length(), kMaxPipeNameLength); + + if (pipe_name.length() == 0 || pipe_name.length() >= kMaxPipeNameLength) { + return false; + } + + // Create socket. + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + return false; + } + + // Make socket non-blocking + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { + PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name; + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + // Delete any old FS instances. + unlink(pipe_name.c_str()); + + // Make sure the path we need exists. + FilePath path(pipe_name); + FilePath dir_path = path.DirName(); + if (!file_util::CreateDirectory(dir_path)) { + return false; + } + + // Create unix_addr structure. + struct sockaddr_un unix_addr; + memset(&unix_addr, 0, sizeof(unix_addr)); + unix_addr.sun_family = AF_UNIX; + int path_len = snprintf(unix_addr.sun_path, IPC::kMaxPipeNameLength, + "%s", pipe_name.c_str()); + DCHECK_EQ(static_cast<int>(pipe_name.length()), path_len); + size_t unix_addr_len = offsetof(struct sockaddr_un, + sun_path) + path_len + 1; + + // Bind the socket. + if (bind(fd, reinterpret_cast<const sockaddr*>(&unix_addr), + unix_addr_len) != 0) { + PLOG(ERROR) << "bind " << pipe_name; + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + // Start listening on the socket. + const int listen_queue_length = 1; + if (listen(fd, listen_queue_length) != 0) { + PLOG(ERROR) << "listen " << pipe_name; + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + *server_listen_fd = fd; + return true; +} + +// Accept a connection on a socket we are listening to. +bool ServerAcceptConnection(int server_listen_fd, int* server_socket) { + DCHECK(server_socket); + + int accept_fd = HANDLE_EINTR(accept(server_listen_fd, NULL, 0)); + if (accept_fd < 0) + return false; + if (fcntl(accept_fd, F_SETFL, O_NONBLOCK) == -1) { + PLOG(ERROR) << "fcntl(O_NONBLOCK) " << accept_fd; + if (HANDLE_EINTR(close(accept_fd)) < 0) + PLOG(ERROR) << "close " << accept_fd; + return false; + } + + *server_socket = accept_fd; + return true; +} + +bool CreateClientUnixDomainSocket(const std::string& pipe_name, + int* client_socket) { + DCHECK(client_socket); + DCHECK_GT(pipe_name.length(), 0u); + DCHECK_LT(pipe_name.length(), kMaxPipeNameLength); + + if (pipe_name.length() == 0 || pipe_name.length() >= kMaxPipeNameLength) { + return false; + } + + // Create socket. + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + PLOG(ERROR) << "socket " << pipe_name; + return false; + } + + // Make socket non-blocking + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { + PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name; + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + // Create server side of socket. + struct sockaddr_un server_unix_addr; + memset(&server_unix_addr, 0, sizeof(server_unix_addr)); + server_unix_addr.sun_family = AF_UNIX; + int path_len = snprintf(server_unix_addr.sun_path, IPC::kMaxPipeNameLength, + "%s", pipe_name.c_str()); + DCHECK_EQ(static_cast<int>(pipe_name.length()), path_len); + size_t server_unix_addr_len = offsetof(struct sockaddr_un, + sun_path) + path_len + 1; + + if (HANDLE_EINTR(connect(fd, reinterpret_cast<sockaddr*>(&server_unix_addr), + server_unix_addr_len)) != 0) { + PLOG(ERROR) << "connect " << pipe_name; + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + *client_socket = fd; + return true; +} + +bool SocketWriteErrorIsRecoverable() { +#if defined(OS_MACOSX) + // 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. + return errno == EAGAIN || errno == EMSGSIZE; +#else + return errno == EAGAIN; +#endif // OS_MACOSX +} + +} // namespace +//------------------------------------------------------------------------------ + +Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle& channel_handle, + Mode mode, Listener* listener) + : mode_(mode), + is_blocked_on_write_(false), + waiting_connect_(true), + message_send_bytes_written_(0), + server_listen_pipe_(-1), + pipe_(-1), + client_pipe_(-1), +#if defined(IPC_USES_READWRITE) + fd_pipe_(-1), + remote_fd_pipe_(-1), +#endif // IPC_USES_READWRITE + pipe_name_(channel_handle.name), + listener_(listener), + must_unlink_(false) { + memset(input_buf_, 0, sizeof(input_buf_)); + memset(input_cmsg_buf_, 0, sizeof(input_cmsg_buf_)); + if (!CreatePipe(channel_handle)) { + // The pipe may have been closed already. + const char *modestr = (mode_ & MODE_SERVER_FLAG) ? "server" : "client"; + // The pipe may have been closed already. + LOG(WARNING) << "Unable to create pipe named \"" << channel_handle.name + << "\" in " << modestr << " mode"; + } +} + +Channel::ChannelImpl::~ChannelImpl() { + Close(); +} + +bool SocketPair(int* fd1, int* fd2) { + int pipe_fds[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) != 0) { + PLOG(ERROR) << "socketpair()"; + 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) { + PLOG(ERROR) << "fcntl(O_NONBLOCK)"; + if (HANDLE_EINTR(close(pipe_fds[0])) < 0) + PLOG(ERROR) << "close"; + if (HANDLE_EINTR(close(pipe_fds[1])) < 0) + PLOG(ERROR) << "close"; + return false; + } + + *fd1 = pipe_fds[0]; + *fd2 = pipe_fds[1]; + + return true; +} + +bool Channel::ChannelImpl::CreatePipe( + const IPC::ChannelHandle& channel_handle) { + DCHECK(server_listen_pipe_ == -1 && pipe_ == -1); + + // Four possible cases: + // 1) It's a channel wrapping a pipe that is given to us. + // 2) It's for a named channel, so we create it. + // 3) It's for a client that we implement ourself. This is used + // in unittesting. + // 4) It's the initial IPC channel: + // 4a) Client side: Pull the pipe out of the GlobalDescriptors set. + // 4b) Server side: create the pipe. + + int local_pipe = -1; + if (channel_handle.socket.fd != -1) { + // Case 1 from comment above. + local_pipe = channel_handle.socket.fd; +#if defined(IPC_USES_READWRITE) + // Test the socket passed into us to make sure it is nonblocking. + // We don't want to call read/write on a blocking socket. + int value = fcntl(local_pipe, F_GETFL); + if (value == -1) { + PLOG(ERROR) << "fcntl(F_GETFL) " << pipe_name_; + return false; + } + if (!(value & O_NONBLOCK)) { + LOG(ERROR) << "Socket " << pipe_name_ << " must be O_NONBLOCK"; + return false; + } +#endif // IPC_USES_READWRITE + } else if (mode_ & MODE_NAMED_FLAG) { + // Case 2 from comment above. + if (mode_ & MODE_SERVER_FLAG) { + if (!CreateServerUnixDomainSocket(pipe_name_, &local_pipe)) { + return false; + } + must_unlink_ = true; + } else if (mode_ & MODE_CLIENT_FLAG) { + if (!CreateClientUnixDomainSocket(pipe_name_, &local_pipe)) { + return false; + } + } else { + LOG(ERROR) << "Bad mode: " << mode_; + return false; + } + } else { + local_pipe = PipeMap::GetInstance()->Lookup(pipe_name_); + if (mode_ & MODE_CLIENT_FLAG) { + if (local_pipe != -1) { + // Case 3 from comment above. + // We only allow one connection. + local_pipe = HANDLE_EINTR(dup(local_pipe)); + PipeMap::GetInstance()->RemoveAndClose(pipe_name_); + } else { + // Case 4a from comment above. + // Guard against inappropriate reuse of the initial IPC channel. If + // an IPC channel closes and someone attempts to reuse it by name, the + // initial channel must not be recycled here. http://crbug.com/26754. + static bool used_initial_channel = false; + if (used_initial_channel) { + LOG(FATAL) << "Denying attempt to reuse initial IPC channel for " + << pipe_name_; + return false; + } + used_initial_channel = true; + + local_pipe = + base::GlobalDescriptors::GetInstance()->Get(kPrimaryIPCChannel); + } + } else if (mode_ & MODE_SERVER_FLAG) { + // Case 4b from comment above. + if (local_pipe != -1) { + LOG(ERROR) << "Server already exists for " << pipe_name_; + return false; + } + if (!SocketPair(&local_pipe, &client_pipe_)) + return false; + PipeMap::GetInstance()->Insert(pipe_name_, client_pipe_); + } else { + LOG(ERROR) << "Bad mode: " << mode_; + return false; + } + } + +#if defined(IPC_USES_READWRITE) + // Create a dedicated socketpair() for exchanging file descriptors. + // See comments for IPC_USES_READWRITE for details. + if (mode_ & MODE_CLIENT_FLAG) { + if (!SocketPair(&fd_pipe_, &remote_fd_pipe_)) { + return false; + } + } +#endif // IPC_USES_READWRITE + + if ((mode_ & MODE_SERVER_FLAG) && (mode_ & MODE_NAMED_FLAG)) { + server_listen_pipe_ = local_pipe; + local_pipe = -1; + } + + pipe_ = local_pipe; + return true; +} + +bool Channel::ChannelImpl::Connect() { + if (server_listen_pipe_ == -1 && pipe_ == -1) { + DLOG(INFO) << "Channel creation failed: " << pipe_name_; + return false; + } + + bool did_connect = true; + if (server_listen_pipe_ != -1) { + // Watch the pipe for connections, and turn any connections into + // active sockets. + MessageLoopForIO::current()->WatchFileDescriptor( + server_listen_pipe_, + true, + MessageLoopForIO::WATCH_READ, + &server_listen_connection_watcher_, + this); + } else { + did_connect = AcceptConnection(); + } + return did_connect; +} + +bool Channel::ChannelImpl::ProcessIncomingMessages() { + ssize_t bytes_read = 0; + + struct msghdr msg = {0}; + struct iovec iov = {input_buf_, Channel::kReadBufferSize}; + + msg.msg_iovlen = 1; + msg.msg_control = input_cmsg_buf_; + + for (;;) { + msg.msg_iov = &iov; + + if (bytes_read == 0) { + if (pipe_ == -1) + return false; + + // Read from pipe. + // recvmsg() returns 0 if the connection has closed or EAGAIN if no data + // is waiting on the pipe. +#if defined(IPC_USES_READWRITE) + if (fd_pipe_ >= 0) { + bytes_read = HANDLE_EINTR(read(pipe_, input_buf_, + Channel::kReadBufferSize)); + msg.msg_controllen = 0; + } else +#endif // IPC_USES_READWRITE + { + msg.msg_controllen = sizeof(input_cmsg_buf_); + bytes_read = HANDLE_EINTR(recvmsg(pipe_, &msg, MSG_DONTWAIT)); + } + if (bytes_read < 0) { + if (errno == EAGAIN) { + return true; +#if defined(OS_MACOSX) + } else if (errno == EPERM) { + // On OSX, reading from a pipe with no listener returns EPERM + // treat this as a special case to prevent spurious error messages + // to the console. + return false; +#endif // OS_MACOSX + } else if (errno == ECONNRESET || errno == EPIPE) { + return false; + } else { + PLOG(ERROR) << "pipe error (" << pipe_ << ")"; + return false; + } + } else if (bytes_read == 0) { + // The pipe has closed... + return false; + } + } + DCHECK(bytes_read); + + if (client_pipe_ != -1) { + PipeMap::GetInstance()->RemoveAndClose(pipe_name_); + 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_EQ(0U, payload_len % sizeof(int)); + wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); + num_wire_fds = payload_len / 4; + + if (msg.msg_flags & MSG_CTRUNC) { + LOG(ERROR) << "SCM_RIGHTS message was truncated" + << " cmsg_len:" << cmsg->cmsg_len + << " fd:" << pipe_; + for (unsigned i = 0; i < num_wire_fds; ++i) + if (HANDLE_EINTR(close(wire_fds[i])) < 0) + PLOG(ERROR) << "close " << i; + return false; + } + break; + } + } + } + + // Process messages from input buffer. + const char *p; + const char *end; + if (input_overflow_buf_.empty()) { + p = input_buf_; + end = p + bytes_read; + } else { + if (input_overflow_buf_.size() > + static_cast<size_t>(kMaximumMessageSize - bytes_read)) { + input_overflow_buf_.clear(); + LOG(ERROR) << "IPC message is too big"; + return false; + } + input_overflow_buf_.append(input_buf_, bytes_read); + p = input_overflow_buf_.data(); + end = p + input_overflow_buf_.size(); + } + + // 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 = NULL; + unsigned num_fds = 0; + 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 { + if (num_wire_fds > 0) { + 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(); + } + + while (p < end) { + const char* message_tail = Message::FindNext(p, end); + if (message_tail) { + int len = static_cast<int>(message_tail - p); + Message m(p, len); + const uint16 header_fds = m.header()->num_fds; + if (header_fds) { + // the message has file descriptors + const char* error = NULL; + if (header_fds > num_fds - fds_i) { + // the message has been completely received, but we didn't get + // enough file descriptors. +#if defined(IPC_USES_READWRITE) + char dummy; + struct iovec fd_pipe_iov = { &dummy, 1 }; + msg.msg_iov = &fd_pipe_iov; + msg.msg_controllen = sizeof(input_cmsg_buf_); + ssize_t n = HANDLE_EINTR(recvmsg(fd_pipe_, &msg, MSG_DONTWAIT)); + if (n == 1 && msg.msg_controllen > 0) { + 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_EQ(0U, payload_len % sizeof(int)); + wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); + num_wire_fds = payload_len / 4; + + if (msg.msg_flags & MSG_CTRUNC) { + LOG(ERROR) << "SCM_RIGHTS message was truncated" + << " cmsg_len:" << cmsg->cmsg_len + << " fd:" << pipe_; + for (unsigned i = 0; i < num_wire_fds; ++i) + if (HANDLE_EINTR(close(wire_fds[i])) < 0) + PLOG(ERROR) << "close " << i; + return false; + } + break; + } + } + if (input_overflow_fds_.empty()) { + fds = wire_fds; + num_fds = num_wire_fds; + } else { + if (num_wire_fds > 0) { + 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(); + } + } + if (header_fds > num_fds - fds_i) +#endif // IPC_USES_READWRITE + error = "Message needs unreceived descriptors"; + } + + if (header_fds > + FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) { + // There are too many descriptors in this message + error = "Message requires an excessive number of descriptors"; + } + + if (error) { + LOG(WARNING) << error + << " channel:" << this + << " message-type:" << m.type() + << " header()->num_fds:" << header_fds + << " num_fds:" << num_fds + << " fds_i:" << fds_i; +#if defined(CHROMIUM_SELINUX) + LOG(WARNING) << "In the case of SELinux this can be caused when " + "using a --user-data-dir to which the default " + "policy doesn't give the renderer access to. "; +#endif // CHROMIUM_SELINUX + // close the existing file descriptors so that we don't leak them + for (unsigned i = fds_i; i < num_fds; ++i) + if (HANDLE_EINTR(close(fds[i])) < 0) + PLOG(ERROR) << "close " << i; + input_overflow_fds_.clear(); + // abort the connection + return false; + } + + m.file_descriptor_set()->SetDescriptors( + &fds[fds_i], header_fds); + fds_i += header_fds; + } + DVLOG(2) << "received message on channel @" << this + << " with type " << m.type() << " on fd " << pipe_; + if (IsHelloMessage(&m)) { + // The Hello message contains only the process id. + void *iter = NULL; + int pid; + if (!m.ReadInt(&iter, &pid)) { + NOTREACHED(); + } +#if defined(IPC_USES_READWRITE) + if (mode_ & MODE_SERVER_FLAG) { + // With IPC_USES_READWRITE, the Hello message from the client to the + // server also contains the fd_pipe_, which will be used for all + // subsequent file descriptor passing. + DCHECK_EQ(m.file_descriptor_set()->size(), 1U); + base::FileDescriptor descriptor; + if (!m.ReadFileDescriptor(&iter, &descriptor)) { + NOTREACHED(); + } + fd_pipe_ = descriptor.fd; + CHECK(descriptor.auto_close); + } +#endif // IPC_USES_READWRITE + listener_->OnChannelConnected(pid); + } else { + listener_->OnMessageReceived(m); + } + p = message_tail; + } else { + // Last message is partial. + break; + } + input_overflow_fds_ = std::vector<int>(&fds[fds_i], &fds[num_fds]); + fds_i = 0; + fds = vector_as_array(&input_overflow_fds_); + num_fds = input_overflow_fds_.size(); + } + input_overflow_buf_.assign(p, end - p); + 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 (input_overflow_buf_.empty() && !input_overflow_fds_.empty()) { + // We close these descriptors in Close() + return false; + } + + bytes_read = 0; // Get more data. + } +} + +bool Channel::ChannelImpl::ProcessOutgoingMessages() { + DCHECK(!waiting_connect_); // Why are we trying to send messages if there's + // no connection? + 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()) { + Message* msg = output_queue_.front(); + + size_t amt_to_write = msg->size() - message_send_bytes_written_; + DCHECK_NE(0U, amt_to_write); + const char* out_bytes = reinterpret_cast<const char*>(msg->data()) + + message_send_bytes_written_; + + struct msghdr msgh = {0}; + struct iovec iov = {const_cast<char*>(out_bytes), amt_to_write}; + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + char buf[CMSG_SPACE( + sizeof(int[FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE]))]; + + ssize_t bytes_written = 1; + int fd_written = -1; +/* + if (message_send_bytes_written_ == 0 && + !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(); + + DCHECK_LE(num_fds, FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE); + if (msg->file_descriptor_set()->ContainsDirectoryDescriptor()) { + LOG(FATAL) << "Panic: attempting to transport directory descriptor over" + " IPC. Aborting to maintain sandbox isolation."; + // If you have hit this then something tried to send a file descriptor + // to a directory over an IPC channel. Since IPC channels span + // sandboxes this is very bad: the receiving process can use openat + // with ".." elements in the path in order to reach the real + // filesystem. + } + + 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; + + // DCHECK_LE above already checks that + // num_fds < MAX_DESCRIPTORS_PER_MESSAGE so no danger of overflow. + msg->header()->num_fds = static_cast<uint16>(num_fds); + +#if defined(IPC_USES_READWRITE) + if (!IsHelloMessage(msg)) { + // Only the Hello message sends the file descriptor with the message. + // Subsequently, we can send file descriptors on the dedicated + // fd_pipe_ which makes Seccomp sandbox operation more efficient. + struct iovec fd_pipe_iov = { const_cast<char *>(""), 1 }; + msgh.msg_iov = &fd_pipe_iov; + fd_written = fd_pipe_; + bytes_written = HANDLE_EINTR(sendmsg(fd_pipe_, &msgh, MSG_DONTWAIT)); + msgh.msg_iov = &iov; + msgh.msg_controllen = 0; + if (bytes_written > 0) { + msg->file_descriptor_set()->CommitAll(); + } + } +#endif // IPC_USES_READWRITE + } + +*/ + if (bytes_written == 1) { + fd_written = pipe_; +#if defined(IPC_USES_READWRITE) + if ((mode_ & MODE_CLIENT_FLAG) && IsHelloMessage(msg)) { + DCHECK_EQ(msg->file_descriptor_set()->size(), 1U); + } + if (!msgh.msg_controllen) { + bytes_written = HANDLE_EINTR(write(pipe_, out_bytes, amt_to_write)); + } else +#endif // IPC_USES_READWRITE + { + bytes_written = HANDLE_EINTR(sendmsg(pipe_, &msgh, MSG_DONTWAIT)); + } + } + if (bytes_written > 0) + msg->file_descriptor_set()->CommitAll(); + + if (bytes_written < 0 && !SocketWriteErrorIsRecoverable()) { +#if defined(OS_MACOSX) + // On OSX writing to a pipe with no listener returns EPERM. + if (errno == EPERM) { + Close(); + return false; + } +#endif // OS_MACOSX + if (errno == EPIPE) { + Close(); + return false; + } + PLOG(ERROR) << "pipe error on " + << fd_written + << " Currently writing message of size: " + << msg->size(); + return false; + } + + if (static_cast<size_t>(bytes_written) != amt_to_write) { + if (bytes_written > 0) { + // If write() fails with EAGAIN then bytes_written will be -1. + message_send_bytes_written_ += 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 { + message_send_bytes_written_ = 0; + + // Message sent OK! + DVLOG(2) << "sent message @" << msg << " on channel @" << this + << " with type " << msg->type() << " on fd " << pipe_; + delete output_queue_.front(); + output_queue_.pop(); + } + } + return true; +} + +bool Channel::ChannelImpl::Send(Message* message) { + DVLOG(2) << "sending message @" << message << " on channel @" << this + << " with type " << message->type() + << " (" << output_queue_.size() << " in queue)"; + +#ifdef IPC_MESSAGE_LOG_ENABLED + Logging::GetInstance()->OnSendMessage(message, ""); +#endif // IPC_MESSAGE_LOG_ENABLED + + output_queue_.push(message); + if (!is_blocked_on_write_ && !waiting_connect_) { + return ProcessOutgoingMessages(); + } + + return true; +} + +int Channel::ChannelImpl::GetClientFileDescriptor() const { + return client_pipe_; +} + +bool Channel::ChannelImpl::AcceptsConnections() const { + return server_listen_pipe_ != -1; +} + +bool Channel::ChannelImpl::HasAcceptedConnection() const { + return AcceptsConnections() && pipe_ != -1; +} + +bool Channel::ChannelImpl::GetClientEuid(uid_t* client_euid) const { + DCHECK(HasAcceptedConnection()); +#if defined(OS_MACOSX) + uid_t peer_euid; + gid_t peer_gid; + if (getpeereid(pipe_, &peer_euid, &peer_gid) != 0) { + PLOG(ERROR) << "getpeereid " << pipe_; + return false; + } + *client_euid = peer_euid; + return true; +#elif defined(OS_SOLARIS) + return false; +#else + struct ucred cred; + socklen_t cred_len = sizeof(cred); + if (getsockopt(pipe_, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) != 0) { + PLOG(ERROR) << "getsockopt " << pipe_; + return false; + } + if (cred_len < sizeof(cred)) { + NOTREACHED() << "Truncated ucred from SO_PEERCRED?"; + return false; + } + *client_euid = cred.uid; + return true; +#endif +} + +void Channel::ChannelImpl::ResetToAcceptingConnectionState() { + // Unregister libevent for the unix domain socket and close it. + read_watcher_.StopWatchingFileDescriptor(); + write_watcher_.StopWatchingFileDescriptor(); + if (pipe_ != -1) { + if (HANDLE_EINTR(close(pipe_)) < 0) + PLOG(ERROR) << "close pipe_ " << pipe_name_; + pipe_ = -1; + } +#if defined(IPC_USES_READWRITE) + if (fd_pipe_ != -1) { + if (HANDLE_EINTR(close(fd_pipe_)) < 0) + PLOG(ERROR) << "close fd_pipe_ " << pipe_name_; + fd_pipe_ = -1; + } + if (remote_fd_pipe_ != -1) { + if (HANDLE_EINTR(close(remote_fd_pipe_)) < 0) + PLOG(ERROR) << "close remote_fd_pipe_ " << pipe_name_; + remote_fd_pipe_ = -1; + } +#endif // IPC_USES_READWRITE + + while (!output_queue_.empty()) { + Message* m = output_queue_.front(); + output_queue_.pop(); + delete m; + } + + // Close any outstanding, received file descriptors. + for (std::vector<int>::iterator + i = input_overflow_fds_.begin(); i != input_overflow_fds_.end(); ++i) { + if (HANDLE_EINTR(close(*i)) < 0) + PLOG(ERROR) << "close"; + } + input_overflow_fds_.clear(); +} + +// static +bool Channel::ChannelImpl::IsNamedServerInitialized( + const std::string& channel_id) { + return file_util::PathExists(FilePath(channel_id)); +} + +// Called by libevent when we can read from the pipe without blocking. +void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) { + bool send_server_hello_msg = false; + if (fd == server_listen_pipe_) { + int new_pipe = 0; + if (!ServerAcceptConnection(server_listen_pipe_, &new_pipe)) { + Close(); + listener_->OnChannelListenError(); + } + + if (pipe_ != -1) { + // We already have a connection. We only handle one at a time. + // close our new descriptor. + if (HANDLE_EINTR(shutdown(new_pipe, SHUT_RDWR)) < 0) + PLOG(ERROR) << "shutdown " << pipe_name_; + if (HANDLE_EINTR(close(new_pipe)) < 0) + PLOG(ERROR) << "close " << pipe_name_; + listener_->OnChannelDenied(); + return; + } + pipe_ = new_pipe; + + if ((mode_ & MODE_OPEN_ACCESS_FLAG) == 0) { + // Verify that the IPC channel peer is running as the same user. + uid_t client_euid; + if (!GetClientEuid(&client_euid)) { + LOG(ERROR) << "Unable to query client euid"; + ResetToAcceptingConnectionState(); + return; + } + if (client_euid != geteuid()) { + LOG(WARNING) << "Client euid is not authorised"; + ResetToAcceptingConnectionState(); + return; + } + } + + if (!AcceptConnection()) { + NOTREACHED() << "AcceptConnection should not fail on server"; + } + send_server_hello_msg = true; + waiting_connect_ = false; + } else if (fd == pipe_) { + if (waiting_connect_ && (mode_ & MODE_SERVER_FLAG)) { + send_server_hello_msg = true; + waiting_connect_ = false; + } + if (!ProcessIncomingMessages()) { + // ClosePipeOnError may delete this object, so we mustn't call + // ProcessOutgoingMessages. + send_server_hello_msg = false; + ClosePipeOnError(); + } + } else { + NOTREACHED() << "Unknown pipe " << fd; + } + + // If we're a server and handshaking, then we want to make sure that we + // only send our handshake message after we've processed the client's. + // This gives us a chance to kill the client if the incoming handshake + // is invalid. + if (send_server_hello_msg) { + ProcessOutgoingMessages(); + } +} + +// Called by libevent when we can write to the pipe without blocking. +void Channel::ChannelImpl::OnFileCanWriteWithoutBlocking(int fd) { + DCHECK_EQ(pipe_, fd); + is_blocked_on_write_ = false; + if (!ProcessOutgoingMessages()) { + ClosePipeOnError(); + } +} + +bool Channel::ChannelImpl::AcceptConnection() { + MessageLoopForIO::current()->WatchFileDescriptor(pipe_, + true, + MessageLoopForIO::WATCH_READ, + &read_watcher_, + this); + QueueHelloMessage(); + + if (mode_ & MODE_CLIENT_FLAG) { + // If we are a client we want to send a hello message out immediately. + // In server mode we will send a hello message when we receive one from a + // client. + waiting_connect_ = false; + return ProcessOutgoingMessages(); + } else if (mode_ & MODE_SERVER_FLAG) { + waiting_connect_ = true; + return true; + } else { + NOTREACHED(); + return false; + } +} + +void Channel::ChannelImpl::ClosePipeOnError() { + if (HasAcceptedConnection()) { + ResetToAcceptingConnectionState(); + listener_->OnChannelError(); + } else { + Close(); + if (AcceptsConnections()) { + listener_->OnChannelListenError(); + } else { + listener_->OnChannelError(); + } + } +} + +void Channel::ChannelImpl::QueueHelloMessage() { + // Create the Hello message + scoped_ptr<Message> msg(new Message(MSG_ROUTING_NONE, + HELLO_MESSAGE_TYPE, + IPC::Message::PRIORITY_NORMAL)); + + if (!msg->WriteInt(base::GetCurrentProcId())) { + NOTREACHED() << "Unable to pickle hello message proc id"; + } +#if defined(IPC_USES_READWRITE) + scoped_ptr<Message> hello; + if (remote_fd_pipe_ != -1) { + if (!msg->WriteFileDescriptor(base::FileDescriptor(remote_fd_pipe_, + false))) { + NOTREACHED() << "Unable to pickle hello message file descriptors"; + } + DCHECK_EQ(msg->file_descriptor_set()->size(), 1U); + } +#endif // IPC_USES_READWRITE + output_queue_.push(msg.release()); +} + +bool Channel::ChannelImpl::IsHelloMessage(const Message* m) const { + return m->routing_id() == MSG_ROUTING_NONE && m->type() == HELLO_MESSAGE_TYPE; +} + +void Channel::ChannelImpl::Close() { + // Close can be called multiple time, so we need to make sure we're + // idempotent. + + ResetToAcceptingConnectionState(); + + if (must_unlink_) { + unlink(pipe_name_.c_str()); + must_unlink_ = false; + } + if (server_listen_pipe_ != -1) { + if (HANDLE_EINTR(close(server_listen_pipe_)) < 0) + PLOG(ERROR) << "close " << server_listen_pipe_; + server_listen_pipe_ = -1; + // Unregister libevent for the listening socket and close it. + server_listen_connection_watcher_.StopWatchingFileDescriptor(); + } + + if (client_pipe_ != -1) { + PipeMap::GetInstance()->RemoveAndClose(pipe_name_); + client_pipe_ = -1; + } +} + +//------------------------------------------------------------------------------ +// Channel's methods simply call through to ChannelImpl. +Channel::Channel(const IPC::ChannelHandle& channel_handle, Mode mode, + Listener* listener) + : channel_impl_(new ChannelImpl(channel_handle, mode, listener)) { +} + +Channel::~Channel() { + delete channel_impl_; +} + +bool Channel::Connect() { + return channel_impl_->Connect(); +} + +void Channel::Close() { + channel_impl_->Close(); +} + +void Channel::set_listener(Listener* listener) { + channel_impl_->set_listener(listener); +} + +bool Channel::Send(Message* message) { + return channel_impl_->Send(message); +} + +int Channel::GetClientFileDescriptor() const { + return channel_impl_->GetClientFileDescriptor(); +} + +bool Channel::AcceptsConnections() const { + return channel_impl_->AcceptsConnections(); +} + +bool Channel::HasAcceptedConnection() const { + return channel_impl_->HasAcceptedConnection(); +} + +bool Channel::GetClientEuid(uid_t* client_euid) const { + return channel_impl_->GetClientEuid(client_euid); +} + +void Channel::ResetToAcceptingConnectionState() { + channel_impl_->ResetToAcceptingConnectionState(); +} + +// static +bool Channel::IsNamedServerInitialized(const std::string& channel_id) { + return ChannelImpl::IsNamedServerInitialized(channel_id); +} + +} // namespace IPC diff --git a/ipc/ipc_channel_posix.h b/ipc/ipc_channel_posix.h new file mode 100644 index 000000000000..b66b1fcaf9b4 --- /dev/null +++ b/ipc/ipc_channel_posix.h @@ -0,0 +1,161 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_IPC_CHANNEL_POSIX_H_ +#define IPC_IPC_CHANNEL_POSIX_H_ +#pragma once + +#include "ipc/ipc_channel.h" + +#include <sys/socket.h> // for CMSG macros + +#include <queue> +#include <string> +#include <vector> + +#include "base/message_loop.h" +#include "ipc/file_descriptor_set_posix.h" + +#if !defined(OS_MACOSX) +// On Linux, the seccomp sandbox makes it very expensive to call +// recvmsg() and sendmsg(). The restriction on calling read() and write(), which +// are cheap, is that we can't pass file descriptors over them. +// +// As we cannot anticipate when the sender will provide us with file +// descriptors, we have to make the decision about whether we call read() or +// recvmsg() before we actually make the call. The easiest option is to +// create a dedicated socketpair() for exchanging file descriptors. Any file +// descriptors are split out of a message, with the non-file-descriptor payload +// going over the normal connection, and the file descriptors being sent +// separately over the other channel. When read()ing from a channel, we'll +// notice if the message was supposed to have come with file descriptors and +// use recvmsg on the other socketpair to retrieve them and combine them +// back with the rest of the message. +// +// Mac can also run in IPC_USES_READWRITE mode if necessary, but at this time +// doesn't take a performance hit from recvmsg and sendmsg, so it doesn't +// make sense to waste resources on having the separate dedicated socketpair. +// It is however useful for debugging between Linux and Mac to be able to turn +// this switch 'on' on the Mac as well. +// +// The HELLO message from the client to the server is always sent using +// sendmsg because it will contain the file descriptor that the server +// needs to send file descriptors in later messages. +#define IPC_USES_READWRITE 1 +#endif + +namespace IPC { + +class Channel::ChannelImpl : public MessageLoopForIO::Watcher { + public: + // Mirror methods of Channel, see ipc_channel.h for description. + ChannelImpl(const IPC::ChannelHandle& channel_handle, Mode mode, + Listener* listener); + virtual ~ChannelImpl(); + bool Connect(); + void Close(); + void set_listener(Listener* listener) { listener_ = listener; } + bool Send(Message* message); + int GetClientFileDescriptor() const; + bool AcceptsConnections() const; + bool HasAcceptedConnection() const; + bool GetClientEuid(uid_t* client_euid) const; + void ResetToAcceptingConnectionState(); + static bool IsNamedServerInitialized(const std::string& channel_id); + + private: + bool CreatePipe(const IPC::ChannelHandle& channel_handle); + + bool ProcessIncomingMessages(); + bool ProcessOutgoingMessages(); + + bool AcceptConnection(); + void ClosePipeOnError(); + void QueueHelloMessage(); + bool IsHelloMessage(const Message* m) const; + + // MessageLoopForIO::Watcher implementation. + virtual void OnFileCanReadWithoutBlocking(int fd); + virtual void OnFileCanWriteWithoutBlocking(int fd); + + 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_; + bool waiting_connect_; + + // If sending a message blocks then we use this variable + // to keep track of where we are. + size_t message_send_bytes_written_; + + // File descriptor we're listening on for new connections if we listen + // for connections. + int server_listen_pipe_; + + // The pipe used for communication. + int pipe_; + + // For a server, the client end of our socketpair() -- the other end of our + // pipe_ that is passed to the client. + int client_pipe_; + +#if defined(IPC_USES_READWRITE) + // Linux/BSD use a dedicated socketpair() for passing file descriptors. + int fd_pipe_; + int remote_fd_pipe_; +#endif + + // 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]; + + enum { + // We assume a worst case: kReadBufferSize bytes of messages, where each + // message has no payload and a full complement of descriptors. + MAX_READ_FDS = (Channel::kReadBufferSize / sizeof(IPC::Message::Header)) * + FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE, + }; + + // This is a control message buffer large enough to hold kMaxReadFDs +#if defined(OS_MACOSX) + // TODO(agl): OSX appears to have non-constant CMSG macros! + char input_cmsg_buf_[1024]; +#else + char input_cmsg_buf_[CMSG_SPACE(sizeof(int) * MAX_READ_FDS)]; +#endif + + // Large messages that span multiple pipe buffers, get built-up using + // this buffer. + std::string input_overflow_buf_; + std::vector<int> input_overflow_fds_; + + // True if we are responsible for unlinking the unix domain socket file. + bool must_unlink_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ChannelImpl); +}; + +// The maximum length of the name of a pipe for MODE_NAMED_SERVER or +// MODE_NAMED_CLIENT if you want to pass in your own socket. +// The standard size on linux is 108, mac is 104. To maintain consistency +// across platforms we standardize on the smaller value. +static const size_t kMaxPipeNameLength = 104; + +} // namespace IPC + +#endif // IPC_IPC_CHANNEL_POSIX_H_ diff --git a/ipc/ipc_channel_posix_unittest.cc b/ipc/ipc_channel_posix_unittest.cc new file mode 100644 index 000000000000..14ecc6083e54 --- /dev/null +++ b/ipc/ipc_channel_posix_unittest.cc @@ -0,0 +1,389 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// These tests are POSIX only. + +#include "ipc/ipc_channel_posix.h" + +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> + +#include "base/basictypes.h" +#include "base/eintr_wrapper.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/test/multiprocess_test.h" +#include "base/test/test_timeouts.h" +#include "testing/multiprocess_func_list.h" + +namespace { + +enum { + QUIT_MESSAGE = 47 +}; + +class IPCChannelPosixTestListener : public IPC::Channel::Listener { + public: + enum STATUS { + DISCONNECTED, + MESSAGE_RECEIVED, + CHANNEL_ERROR, + CONNECTED, + DENIED, + LISTEN_ERROR + }; + + IPCChannelPosixTestListener(bool quit_only_on_message) + : status_(DISCONNECTED), quit_only_on_message_(quit_only_on_message) {} + + virtual ~IPCChannelPosixTestListener() {} + + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { + EXPECT_EQ(message.type(), QUIT_MESSAGE); + status_ = MESSAGE_RECEIVED; + QuitRunLoop(); + return true; + } + + virtual void OnChannelConnected(int32 peer_pid) OVERRIDE { + status_ = CONNECTED; + if (!quit_only_on_message_) { + QuitRunLoop(); + } + } + + virtual void OnChannelError() OVERRIDE { + status_ = CHANNEL_ERROR; + if (!quit_only_on_message_) { + QuitRunLoop(); + } + } + + virtual void OnChannelDenied() OVERRIDE { + status_ = DENIED; + if (!quit_only_on_message_) { + QuitRunLoop(); + } + } + + virtual void OnChannelListenError() OVERRIDE { + status_ = LISTEN_ERROR; + if (!quit_only_on_message_) { + QuitRunLoop(); + } + } + + STATUS status() { return status_; } + + void QuitRunLoop() { + MessageLoopForIO::current()->QuitNow(); + } + + private: + // The current status of the listener. + STATUS status_; + // If |quit_only_on_message_| then the listener will only break out of + // the run loop when the QUIT_MESSAGE is received. + bool quit_only_on_message_; +}; + +} // namespace + +class IPCChannelPosixTest : public base::MultiProcessTest { + public: + static const char kConnectionSocketTestName[]; + static void SetUpSocket(IPC::ChannelHandle *handle, + IPC::Channel::Mode mode); + static void SpinRunLoop(int milliseconds); + + protected: + virtual void SetUp(); + virtual void TearDown(); + +private: + scoped_ptr<MessageLoopForIO> message_loop_; +}; + +const char IPCChannelPosixTest::kConnectionSocketTestName[] = + "/var/tmp/chrome_IPCChannelPosixTest__ConnectionSocket"; + +void IPCChannelPosixTest::SetUp() { + MultiProcessTest::SetUp(); + // Construct a fresh IO Message loop for the duration of each test. + message_loop_.reset(new MessageLoopForIO()); +} + +void IPCChannelPosixTest::TearDown() { + message_loop_.reset(NULL); + MultiProcessTest::TearDown(); +} + +// Create up a socket and bind and listen to it, or connect it +// depending on the |mode|. +void IPCChannelPosixTest::SetUpSocket(IPC::ChannelHandle *handle, + IPC::Channel::Mode mode) { + const std::string& name = handle->name; + + int socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); + ASSERT_GE(socket_fd, 0) << name; + ASSERT_GE(fcntl(socket_fd, F_SETFL, O_NONBLOCK), 0); + struct sockaddr_un server_address = { 0 }; + memset(&server_address, 0, sizeof(server_address)); + server_address.sun_family = AF_UNIX; + int path_len = snprintf(server_address.sun_path, IPC::kMaxPipeNameLength, + "%s", name.c_str()); + DCHECK_EQ(static_cast<int>(name.length()), path_len); + size_t server_address_len = offsetof(struct sockaddr_un, + sun_path) + path_len + 1; + + if (mode == IPC::Channel::MODE_NAMED_SERVER) { + // Only one server at a time. Cleanup garbage if it exists. + unlink(name.c_str()); + // Make sure the path we need exists. + FilePath path(name); + FilePath dir_path = path.DirName(); + ASSERT_TRUE(file_util::CreateDirectory(dir_path)); + ASSERT_GE(bind(socket_fd, + reinterpret_cast<struct sockaddr *>(&server_address), + server_address_len), 0) << server_address.sun_path + << ": " << strerror(errno) + << "(" << errno << ")"; + ASSERT_GE(listen(socket_fd, SOMAXCONN), 0) << server_address.sun_path + << ": " << strerror(errno) + << "(" << errno << ")"; + } else if (mode == IPC::Channel::MODE_NAMED_CLIENT) { + ASSERT_GE(connect(socket_fd, + reinterpret_cast<struct sockaddr *>(&server_address), + server_address_len), 0) << server_address.sun_path + << ": " << strerror(errno) + << "(" << errno << ")"; + } else { + FAIL() << "Unknown mode " << mode; + } + handle->socket.fd = socket_fd; +} + +void IPCChannelPosixTest::SpinRunLoop(int milliseconds) { + MessageLoopForIO *loop = MessageLoopForIO::current(); + // Post a quit task so that this loop eventually ends and we don't hang + // in the case of a bad test. Usually, the run loop will quit sooner than + // that because all tests use a IPCChannelPosixTestListener which quits the + // current run loop on any channel activity. + loop->PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask(), milliseconds); + loop->Run(); +} + +TEST_F(IPCChannelPosixTest, BasicListen) { + // Test creating a socket that is listening. + IPC::ChannelHandle handle("/var/tmp/IPCChannelPosixTest_BasicListen"); + SetUpSocket(&handle, IPC::Channel::MODE_NAMED_SERVER); + unlink(handle.name.c_str()); + IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_SERVER, NULL); + ASSERT_TRUE(channel.Connect()); + ASSERT_TRUE(channel.AcceptsConnections()); + ASSERT_FALSE(channel.HasAcceptedConnection()); + channel.ResetToAcceptingConnectionState(); + ASSERT_FALSE(channel.HasAcceptedConnection()); +} + +TEST_F(IPCChannelPosixTest, BasicConnected) { + // Test creating a socket that is connected. + int pipe_fds[2]; + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds)); + std::string socket_name("/var/tmp/IPCChannelPosixTest_BasicConnected"); + ASSERT_GE(fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK), 0); + + base::FileDescriptor fd(pipe_fds[0], false); + IPC::ChannelHandle handle(socket_name, fd); + IPC::Channel channel(handle, IPC::Channel::MODE_SERVER, NULL); + ASSERT_TRUE(channel.Connect()); + ASSERT_FALSE(channel.AcceptsConnections()); + channel.Close(); + ASSERT_TRUE(HANDLE_EINTR(close(pipe_fds[1])) == 0); + + // Make sure that we can use the socket that is created for us by + // a standard channel. + IPC::Channel channel2(socket_name, IPC::Channel::MODE_SERVER, NULL); + ASSERT_TRUE(channel2.Connect()); + ASSERT_FALSE(channel2.AcceptsConnections()); +} + +TEST_F(IPCChannelPosixTest, AdvancedConnected) { + // Test creating a connection to an external process. + IPCChannelPosixTestListener listener(false); + IPC::ChannelHandle chan_handle(kConnectionSocketTestName); + SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER); + IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener); + ASSERT_TRUE(channel.Connect()); + ASSERT_TRUE(channel.AcceptsConnections()); + ASSERT_FALSE(channel.HasAcceptedConnection()); + + base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc", + false); + ASSERT_TRUE(handle); + SpinRunLoop(TestTimeouts::action_max_timeout_ms()); + ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status()); + ASSERT_TRUE(channel.HasAcceptedConnection()); + IPC::Message* message = new IPC::Message(0, // routing_id + QUIT_MESSAGE, // message type + IPC::Message::PRIORITY_NORMAL); + channel.Send(message); + SpinRunLoop(TestTimeouts::action_timeout_ms()); + int exit_code = 0; + EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code)); + EXPECT_EQ(0, exit_code); + ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status()); + ASSERT_FALSE(channel.HasAcceptedConnection()); +} + +TEST_F(IPCChannelPosixTest, ResetState) { + // Test creating a connection to an external process. Close the connection, + // but continue to listen and make sure another external process can connect + // to us. + IPCChannelPosixTestListener listener(false); + IPC::ChannelHandle chan_handle(kConnectionSocketTestName); + SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER); + IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener); + ASSERT_TRUE(channel.Connect()); + ASSERT_TRUE(channel.AcceptsConnections()); + ASSERT_FALSE(channel.HasAcceptedConnection()); + + base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc", + false); + ASSERT_TRUE(handle); + SpinRunLoop(TestTimeouts::action_max_timeout_ms()); + ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status()); + ASSERT_TRUE(channel.HasAcceptedConnection()); + channel.ResetToAcceptingConnectionState(); + ASSERT_FALSE(channel.HasAcceptedConnection()); + + base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixTestConnectionProc", + false); + ASSERT_TRUE(handle2); + SpinRunLoop(TestTimeouts::action_max_timeout_ms()); + ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status()); + ASSERT_TRUE(channel.HasAcceptedConnection()); + IPC::Message* message = new IPC::Message(0, // routing_id + QUIT_MESSAGE, // message type + IPC::Message::PRIORITY_NORMAL); + channel.Send(message); + SpinRunLoop(TestTimeouts::action_timeout_ms()); + EXPECT_TRUE(base::KillProcess(handle, 0, false)); + int exit_code = 0; + EXPECT_TRUE(base::WaitForExitCode(handle2, &exit_code)); + EXPECT_EQ(0, exit_code); + ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status()); + ASSERT_FALSE(channel.HasAcceptedConnection()); +} + +TEST_F(IPCChannelPosixTest, MultiConnection) { + // Test setting up a connection to an external process, and then have + // another external process attempt to connect to us. + IPCChannelPosixTestListener listener(false); + IPC::ChannelHandle chan_handle(kConnectionSocketTestName); + SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER); + IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener); + ASSERT_TRUE(channel.Connect()); + ASSERT_TRUE(channel.AcceptsConnections()); + ASSERT_FALSE(channel.HasAcceptedConnection()); + + base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc", + false); + ASSERT_TRUE(handle); + SpinRunLoop(TestTimeouts::action_max_timeout_ms()); + ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status()); + ASSERT_TRUE(channel.HasAcceptedConnection()); + base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixFailConnectionProc", + false); + ASSERT_TRUE(handle2); + SpinRunLoop(TestTimeouts::action_max_timeout_ms()); + int exit_code = 0; + EXPECT_TRUE(base::WaitForExitCode(handle2, &exit_code)); + EXPECT_EQ(exit_code, 0); + ASSERT_EQ(IPCChannelPosixTestListener::DENIED, listener.status()); + ASSERT_TRUE(channel.HasAcceptedConnection()); + IPC::Message* message = new IPC::Message(0, // routing_id + QUIT_MESSAGE, // message type + IPC::Message::PRIORITY_NORMAL); + channel.Send(message); + SpinRunLoop(TestTimeouts::action_timeout_ms()); + EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code)); + EXPECT_EQ(exit_code, 0); + ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status()); + ASSERT_FALSE(channel.HasAcceptedConnection()); +} + +TEST_F(IPCChannelPosixTest, DoubleServer) { + // Test setting up two servers with the same name. + IPCChannelPosixTestListener listener(false); + IPCChannelPosixTestListener listener2(false); + IPC::ChannelHandle chan_handle(kConnectionSocketTestName); + IPC::Channel channel(chan_handle, IPC::Channel::MODE_SERVER, &listener); + IPC::Channel channel2(chan_handle, IPC::Channel::MODE_SERVER, &listener2); + ASSERT_TRUE(channel.Connect()); + ASSERT_FALSE(channel2.Connect()); +} + +TEST_F(IPCChannelPosixTest, BadMode) { + // Test setting up two servers with a bad mode. + IPCChannelPosixTestListener listener(false); + IPC::ChannelHandle chan_handle(kConnectionSocketTestName); + IPC::Channel channel(chan_handle, IPC::Channel::MODE_NONE, &listener); + ASSERT_FALSE(channel.Connect()); +} + +TEST_F(IPCChannelPosixTest, IsNamedServerInitialized) { + IPCChannelPosixTestListener listener(false); + IPC::ChannelHandle chan_handle(kConnectionSocketTestName); + ASSERT_TRUE(file_util::Delete(FilePath(kConnectionSocketTestName), false)); + ASSERT_FALSE(IPC::Channel::IsNamedServerInitialized( + kConnectionSocketTestName)); + IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener); + ASSERT_TRUE(IPC::Channel::IsNamedServerInitialized( + kConnectionSocketTestName)); + channel.Close(); + ASSERT_FALSE(IPC::Channel::IsNamedServerInitialized( + kConnectionSocketTestName)); +} + +// A long running process that connects to us +MULTIPROCESS_TEST_MAIN(IPCChannelPosixTestConnectionProc) { + MessageLoopForIO message_loop; + IPCChannelPosixTestListener listener(true); + IPC::ChannelHandle handle(IPCChannelPosixTest::kConnectionSocketTestName); + IPCChannelPosixTest::SetUpSocket(&handle, IPC::Channel::MODE_NAMED_CLIENT); + IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_CLIENT, &listener); + EXPECT_TRUE(channel.Connect()); + IPCChannelPosixTest::SpinRunLoop(TestTimeouts::action_max_timeout_ms()); + EXPECT_EQ(IPCChannelPosixTestListener::MESSAGE_RECEIVED, listener.status()); + return 0; +} + +// Simple external process that shouldn't be able to connect to us. +MULTIPROCESS_TEST_MAIN(IPCChannelPosixFailConnectionProc) { + MessageLoopForIO message_loop; + IPCChannelPosixTestListener listener(false); + IPC::ChannelHandle handle(IPCChannelPosixTest::kConnectionSocketTestName); + IPCChannelPosixTest::SetUpSocket(&handle, IPC::Channel::MODE_NAMED_CLIENT); + IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_CLIENT, &listener); + + // In this case connect may succeed or fail depending on if the packet + // actually gets sent at sendmsg. Since we never delay on send, we may not + // see the error. However even if connect succeeds, eventually we will get an + // error back since the channel will be closed when we attempt to read from + // it. + bool connected = channel.Connect(); + if (connected) { + IPCChannelPosixTest::SpinRunLoop(TestTimeouts::action_max_timeout_ms()); + EXPECT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status()); + } else { + EXPECT_EQ(IPCChannelPosixTestListener::DISCONNECTED, listener.status()); + } + return 0; +} + diff --git a/ipc/ipc_channel_proxy.cc b/ipc/ipc_channel_proxy.cc new file mode 100644 index 000000000000..dc990c26184a --- /dev/null +++ b/ipc/ipc_channel_proxy.cc @@ -0,0 +1,397 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "ipc/ipc_channel_proxy.h" +#include "ipc/ipc_logging.h" +#include "ipc/ipc_message_utils.h" + +namespace IPC { + +//------------------------------------------------------------------------------ + +// This task ensures the message is deleted if the task is deleted without +// having been run. +class SendTask : public Task { + public: + SendTask(ChannelProxy::Context* context, Message* message) + : context_(context), + message_(message) { + } + + virtual void Run() { + context_->OnSendMessage(message_.release()); + } + + private: + scoped_refptr<ChannelProxy::Context> context_; + scoped_ptr<Message> message_; + + DISALLOW_COPY_AND_ASSIGN(SendTask); +}; + +//------------------------------------------------------------------------------ + +ChannelProxy::MessageFilter::MessageFilter() {} + +ChannelProxy::MessageFilter::~MessageFilter() {} + +void ChannelProxy::MessageFilter::OnFilterAdded(Channel* channel) {} + +void ChannelProxy::MessageFilter::OnFilterRemoved() {} + +void ChannelProxy::MessageFilter::OnChannelConnected(int32 peer_pid) {} + +void ChannelProxy::MessageFilter::OnChannelError() {} + +void ChannelProxy::MessageFilter::OnChannelClosing() {} + +bool ChannelProxy::MessageFilter::OnMessageReceived(const Message& message) { + return false; +} + +void ChannelProxy::MessageFilter::OnDestruct() const { + delete this; +} + +//------------------------------------------------------------------------------ + +ChannelProxy::Context::Context(Channel::Listener* listener, + base::MessageLoopProxy* ipc_message_loop) + : listener_message_loop_(base::MessageLoopProxy::current()), + listener_(listener), + ipc_message_loop_(ipc_message_loop), + peer_pid_(0), + channel_connected_called_(false) { +} + +ChannelProxy::Context::~Context() { +} + +void ChannelProxy::Context::CreateChannel(const IPC::ChannelHandle& handle, + const Channel::Mode& mode) { + DCHECK(channel_.get() == NULL); + channel_id_ = handle.name; + channel_.reset(new Channel(handle, mode, this)); +} + +bool ChannelProxy::Context::TryFilters(const Message& message) { +#ifdef IPC_MESSAGE_LOG_ENABLED + Logging* logger = Logging::GetInstance(); + if (logger->Enabled()) + logger->OnPreDispatchMessage(message); +#endif + + for (size_t i = 0; i < filters_.size(); ++i) { + if (filters_[i]->OnMessageReceived(message)) { +#ifdef IPC_MESSAGE_LOG_ENABLED + if (logger->Enabled()) + logger->OnPostDispatchMessage(message, channel_id_); +#endif + return true; + } + } + return false; +} + +// Called on the IPC::Channel thread +bool ChannelProxy::Context::OnMessageReceived(const Message& message) { + // First give a chance to the filters to process this message. + if (!TryFilters(message)) + OnMessageReceivedNoFilter(message); + return true; +} + +// Called on the IPC::Channel thread +bool ChannelProxy::Context::OnMessageReceivedNoFilter(const Message& message) { + // NOTE: This code relies on the listener's message loop not going away while + // this thread is active. That should be a reasonable assumption, but it + // feels risky. We may want to invent some more indirect way of referring to + // a MessageLoop if this becomes a problem. + listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &Context::OnDispatchMessage, message)); + return true; +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnChannelConnected(int32 peer_pid) { + // Add any pending filters. This avoids a race condition where someone + // creates a ChannelProxy, calls AddFilter, and then right after starts the + // peer process. The IO thread could receive a message before the task to add + // the filter is run on the IO thread. + OnAddFilter(); + + peer_pid_ = peer_pid; + for (size_t i = 0; i < filters_.size(); ++i) + filters_[i]->OnChannelConnected(peer_pid); + + // See above comment about using listener_message_loop_ here. + listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &Context::OnDispatchConnected)); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnChannelError() { + for (size_t i = 0; i < filters_.size(); ++i) + filters_[i]->OnChannelError(); + + // See above comment about using listener_message_loop_ here. + listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &Context::OnDispatchError)); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnChannelOpened() { + DCHECK(channel_ != NULL); + + // Assume a reference to ourselves on behalf of this thread. This reference + // will be released when we are closed. + AddRef(); + + if (!channel_->Connect()) { + OnChannelError(); + return; + } + + for (size_t i = 0; i < filters_.size(); ++i) + filters_[i]->OnFilterAdded(channel_.get()); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnChannelClosed() { + // It's okay for IPC::ChannelProxy::Close to be called more than once, which + // would result in this branch being taken. + if (!channel_.get()) + return; + + for (size_t i = 0; i < filters_.size(); ++i) { + filters_[i]->OnChannelClosing(); + filters_[i]->OnFilterRemoved(); + } + + // We don't need the filters anymore. + filters_.clear(); + + channel_.reset(); + + // Balance with the reference taken during startup. This may result in + // self-destruction. + Release(); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnSendMessage(Message* message) { + if (!channel_.get()) { + delete message; + OnChannelClosed(); + return; + } + if (!channel_->Send(message)) + OnChannelError(); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnAddFilter() { + std::vector<scoped_refptr<MessageFilter> > new_filters; + { + base::AutoLock auto_lock(pending_filters_lock_); + new_filters.swap(pending_filters_); + } + + for (size_t i = 0; i < new_filters.size(); ++i) { + filters_.push_back(new_filters[i]); + + // If the channel has already been created, then we need to send this + // message so that the filter gets access to the Channel. + if (channel_.get()) + new_filters[i]->OnFilterAdded(channel_.get()); + // Ditto for if the channel has been connected. + if (peer_pid_) + new_filters[i]->OnChannelConnected(peer_pid_); + } +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnRemoveFilter(MessageFilter* filter) { + for (size_t i = 0; i < filters_.size(); ++i) { + if (filters_[i].get() == filter) { + filter->OnFilterRemoved(); + filters_.erase(filters_.begin() + i); + return; + } + } + + NOTREACHED() << "filter to be removed not found"; +} + +// Called on the listener's thread +void ChannelProxy::Context::AddFilter(MessageFilter* filter) { + base::AutoLock auto_lock(pending_filters_lock_); + pending_filters_.push_back(make_scoped_refptr(filter)); + ipc_message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &Context::OnAddFilter)); +} + +// Called on the listener's thread +void ChannelProxy::Context::OnDispatchMessage(const Message& message) { + if (!listener_) + return; + + OnDispatchConnected(); + +#ifdef IPC_MESSAGE_LOG_ENABLED + Logging* logger = Logging::GetInstance(); + if (message.type() == IPC_LOGGING_ID) { + logger->OnReceivedLoggingMessage(message); + return; + } + + if (logger->Enabled()) + logger->OnPreDispatchMessage(message); +#endif + + listener_->OnMessageReceived(message); + +#ifdef IPC_MESSAGE_LOG_ENABLED + if (logger->Enabled()) + logger->OnPostDispatchMessage(message, channel_id_); +#endif +} + +// Called on the listener's thread +void ChannelProxy::Context::OnDispatchConnected() { + if (channel_connected_called_) + return; + + channel_connected_called_ = true; + if (listener_) + listener_->OnChannelConnected(peer_pid_); +} + +// Called on the listener's thread +void ChannelProxy::Context::OnDispatchError() { + if (listener_) + listener_->OnChannelError(); +} + +//----------------------------------------------------------------------------- + +ChannelProxy::ChannelProxy(const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + Channel::Listener* listener, + base::MessageLoopProxy* ipc_thread) + : context_(new Context(listener, ipc_thread)), + outgoing_message_filter_(NULL) { + Init(channel_handle, mode, ipc_thread, true); +} + +ChannelProxy::ChannelProxy(const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + base::MessageLoopProxy* ipc_thread, + Context* context, + bool create_pipe_now) + : context_(context), + outgoing_message_filter_(NULL) { + Init(channel_handle, mode, ipc_thread, create_pipe_now); +} + +ChannelProxy::~ChannelProxy() { + Close(); +} + +void ChannelProxy::Init(const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + base::MessageLoopProxy* ipc_thread_loop, + bool create_pipe_now) { +#if defined(OS_POSIX) + // When we are creating a server on POSIX, we need its file descriptor + // to be created immediately so that it can be accessed and passed + // to other processes. Forcing it to be created immediately avoids + // race conditions that may otherwise arise. + if (mode & Channel::MODE_SERVER_FLAG) { + create_pipe_now = true; + } +#endif // defined(OS_POSIX) + + if (create_pipe_now) { + // Create the channel immediately. This effectively sets up the + // low-level pipe so that the client can connect. Without creating + // the pipe immediately, it is possible for a listener to attempt + // to connect and get an error since the pipe doesn't exist yet. + context_->CreateChannel(channel_handle, mode); + } else { + context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + context_.get(), &Context::CreateChannel, channel_handle, mode)); + } + + // complete initialization on the background thread + context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + context_.get(), &Context::OnChannelOpened)); +} + +void ChannelProxy::Close() { + // Clear the backpointer to the listener so that any pending calls to + // Context::OnDispatchMessage or OnDispatchError will be ignored. It is + // possible that the channel could be closed while it is receiving messages! + context_->Clear(); + + if (context_->ipc_message_loop()) { + context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + context_.get(), &Context::OnChannelClosed)); + } +} + +bool ChannelProxy::Send(Message* message) { + if (outgoing_message_filter()) + message = outgoing_message_filter()->Rewrite(message); + +#ifdef IPC_MESSAGE_LOG_ENABLED + Logging::GetInstance()->OnSendMessage(message, context_->channel_id()); +#endif + + context_->ipc_message_loop()->PostTask(FROM_HERE, + new SendTask(context_.get(), message)); + return true; +} + +void ChannelProxy::AddFilter(MessageFilter* filter) { + context_->AddFilter(filter); +} + +void ChannelProxy::RemoveFilter(MessageFilter* filter) { + context_->ipc_message_loop()->PostTask( + FROM_HERE, NewRunnableMethod( + context_.get(), + &Context::OnRemoveFilter, + make_scoped_refptr(filter))); +} + +void ChannelProxy::ClearIPCMessageLoop() { + context()->ClearIPCMessageLoop(); +} + +#if defined(OS_POSIX) && !defined(OS_NACL) +// See the TODO regarding lazy initialization of the channel in +// ChannelProxy::Init(). +// We assume that IPC::Channel::GetClientFileDescriptorMapping() is thread-safe. +int ChannelProxy::GetClientFileDescriptor() const { + Channel *channel = context_.get()->channel_.get(); + // Channel must have been created first. + DCHECK(channel) << context_.get()->channel_id_; + return channel->GetClientFileDescriptor(); +} + +bool ChannelProxy::GetClientEuid(uid_t* client_euid) const { + Channel *channel = context_.get()->channel_.get(); + // Channel must have been created first. + DCHECK(channel) << context_.get()->channel_id_; + return channel->GetClientEuid(client_euid); +} +#endif + +//----------------------------------------------------------------------------- + +} // namespace IPC diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h new file mode 100644 index 000000000000..792e3d6a506e --- /dev/null +++ b/ipc/ipc_channel_proxy.h @@ -0,0 +1,272 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_IPC_CHANNEL_PROXY_H_ +#define IPC_IPC_CHANNEL_PROXY_H_ +#pragma once + +#include <vector> + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop_proxy.h" +#include "base/synchronization/lock.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_channel_handle.h" + +namespace IPC { + +class SendTask; + +//----------------------------------------------------------------------------- +// IPC::ChannelProxy +// +// This class is a helper class that is useful when you wish to run an IPC +// channel on a background thread. It provides you with the option of either +// handling IPC messages on that background thread or having them dispatched to +// your main thread (the thread on which the IPC::ChannelProxy is created). +// +// The API for an IPC::ChannelProxy is very similar to that of an IPC::Channel. +// When you send a message to an IPC::ChannelProxy, the message is routed to +// the background thread, where it is then passed to the IPC::Channel's Send +// method. This means that you can send a message from your thread and your +// message will be sent over the IPC channel when possible instead of being +// delayed until your thread returns to its message loop. (Often IPC messages +// will queue up on the IPC::Channel when there is a lot of traffic, and the +// channel will not get cycles to flush its message queue until the thread, on +// which it is running, returns to its message loop.) +// +// An IPC::ChannelProxy can have a MessageFilter associated with it, which will +// be notified of incoming messages on the IPC::Channel's thread. This gives +// the consumer of IPC::ChannelProxy the ability to respond to incoming +// messages on this background thread instead of on their own thread, which may +// be bogged down with other processing. The result can be greatly improved +// latency for messages that can be handled on a background thread. +// +// The consumer of IPC::ChannelProxy is responsible for allocating the Thread +// instance where the IPC::Channel will be created and operated. +// +class IPC_EXPORT ChannelProxy : public Message::Sender { + public: + + struct MessageFilterTraits; + + // A class that receives messages on the thread where the IPC channel is + // running. It can choose to prevent the default action for an IPC message. + class IPC_EXPORT MessageFilter + : public base::RefCountedThreadSafe<MessageFilter, MessageFilterTraits> { + public: + MessageFilter(); + virtual ~MessageFilter(); + + // Called on the background thread to provide the filter with access to the + // channel. Called when the IPC channel is initialized or when AddFilter + // is called if the channel is already initialized. + virtual void OnFilterAdded(Channel* channel); + + // Called on the background thread when the filter has been removed from + // the ChannelProxy and when the Channel is closing. After a filter is + // removed, it will not be called again. + virtual void OnFilterRemoved(); + + // Called to inform the filter that the IPC channel is connected and we + // have received the internal Hello message from the peer. + virtual void OnChannelConnected(int32 peer_pid); + + // Called when there is an error on the channel, typically that the channel + // has been closed. + virtual void OnChannelError(); + + // Called to inform the filter that the IPC channel will be destroyed. + // OnFilterRemoved is called immediately after this. + virtual void OnChannelClosing(); + + // Return true to indicate that the message was handled, or false to let + // the message be handled in the default way. + virtual bool OnMessageReceived(const Message& message); + + // Called when the message filter is about to be deleted. This gives + // derived classes the option of controlling which thread they're deleted + // on etc. + virtual void OnDestruct() const; + }; + + struct MessageFilterTraits { + static void Destruct(const MessageFilter* filter) { + filter->OnDestruct(); + } + }; + + // Interface for a filter to be imposed on outgoing messages which can + // re-write the message. Used mainly for testing. + class OutgoingMessageFilter { + public: + // Returns a re-written message, freeing the original, or simply the + // original unchanged if no rewrite indicated. + virtual Message *Rewrite(Message *message) = 0; + }; + + // Initializes a channel proxy. The channel_handle and mode parameters are + // passed directly to the underlying IPC::Channel. The listener is called on + // the thread that creates the ChannelProxy. The filter's OnMessageReceived + // method is called on the thread where the IPC::Channel is running. The + // filter may be null if the consumer is not interested in handling messages + // on the background thread. Any message not handled by the filter will be + // dispatched to the listener. The given message loop indicates where the + // IPC::Channel should be created. + ChannelProxy(const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + Channel::Listener* listener, + base::MessageLoopProxy* ipc_thread_loop); + + virtual ~ChannelProxy(); + + // Close the IPC::Channel. This operation completes asynchronously, once the + // background thread processes the command to close the channel. It is ok to + // call this method multiple times. Redundant calls are ignored. + // + // WARNING: The MessageFilter object held by the ChannelProxy is also + // released asynchronously, and it may in fact have its final reference + // released on the background thread. The caller should be careful to deal + // with / allow for this possibility. + void Close(); + + // Send a message asynchronously. The message is routed to the background + // thread where it is passed to the IPC::Channel's Send method. + virtual bool Send(Message* message); + + // Used to intercept messages as they are received on the background thread. + // + // Ordinarily, messages sent to the ChannelProxy are routed to the matching + // listener on the worker thread. This API allows code to intercept messages + // before they are sent to the worker thread. + // If you call this before the target process is launched, then you're + // guaranteed to not miss any messages. But if you call this anytime after, + // then some messages might be missed since the filter is added internally on + // the IO thread. + void AddFilter(MessageFilter* filter); + void RemoveFilter(MessageFilter* filter); + + void set_outgoing_message_filter(OutgoingMessageFilter* filter) { + outgoing_message_filter_ = filter; + } + + // Called to clear the pointer to the IPC message loop when it's going away. + void ClearIPCMessageLoop(); + +#if defined(OS_POSIX) + // Calls through to the underlying channel's methods. + int GetClientFileDescriptor() const; + bool GetClientEuid(uid_t* client_euid) const; +#endif // defined(OS_POSIX) + + protected: + class Context; + // A subclass uses this constructor if it needs to add more information + // to the internal state. If create_pipe_now is true, the pipe is created + // immediately. Otherwise it's created on the IO thread. + ChannelProxy(const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + base::MessageLoopProxy* ipc_thread_loop, + Context* context, + bool create_pipe_now); + + // Used internally to hold state that is referenced on the IPC thread. + class Context : public base::RefCountedThreadSafe<Context>, + public Channel::Listener { + public: + Context(Channel::Listener* listener, base::MessageLoopProxy* ipc_thread); + void ClearIPCMessageLoop() { ipc_message_loop_ = NULL; } + base::MessageLoopProxy* ipc_message_loop() const { + return ipc_message_loop_.get(); + } + const std::string& channel_id() const { return channel_id_; } + + // Dispatches a message on the listener thread. + void OnDispatchMessage(const Message& message); + + protected: + friend class base::RefCountedThreadSafe<Context>; + virtual ~Context(); + + // IPC::Channel::Listener methods: + virtual bool OnMessageReceived(const Message& message) OVERRIDE; + virtual void OnChannelConnected(int32 peer_pid) OVERRIDE; + virtual void OnChannelError() OVERRIDE; + + // Like OnMessageReceived but doesn't try the filters. + bool OnMessageReceivedNoFilter(const Message& message); + + // Gives the filters a chance at processing |message|. + // Returns true if the message was processed, false otherwise. + bool TryFilters(const Message& message); + + // Like Open and Close, but called on the IPC thread. + virtual void OnChannelOpened(); + virtual void OnChannelClosed(); + + // Called on the consumers thread when the ChannelProxy is closed. At that + // point the consumer is telling us that they don't want to receive any + // more messages, so we honor that wish by forgetting them! + virtual void Clear() { listener_ = NULL; } + + private: + friend class ChannelProxy; + friend class SendTask; + + // Create the Channel + void CreateChannel(const IPC::ChannelHandle& channel_handle, + const Channel::Mode& mode); + + // Methods called on the IO thread. + void OnSendMessage(Message* message_ptr); + void OnAddFilter(); + void OnRemoveFilter(MessageFilter* filter); + + // Methods called on the listener thread. + void AddFilter(MessageFilter* filter); + void OnDispatchConnected(); + void OnDispatchError(); + + scoped_refptr<base::MessageLoopProxy> listener_message_loop_; + Channel::Listener* listener_; + + // List of filters. This is only accessed on the IPC thread. + std::vector<scoped_refptr<MessageFilter> > filters_; + scoped_refptr<base::MessageLoopProxy> ipc_message_loop_; + scoped_ptr<Channel> channel_; + std::string channel_id_; + int peer_pid_; + bool channel_connected_called_; + + // Holds filters between the AddFilter call on the listerner thread and the + // IPC thread when they're added to filters_. + std::vector<scoped_refptr<MessageFilter> > pending_filters_; + // Lock for pending_filters_. + base::Lock pending_filters_lock_; + }; + + Context* context() { return context_; } + + OutgoingMessageFilter* outgoing_message_filter() { + return outgoing_message_filter_; + } + + private: + friend class SendTask; + + void Init(const IPC::ChannelHandle& channel_handle, Channel::Mode mode, + base::MessageLoopProxy* ipc_thread_loop, bool create_pipe_now); + + // By maintaining this indirection (ref-counted) to our internal state, we + // can safely be destroyed while the background thread continues to do stuff + // that involves this data. + scoped_refptr<Context> context_; + + OutgoingMessageFilter* outgoing_message_filter_; +}; + +} // namespace IPC + +#endif // IPC_IPC_CHANNEL_PROXY_H_ diff --git a/ipc/ipc_channel_win.cc b/ipc/ipc_channel_win.cc new file mode 100644 index 000000000000..3a85a1651fb4 --- /dev/null +++ b/ipc/ipc_channel_win.cc @@ -0,0 +1,430 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ipc/ipc_channel_win.h" + +#include <windows.h> + +#include "base/auto_reset.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/threading/non_thread_safe.h" +#include "base/utf_string_conversions.h" +#include "base/win/scoped_handle.h" +#include "ipc/ipc_logging.h" +#include "ipc/ipc_message_utils.h" + +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 IPC::ChannelHandle &channel_handle, + Mode mode, Listener* listener) + : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)), + ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)), + pipe_(INVALID_HANDLE_VALUE), + listener_(listener), + waiting_connect_(mode & MODE_SERVER_FLAG), + processing_incoming_(false), + ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) { + CreatePipe(channel_handle, mode); +} + +Channel::ChannelImpl::~ChannelImpl() { + Close(); +} + +void Channel::ChannelImpl::Close() { + if (thread_check_.get()) { + DCHECK(thread_check_->CalledOnValidThread()); + } + + if (input_state_.is_pending || output_state_.is_pending) + CancelIo(pipe_); + + // 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; + } + + // Make sure all IO has completed. + base::Time start = base::Time::Now(); + while (input_state_.is_pending || output_state_.is_pending) { + MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this); + } + + while (!output_queue_.empty()) { + Message* m = output_queue_.front(); + output_queue_.pop(); + delete m; + } +} + +bool Channel::ChannelImpl::Send(Message* message) { + DCHECK(thread_check_->CalledOnValidThread()); + DVLOG(2) << "sending message @" << message << " on channel @" << this + << " with type " << message->type() + << " (" << output_queue_.size() << " in queue)"; + +#ifdef IPC_MESSAGE_LOG_ENABLED + Logging::GetInstance()->OnSendMessage(message, ""); +#endif + + output_queue_.push(message); + // ensure waiting to write + if (!waiting_connect_) { + if (!output_state_.is_pending) { + if (!ProcessOutgoingMessages(NULL, 0)) + return false; + } + } + + return true; +} + +// static +bool Channel::ChannelImpl::IsNamedServerInitialized( + const std::string& channel_id) { + if (WaitNamedPipe(PipeName(channel_id).c_str(), 1)) + return true; + // If ERROR_SEM_TIMEOUT occurred, the pipe exists but is handling another + // connection. + return GetLastError() == ERROR_SEM_TIMEOUT; +} + +// static +const std::wstring Channel::ChannelImpl::PipeName( + const std::string& channel_id) { + std::string name("\\\\.\\pipe\\chrome."); + return ASCIIToWide(name.append(channel_id)); +} + +bool Channel::ChannelImpl::CreatePipe(const IPC::ChannelHandle &channel_handle, + Mode mode) { + DCHECK_EQ(INVALID_HANDLE_VALUE, pipe_); + const std::wstring pipe_name = PipeName(channel_handle.name); + if (mode & MODE_SERVER_FLAG) { + pipe_ = CreateNamedPipeW(pipe_name.c_str(), + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | + FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, + 1, + Channel::kReadBufferSize, + Channel::kReadBufferSize, + 5000, + NULL); + } else if (mode & MODE_CLIENT_FLAG) { + pipe_ = CreateFileW(pipe_name.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION | + FILE_FLAG_OVERLAPPED, + NULL); + } else { + NOTREACHED(); + } + if (pipe_ == INVALID_HANDLE_VALUE) { + // If this process is being closed, the pipe may be gone already. + LOG(WARNING) << "Unable to create pipe \"" << pipe_name << + "\" in " << (mode == 0 ? "server" : "client") + << " mode. Error :" << GetLastError(); + return false; + } + + // Create the Hello message to be sent when Connect is called + scoped_ptr<Message> m(new Message(MSG_ROUTING_NONE, + HELLO_MESSAGE_TYPE, + IPC::Message::PRIORITY_NORMAL)); + if (!m->WriteInt(GetCurrentProcessId())) { + CloseHandle(pipe_); + pipe_ = INVALID_HANDLE_VALUE; + return false; + } + + output_queue_.push(m.release()); + return true; +} + +bool Channel::ChannelImpl::Connect() { + DLOG_IF(WARNING, thread_check_.get()) << "Connect called more than once"; + + if (!thread_check_.get()) + thread_check_.reset(new base::NonThreadSafe()); + + 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(FROM_HERE, factory_.NewRunnableMethod( + &Channel::ChannelImpl::OnIOCompleted, &input_state_.context, 0, 0)); + } + + if (!waiting_connect_) + ProcessOutgoingMessages(NULL, 0); + return true; +} + +bool Channel::ChannelImpl::ProcessConnection() { + DCHECK(thread_check_->CalledOnValidThread()); + 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; + case ERROR_NO_DATA: + // The pipe is being closed. + return false; + default: + NOTREACHED(); + return false; + } + + return true; +} + +bool Channel::ChannelImpl::ProcessIncomingMessages( + MessageLoopForIO::IOContext* context, + DWORD bytes_read) { + DCHECK(thread_check_->CalledOnValidThread()); + 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_, + Channel::kReadBufferSize, + &bytes_read, + &input_state_.context.overlapped); + if (!ok) { + DWORD err = GetLastError(); + if (err == ERROR_IO_PENDING) { + input_state_.is_pending = true; + return true; + } + 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, *end; + if (input_overflow_buf_.empty()) { + p = input_buf_; + end = p + bytes_read; + } else { + if (input_overflow_buf_.size() > (kMaximumMessageSize - bytes_read)) { + input_overflow_buf_.clear(); + LOG(ERROR) << "IPC message is too big"; + return false; + } + input_overflow_buf_.append(input_buf_, bytes_read); + p = input_overflow_buf_.data(); + end = p + input_overflow_buf_.size(); + } + + while (p < end) { + const char* message_tail = Message::FindNext(p, end); + if (message_tail) { + int len = static_cast<int>(message_tail - p); + const Message m(p, len); + DVLOG(2) << "received message on channel @" << this + << " with type " << m.type(); + 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()); + } else { + listener_->OnMessageReceived(m); + } + p = message_tail; + } else { + // Last message is partial. + break; + } + } + input_overflow_buf_.assign(p, end - p); + + 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? + DCHECK(thread_check_->CalledOnValidThread()); + + if (output_state_.is_pending) { + DCHECK(context); + output_state_.is_pending = false; + if (!context || bytes_written == 0) { + DWORD err = GetLastError(); + LOG(ERROR) << "pipe error: " << err; + return false; + } + // Message was sent. + DCHECK(!output_queue_.empty()); + Message* m = output_queue_.front(); + output_queue_.pop(); + delete m; + } + + if (output_queue_.empty()) + return true; + + if (INVALID_HANDLE_VALUE == pipe_) + return false; + + // Write to pipe... + Message* m = output_queue_.front(); + DCHECK(m->size() <= INT_MAX); + BOOL ok = WriteFile(pipe_, + m->data(), + static_cast<int>(m->size()), + &bytes_written, + &output_state_.context.overlapped); + if (!ok) { + DWORD err = GetLastError(); + if (err == ERROR_IO_PENDING) { + output_state_.is_pending = true; + + DVLOG(2) << "sent pending message @" << m << " on channel @" << this + << " with type " << m->type(); + + return true; + } + LOG(ERROR) << "pipe error: " << err; + return false; + } + + DVLOG(2) << "sent message @" << m << " on channel @" << this + << " with type " << m->type(); + + output_state_.is_pending = true; + return true; +} + +void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext* context, + DWORD bytes_transfered, DWORD error) { + bool ok; + DCHECK(thread_check_->CalledOnValidThread()); + 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_); + AutoReset<bool> auto_reset_processing_incoming(&processing_incoming_, true); + ok = ProcessIncomingMessages(context, bytes_transfered); + } 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(); + } +} + +//------------------------------------------------------------------------------ +// Channel's methods simply call through to ChannelImpl. +Channel::Channel(const IPC::ChannelHandle &channel_handle, Mode mode, + Listener* listener) + : channel_impl_(new ChannelImpl(channel_handle, mode, listener)) { +} + +Channel::~Channel() { + delete channel_impl_; +} + +bool Channel::Connect() { + return channel_impl_->Connect(); +} + +void Channel::Close() { + channel_impl_->Close(); +} + +void Channel::set_listener(Listener* listener) { + channel_impl_->set_listener(listener); +} + +bool Channel::Send(Message* message) { + return channel_impl_->Send(message); +} + +// static +bool Channel::IsNamedServerInitialized(const std::string& channel_id) { + return ChannelImpl::IsNamedServerInitialized(channel_id); +} + +} // namespace IPC diff --git a/ipc/ipc_channel_win.h b/ipc/ipc_channel_win.h new file mode 100644 index 000000000000..ada88ac3c0e4 --- /dev/null +++ b/ipc/ipc_channel_win.h @@ -0,0 +1,91 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_IPC_CHANNEL_WIN_H_ +#define IPC_IPC_CHANNEL_WIN_H_ +#pragma once + +#include "ipc/ipc_channel.h" + +#include <queue> +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" + +namespace base { +class NonThreadSafe; +} + +namespace IPC { + +class Channel::ChannelImpl : public MessageLoopForIO::IOHandler { + public: + // Mirror methods of Channel, see ipc_channel.h for description. + ChannelImpl(const IPC::ChannelHandle &channel_handle, Mode mode, + Listener* listener); + ~ChannelImpl(); + bool Connect(); + void Close(); + void set_listener(Listener* listener) { listener_ = listener; } + bool Send(Message* message); + static bool IsNamedServerInitialized(const std::string& channel_id); + private: + static const std::wstring PipeName(const std::string& channel_id); + bool CreatePipe(const IPC::ChannelHandle &channel_handle, Mode mode); + + 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_; + + // We read from the pipe into this buffer + char input_buf_[Channel::kReadBufferSize]; + + // Large messages that span multiple pipe buffers, get built-up using + // this buffer. + std::string input_overflow_buf_; + + // 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_; + + ScopedRunnableMethodFactory<ChannelImpl> factory_; + + scoped_ptr<base::NonThreadSafe> thread_check_; + + DISALLOW_COPY_AND_ASSIGN(ChannelImpl); +}; + +} // namespace IPC + +#endif // IPC_IPC_CHANNEL_WIN_H_ diff --git a/ipc/ipc_descriptors.h b/ipc/ipc_descriptors.h new file mode 100644 index 000000000000..4750fa8e7633 --- /dev/null +++ b/ipc/ipc_descriptors.h @@ -0,0 +1,15 @@ +// 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 IPC_IPC_DESCRIPTORS_H_ +#define IPC_IPC_DESCRIPTORS_H_ +#pragma once + +// This is a list of global descriptor keys to be used with the +// base::GlobalDescriptors object (see base/global_descriptors_posix.h) +enum { + kPrimaryIPCChannel = 0, +}; + +#endif // IPC_IPC_DESCRIPTORS_H_ diff --git a/ipc/ipc_export.h b/ipc/ipc_export.h new file mode 100644 index 000000000000..dc38fffa204b --- /dev/null +++ b/ipc/ipc_export.h @@ -0,0 +1,29 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_IPC_OSP_EXPORT_ +#define IPC_IPC_OSP_EXPORT_ +#pragma once + +// Defines IPC_EXPORT so that functionality implemented by the IPC module can be +// exported to consumers. + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(IPC_IMPLEMENTATION) +#define IPC_EXPORT __declspec(dllexport) +#else +#define IPC_EXPORT __declspec(dllimport) +#endif // defined(IPC_IMPLEMENTATION) + +#else // defined(WIN32) +#define IPC_EXPORT __attribute__((visibility("default"))) +#endif + +#else // defined(COMPONENT_BUILD) +#define IPC_EXPORT +#endif + +#endif // IPC_IPC_OSP_EXPORT_ diff --git a/ipc/ipc_logging.cc b/ipc/ipc_logging.cc new file mode 100644 index 000000000000..138e6acce735 --- /dev/null +++ b/ipc/ipc_logging.cc @@ -0,0 +1,251 @@ +// Copyright (c) 2010 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 "ipc/ipc_logging.h" + +#ifdef IPC_MESSAGE_LOG_ENABLED +#define IPC_MESSAGE_MACROS_LOG_ENABLED +#endif + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/process_util.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/threading/thread.h" +#include "base/time.h" +#include "ipc/ipc_switches.h" +#include "ipc/ipc_sync_message.h" +#include "ipc/ipc_message_utils.h" + +#if defined(OS_POSIX) +#include <unistd.h> +#endif + +#ifdef IPC_MESSAGE_LOG_ENABLED + +using base::Time; + +// IPC::Logging is allocated as a singleton, so we don't need any kind of +// special retention program. +DISABLE_RUNNABLE_METHOD_REFCOUNT(IPC::Logging); + +namespace IPC { + +const int kLogSendDelayMs = 100; + +// We use a pointer to the function table to avoid any linker dependencies on +// all the traits used as IPC message parameters. +LogFunctionMap* Logging::log_function_map_; + +Logging::Logging() + : enabled_(false), + enabled_on_stderr_(false), + queue_invoke_later_pending_(false), + sender_(NULL), + main_thread_(MessageLoop::current()), + consumer_(NULL) { +#if defined(OS_WIN) + // getenv triggers an unsafe warning. Simply check how big of a buffer + // would be needed to fetch the value to see if the enviornment variable is + // set. + size_t requiredSize = 0; + getenv_s(&requiredSize, NULL, 0, "CHROME_IPC_LOGGING"); + bool logging_env_var_set = (requiredSize != 0); +#else // !defined(OS_WIN) + bool logging_env_var_set = (getenv("CHROME_IPC_LOGGING") != NULL); +#endif //defined(OS_WIN) + if (logging_env_var_set) { + enabled_ = true; + enabled_on_stderr_ = true; + } +} + +Logging::~Logging() { +} + +Logging* Logging::GetInstance() { + return Singleton<Logging>::get(); +} + +void Logging::SetConsumer(Consumer* consumer) { + consumer_ = consumer; +} + +void Logging::Enable() { + enabled_ = true; +} + +void Logging::Disable() { + enabled_ = false; +} + +void Logging::OnSendLogs() { + queue_invoke_later_pending_ = false; + if (!sender_) + return; + + Message* msg = new Message( + MSG_ROUTING_CONTROL, IPC_LOGGING_ID, Message::PRIORITY_NORMAL); + WriteParam(msg, queued_logs_); + queued_logs_.clear(); + sender_->Send(msg); +} + +void Logging::SetIPCSender(IPC::Message::Sender* sender) { + sender_ = sender; +} + +void Logging::OnReceivedLoggingMessage(const Message& message) { + std::vector<LogData> data; + void* iter = NULL; + if (!ReadParam(&message, &iter, &data)) + return; + + for (size_t i = 0; i < data.size(); ++i) { + Log(data[i]); + } +} + +void Logging::OnSendMessage(Message* message, const std::string& channel_id) { + if (!Enabled()) + return; + + if (message->is_reply()) { + LogData* data = message->sync_log_data(); + if (!data) + return; + + // This is actually the delayed reply to a sync message. Create a string + // of the output parameters, add it to the LogData that was earlier stashed + // with the reply, and log the result. + data->channel = channel_id; + GenerateLogData("", *message, data); + Log(*data); + delete data; + message->set_sync_log_data(NULL); + } else { + // If the time has already been set (i.e. by ChannelProxy), keep that time + // instead as it's more accurate. + if (!message->sent_time()) + message->set_sent_time(Time::Now().ToInternalValue()); + } +} + +void Logging::OnPreDispatchMessage(const Message& message) { + message.set_received_time(Time::Now().ToInternalValue()); +} + +void Logging::OnPostDispatchMessage(const Message& message, + const std::string& channel_id) { + if (!Enabled() || + !message.sent_time() || + !message.received_time() || + message.dont_log()) + return; + + LogData data; + GenerateLogData(channel_id, message, &data); + + if (MessageLoop::current() == main_thread_) { + Log(data); + } else { + main_thread_->PostTask(FROM_HERE, NewRunnableMethod( + this, &Logging::Log, data)); + } +} + +void Logging::GetMessageText(uint32 type, std::string* name, + const Message* message, + std::string* params) { + if (!log_function_map_) + return; + + LogFunctionMap::iterator it = log_function_map_->find(type); + if (it == log_function_map_->end()) { + if (name) { + *name = "[UNKNOWN MSG "; + *name += base::IntToString(type); + *name += " ]"; + } + return; + } + + (*it->second)(name, message, params); +} + +void Logging::Log(const LogData& data) { + if (consumer_) { + // We're in the browser process. + consumer_->Log(data); + } else { + // We're in the renderer or plugin processes. + if (sender_) { + queued_logs_.push_back(data); + if (!queue_invoke_later_pending_) { + queue_invoke_later_pending_ = true; + MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod( + this, &Logging::OnSendLogs), kLogSendDelayMs); + } + } + } + if (enabled_on_stderr_) { + std::string message_name; + if (data.message_name.empty()) { + message_name = StringPrintf("[unknown type %d]", data.type); + } else { + message_name = data.message_name; + } + fprintf(stderr, "ipc %s %d %s %s %s\n", + data.channel.c_str(), + data.routing_id, + data.flags.c_str(), + message_name.c_str(), + data.params.c_str()); + } +} + +void GenerateLogData(const std::string& channel, const Message& message, + LogData* data) { + if (message.is_reply()) { + // "data" should already be filled in. + std::string params; + Logging::GetMessageText(data->type, NULL, &message, ¶ms); + + if (!data->params.empty() && !params.empty()) + data->params += ", "; + + data->flags += " DR"; + + data->params += params; + } else { + std::string flags; + if (message.is_sync()) + flags = "S"; + + if (message.is_reply()) + flags += "R"; + + if (message.is_reply_error()) + flags += "E"; + + std::string params, message_name; + Logging::GetMessageText(message.type(), &message_name, &message, ¶ms); + + data->channel = channel; + data->routing_id = message.routing_id(); + data->type = message.type(); + data->flags = flags; + data->sent = message.sent_time(); + data->receive = message.received_time(); + data->dispatch = Time::Now().ToInternalValue(); + data->params = params; + data->message_name = message_name; + } +} + +} + +#endif // IPC_MESSAGE_LOG_ENABLED diff --git a/ipc/ipc_logging.h b/ipc/ipc_logging.h new file mode 100644 index 000000000000..42237c7ec61e --- /dev/null +++ b/ipc/ipc_logging.h @@ -0,0 +1,112 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_IPC_LOGGING_H_ +#define IPC_IPC_LOGGING_H_ +#pragma once + +#include "ipc/ipc_message.h" // For IPC_MESSAGE_LOG_ENABLED. + +#ifdef IPC_MESSAGE_LOG_ENABLED + +#include <vector> + +#include "base/hash_tables.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "base/message_loop.h" +#include "ipc/ipc_export.h" + +// Logging function. |name| is a string in ASCII and |params| is a string in +// UTF-8. +typedef void (*LogFunction)(std::string* name, + const IPC::Message* msg, + std::string* params); + +typedef base::hash_map<uint32, LogFunction > LogFunctionMap; + +namespace IPC { + +class Message; + +// One instance per process. Needs to be created on the main thread (the UI +// thread in the browser) but OnPreDispatchMessage/OnPostDispatchMessage +// can be called on other threads. +class IPC_EXPORT Logging { + public: + // Implemented by consumers of log messages. + class Consumer { + public: + virtual void Log(const LogData& data) = 0; + + protected: + virtual ~Consumer() {} + }; + + void SetConsumer(Consumer* consumer); + + ~Logging(); + static Logging* GetInstance(); + + // Enable and Disable are NOT cross-process; they only affect the + // current thread/process. If you want to modify the value for all + // processes, perhaps your intent is to call + // g_browser_process->SetIPCLoggingEnabled(). + void Enable(); + void Disable(); + bool Enabled() const { return enabled_; } + + // Called by child processes to give the logger object the channel to send + // logging data to the browser process. + void SetIPCSender(Message::Sender* sender); + + // Called in the browser process when logging data from a child process is + // received. + void OnReceivedLoggingMessage(const Message& message); + + void OnSendMessage(Message* message, const std::string& channel_id); + void OnPreDispatchMessage(const Message& message); + void OnPostDispatchMessage(const Message& message, + const std::string& channel_id); + + // Like the *MsgLog functions declared for each message class, except this + // calls the correct one based on the message type automatically. Defined in + // ipc_logging.cc. + static void GetMessageText(uint32 type, std::string* name, + const Message* message, std::string* params); + + static void set_log_function_map(LogFunctionMap* functions) { + log_function_map_ = functions; + } + + static LogFunctionMap* log_function_map() { + return log_function_map_; + } + + private: + friend struct DefaultSingletonTraits<Logging>; + Logging(); + + void OnSendLogs(); + void Log(const LogData& data); + + bool enabled_; + bool enabled_on_stderr_; // only used on POSIX for now + + std::vector<LogData> queued_logs_; + bool queue_invoke_later_pending_; + + Message::Sender* sender_; + MessageLoop* main_thread_; + + Consumer* consumer_; + + static LogFunctionMap* log_function_map_; +}; + +} // namespace IPC + +#endif // IPC_MESSAGE_LOG_ENABLED + +#endif // IPC_IPC_LOGGING_H_ diff --git a/ipc/ipc_message.cc b/ipc/ipc_message.cc new file mode 100644 index 000000000000..235a7d592eca --- /dev/null +++ b/ipc/ipc_message.cc @@ -0,0 +1,128 @@ +// 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 "ipc/ipc_message.h" + +//#include "base/logging.h" +#include "log.h" +#include "build/build_config.h" + +#if defined(OS_POSIX) +//#include "ipc/file_descriptor_set_posix.h" +#endif + +namespace IPC { + +//------------------------------------------------------------------------------ + +Message::~Message() { +} + +Message::Message() + : Pickle(sizeof(Header)) { + header()->routing = header()->type = header()->flags = 0; +#if defined(OS_POSIX) + header()->num_fds = 0; + header()->pad = 0; +#endif + InitLoggingVariables(); +} + +Message::Message(int32 routing_id, uint32 type, PriorityValue priority) + : Pickle(sizeof(Header)) { + header()->routing = routing_id; + header()->type = type; + header()->flags = priority; +#if defined(OS_POSIX) + header()->num_fds = 0; + header()->pad = 0; +#endif + InitLoggingVariables(); +} + +Message::Message(const char* data, int data_len) : Pickle(data, data_len) { + InitLoggingVariables(); +} + +Message::Message(const Message& other) : Pickle(other) { + InitLoggingVariables(); +#if defined(OS_POSIX) +// file_descriptor_set_ = other.file_descriptor_set_; +#endif +} + +void Message::InitLoggingVariables() { +#ifdef IPC_MESSAGE_LOG_ENABLED + received_time_ = 0; + dont_log_ = false; + log_data_ = NULL; +#endif +} + +Message& Message::operator=(const Message& other) { + *static_cast<Pickle*>(this) = other; +#if defined(OS_POSIX) +// file_descriptor_set_ = other.file_descriptor_set_; +#endif + return *this; +} + +#ifdef IPC_MESSAGE_LOG_ENABLED +void Message::set_sent_time(int64 time) { + DCHECK((header()->flags & HAS_SENT_TIME_BIT) == 0); + header()->flags |= HAS_SENT_TIME_BIT; + WriteInt64(time); +} + +int64 Message::sent_time() const { + if ((header()->flags & HAS_SENT_TIME_BIT) == 0) + return 0; + + const char* data = end_of_payload(); + data -= sizeof(int64); + return *(reinterpret_cast<const int64*>(data)); +} + +void Message::set_received_time(int64 time) const { + received_time_ = time; +} +#endif + +#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. + 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(void** 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 = true; + + return descriptor->fd >= 0; +} + +void Message::EnsureFileDescriptorSet() { + if (file_descriptor_set_.get() == NULL) + file_descriptor_set_ = new FileDescriptorSet; +} +*/ +#endif + +} // namespace IPC diff --git a/ipc/ipc_message.h b/ipc/ipc_message.h new file mode 100644 index 000000000000..11ed17c4672c --- /dev/null +++ b/ipc/ipc_message.h @@ -0,0 +1,284 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_IPC_MESSAGE_H_ +#define IPC_IPC_MESSAGE_H_ +#pragma once + +#include <string> + +#include "base/basictypes.h" +#include "base/pickle.h" +#include "ipc/ipc_export.h" + +#ifndef NDEBUG +//#define IPC_MESSAGE_LOG_ENABLED +#endif + +#if defined(OS_POSIX) +#include "base/memory/ref_counted.h" +#endif + +namespace base { +struct FileDescriptor; +} + +class FileDescriptorSet; + +namespace IPC { + +//------------------------------------------------------------------------------ + +class Channel; +class Message; +struct LogData; + +class IPC_EXPORT Message : public Pickle { + public: + // Implemented by objects that can send IPC messages across a channel. + class IPC_EXPORT Sender { + public: + virtual ~Sender() {} + + // Sends the given IPC message. The implementor takes ownership of the + // given Message regardless of whether or not this method succeeds. This + // is done to make this method easier to use. Returns true on success and + // false otherwise. + virtual bool Send(Message* msg) = 0; + }; + + enum PriorityValue { + PRIORITY_LOW = 1, + PRIORITY_NORMAL, + PRIORITY_HIGH + }; + + virtual ~Message(); + + Message(); + + // Initialize a message with a user-defined type, priority value, and + // destination WebView ID. + Message(int32 routing_id, uint32 type, PriorityValue priority); + + // Initializes a message from a const block of data. The data is not copied; + // instead the data is merely referenced by this message. Only const methods + // should be used on the message when initialized this way. + Message(const char* data, int data_len); + + Message(const Message& other); + Message& operator=(const Message& other); + + PriorityValue priority() const { + return static_cast<PriorityValue>(header()->flags & PRIORITY_MASK); + } + + // True if this is a synchronous message. + bool is_sync() const { + return (header()->flags & SYNC_BIT) != 0; + } + + // 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; + } + + // Normally when a receiver gets a message and they're blocked on a + // synchronous message Send, they buffer a message. Setting this flag causes + // the receiver to be unblocked and the message to be dispatched immediately. + void set_unblock(bool unblock) { + if (unblock) { + header()->flags |= UNBLOCK_BIT; + } else { + header()->flags &= ~UNBLOCK_BIT; + } + } + + bool should_unblock() const { + return (header()->flags & UNBLOCK_BIT) != 0; + } + + // Tells the receiver that the caller is pumping messages while waiting + // for the result. + bool is_caller_pumping_messages() const { + return (header()->flags & PUMPING_MSGS_BIT) != 0; + } + + uint32 type() const { + return header()->type; + } + + int32 routing_id() const { + return header()->routing; + } + + void set_routing_id(int32 new_id) { + header()->routing = new_id; + } + + template<class T, class S> + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)()) { + (obj->*func)(); + return true; + } + + template<class T, class S> + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)() const) { + (obj->*func)(); + return true; + } + + template<class T, class S> + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)(const Message&)) { + (obj->*func)(*msg); + return true; + } + + template<class T, class S> + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)(const Message&) const) { + (obj->*func)(*msg); + return true; + } + + // Used for async messages with no parameters. + static void Log(std::string* name, const Message* msg, std::string* l) { + } + + // Find the end of the message data that starts at range_start. Returns NULL + // if the entire message is not found in the given data range. + static const char* FindNext(const char* range_start, const char* range_end) { + return Pickle::FindNext(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(void** iter, base::FileDescriptor* descriptor) const; +#endif + +#ifdef IPC_MESSAGE_LOG_ENABLED + // Adds the outgoing time from Time::Now() at the end of the message and sets + // a bit to indicate that it's been added. + void set_sent_time(int64 time); + int64 sent_time() const; + + void set_received_time(int64 time) const; + int64 received_time() const { return received_time_; } + void set_output_params(const std::string& op) const { output_params_ = op; } + const std::string& output_params() const { return output_params_; } + // The following four functions are needed so we can log sync messages with + // delayed replies. We stick the log data from the sent message into the + // reply message, so that when it's sent and we have the output parameters + // we can log it. As such, we set a flag on the sent message to not log it. + void set_sync_log_data(LogData* data) const { log_data_ = data; } + LogData* sync_log_data() const { return log_data_; } + void set_dont_log() const { dont_log_ = true; } + bool dont_log() const { return dont_log_; } +#endif + + protected: + friend class Channel; + friend class MessageReplyDeserializer; + friend class SyncMessage; + + void set_sync() { + header()->flags |= SYNC_BIT; + } + + // flags + enum { + PRIORITY_MASK = 0x0003, + SYNC_BIT = 0x0004, + REPLY_BIT = 0x0008, + REPLY_ERROR_BIT = 0x0010, + UNBLOCK_BIT = 0x0020, + PUMPING_MSGS_BIT= 0x0040, + HAS_SENT_TIME_BIT = 0x0080, + }; + +#pragma pack(push, 4) + struct Header : Pickle::Header { + int32 routing; // ID of the view that this message is destined for + uint32 type; // specifies the user-defined message type + uint32 flags; // specifies control flags for the message +#if defined(OS_POSIX) + uint16 num_fds; // the number of descriptors included with this message + uint16 pad; // explicitly initialize this to appease valgrind +#endif + }; +#pragma pack(pop) + + Header* header() { + return headerT<Header>(); + } + const Header* header() const { + return headerT<Header>(); + } + + void InitLoggingVariables(); + +#if defined(OS_POSIX) + // The set of file descriptors associated with this message. +// scoped_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 + +#ifdef IPC_MESSAGE_LOG_ENABLED + // Used for logging. + mutable int64 received_time_; + mutable std::string output_params_; + mutable LogData* log_data_; + mutable bool dont_log_; +#endif +}; + +//------------------------------------------------------------------------------ + +} // namespace IPC + +enum SpecialRoutingIDs { + // indicates that we don't have a routing ID yet. + MSG_ROUTING_NONE = -2, + + // indicates a general message not sent to a particular tab. + MSG_ROUTING_CONTROL = kint32max, +}; + +#define IPC_REPLY_ID 0xFFFFFFF0 // Special message id for replies +#define IPC_LOGGING_ID 0xFFFFFFF1 // Special message id for logging + +#endif // IPC_IPC_MESSAGE_H_ diff --git a/ipc/ipc_message_macros.h b/ipc/ipc_message_macros.h new file mode 100644 index 000000000000..302d12544e4e --- /dev/null +++ b/ipc/ipc_message_macros.h @@ -0,0 +1,822 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defining IPC Messages +// +// Your IPC messages will be defined by macros inside of an XXX_messages.h +// header file. Most of the time, the system can automatically generate all +// of messaging mechanism from these definitions, but sometimes some manual +// coding is required. In these cases, you will also have an XXX_messages.cc +// implemation file as well. +// +// The senders of your messages will include your XXX_messages.h file to +// get the full set of definitions they need to send your messages. +// +// Each XXX_messages.h file must be registered with the IPC system. This +// requires adding two things: +// - An XXXMsgStart value to the IPCMessageStart enum in ipc_message_utils.h +// - An inclusion of XXX_messages.h file in a message generator .h file +// +// The XXXMsgStart value is an enumeration that ensures uniqueness for +// each different message file. Later, you will use this inside your +// XXX_messages.h file before invoking message declatation macros: +// #define IPC_MESSAGE_START XXXMsgStart +// ( ... your macro invocations go here ... ) +// +// Message Generator Files +// +// A message generator .h header file pulls in all other message-declaring +// headers for a given component. It is included by a message generator +// .cc file, which is where all the generated code will wind up. Typically, +// you will use an existing generator (e.g. common_message_generator.cc +// in /chrome/common), but there are circumstances where you may add a +// new one. +// +// In the rare cicrucmstances where you can't re-use an existing file, +// your YYY_message_generator.cc file for a component YYY would contain +// the following code: +// // Get basic type definitions. +// #define IPC_MESSAGE_IMPL +// #include "path/to/YYY_message_generator.h" +// // Generate constructors. +// #include "ipc/struct_constructor_macros.h" +// #include "path/to/YYY_message_generator.h" +// // Generate destructors. +// #include "ipc/struct_destructor_macros.h" +// #include "path/to/YYY_message_generator.h" +// // Generate param traits write methods. +// #include "ipc/param_traits_write_macros.h" +// namespace IPC { +// #include "path/to/YYY_message_generator.h" +// } // namespace IPC +// // Generate param traits read methods. +// #include "ipc/param_traits_read_macros.h" +// namespace IPC { +// #include "path/to/YYY_message_generator.h" +// } // namespace IPC +// // Generate param traits log methods. +// #include "ipc/param_traits_log_macros.h" +// namespace IPC { +// #include "path/to/YYY_message_generator.h" +// } // namespace IPC +// +// In cases where manual generation is required, in your XXX_messages.cc +// file, put the following after all the includes for param types: +// #define IPC_MESSAGE_IMPL +// #include "XXX_messages.h" +// (... implementation of traits not auto-generated ...) +// +// Multiple Inclusion +// +// The XXX_messages.h file will be multiply-included by the +// YYY_message_generator.cc file, so your XXX_messages file can't be +// guarded in the usual manner. Ideally, there will be no need for any +// inclusion guard, since the XXX_messages.h file should consist soley +// of inclusions of other headers (which are self-guarding) and IPC +// macros (which are multiply evaluating). +// +// Note that there is no #pragma once either; doing so would mark the whole +// file as being singly-included. Since your XXX_messages.h file is only +// partially-guarded, care must be taken to ensure that it is only included +// by other .cc files (and the YYY_message_generator.h file). Including an +// XXX_messages.h file in some other .h file may result in duplicate +// declarations and a compilation failure. +// +// Type Declarations +// +// It is generally a bad idea to have type definitions in a XXX_messages.h +// file; most likely the typedef will then be used in the message, as opposed +// to the struct iself. Later, an IPC message dispatcher wil need to call +// a function taking that type, and that function is declared in some other +// header. Thus, in order to get the type definition, the other header +// would have to include the XXX_messages.h file, violating the rule above +// about not including XXX_messages.h file in other .h files. +// +// One approach here is to move these type definitions to another (guarded) +// .h file and include this second .h in your XXX_messages.h file. This +// is still less than ideal, because the dispatched function would have to +// redeclare the typedef or include this second header. This may be +// reasonable in a few cases. +// +// Failing all of the above, then you will want to bracket the smallest +// possible section of your XXX_messages.h file containing these types +// with an include guard macro. Be aware that providing an incomplete +// class type declaration to avoid pulling in a long chain of headers is +// acceptable when your XXX_messages.h header is being included by the +// message sending caller's code, but not when the YYY_message_generator.c +// is building the messages. In addtion, due to the multiple inclusion +// restriction, these type ought to be guarded. Follow a convention like: +// #ifndef SOME_GUARD_MACRO +// #define SOME_GUARD_MACRO +// class some_class; // One incomplete class declaration +// class_some_other_class; // Another incomplete class declaration +// #endif // SOME_GUARD_MACRO +// #ifdef IPC_MESSAGE_IMPL +// #inlcude "path/to/some_class.h" // Full class declaration +// #inlcude "path/to/some_other_class.h" // Full class declaration +// #endif // IPC_MESSAGE_IMPL +// (.. IPC macros using some_class and some_other_class ...) +// +// Macro Invocations +// +// You will use IPC message macro invocations for three things: +// - New struct definitions for IPC +// - Registering existing struct and enum definitions with IPC +// - Defining the messages themselves +// +// New structs are defined with IPC_STRUCT_BEGIN(), IPC_STRUCT_MEMBER(), +// IPC_STRUCT_END() family of macros. These cause the XXX_messages.h +// to proclaim equivalent struct declarations for use by callers, as well +// as later registering the type with the message generation. Note that +// IPC_STRUCT_MEMBER() is only permitted inside matching calls to +// IPC_STRUCT_BEGIN() / IPC_STRUCT_END(). +// +// Externally-defined structs are registered with IPC_STRUCT_TRAITS_BEGIN(), +// IPC_STRUCT_TRAITS_MEMBER(), and IPC_STRUCT_TRAITS_END() macros. These +// cause registration of the types with message generation only. +// There's also IPC_STRUCT_TRAITS_PARENT, which is used to register a parent +// class (whose own traits are already defined). Note that +// IPC_STRUCT_TRAITS_MEMBER() and IPC_STRUCT_TRAITS_PARENT are only permitted +// inside matching calls to IPC_STRUCT_TRAITS_BEGIN() / +// IPC_STRUCT_TRAITS_END(). +// +// Enum types are registered with a single IPC_ENUM_TRAITS() macro. There +// is no need to enumerate each value to the IPC mechanism. +// +// Do not place semicolons following these IPC_ macro invocations. There +// is no reason to expect that their expansion corresponds one-to-one with +// C++ statements. +// +// Once the types have been declared / registered, message definitions follow. +// "Sync" messages are just synchronous calls, the Send() call doesn't return +// until a reply comes back. Input parameters are first (const TYPE&), and +// To declare a sync message, use the IPC_SYNC_ macros. The numbers at the +// end show how many input/output parameters there are (i.e. 1_2 is 1 in, 2 +// out). The caller does a Send([route id, ], in1, &out1, &out2). +// The receiver's handler function will be +// void OnSyncMessageName(const type1& in1, type2* out1, type3* out2) +// +// A caller can also send a synchronous message, while the receiver can respond +// at a later time. This is transparent from the sender's side. The receiver +// needs to use a different handler that takes in a IPC::Message* as the output +// type, stash the message, and when it has the data it can Send the message. +// +// Use the IPC_MESSAGE_HANDLER_DELAY_REPLY macro instead of IPC_MESSAGE_HANDLER +// IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_SyncMessageName, +// OnSyncMessageName) +// +// The handler function will look like: +// void OnSyncMessageName(const type1& in1, IPC::Message* reply_msg); +// +// Receiver stashes the IPC::Message* pointer, and when it's ready, it does: +// ViewHostMsg_SyncMessageName::WriteReplyParams(reply_msg, out1, out2); +// Send(reply_msg); + +#ifndef IPC_IPC_MESSAGE_MACROS_H_ +#define IPC_IPC_MESSAGE_MACROS_H_ + +#include "ipc/ipc_message_utils.h" +#include "ipc/param_traits_macros.h" + +#if defined(IPC_MESSAGE_IMPL) +#include "ipc/ipc_message_utils_impl.h" +#endif + +// Macros for defining structs. May be subsequently redefined. +#define IPC_STRUCT_BEGIN(struct_name) \ + struct struct_name; \ + IPC_STRUCT_TRAITS_BEGIN(struct_name) \ + IPC_STRUCT_TRAITS_END() \ + struct struct_name : IPC::NoParams { \ + struct_name(); \ + ~struct_name(); +#define IPC_STRUCT_MEMBER(type, name) type name; +#define IPC_STRUCT_END() }; + +// Message macros collect specific numbers of arguments and funnel them into +// the common message generation macro. These should never be redefined. +#define IPC_MESSAGE_CONTROL0(msg_class) \ + IPC_MESSAGE_DECL(EMPTY, CONTROL, msg_class, 0, 0, (), ()) + +#define IPC_MESSAGE_CONTROL1(msg_class, type1) \ + IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 1, 0, (type1), ()) + +#define IPC_MESSAGE_CONTROL2(msg_class, type1, type2) \ + IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 2, 0, (type1, type2), ()) + +#define IPC_MESSAGE_CONTROL3(msg_class, type1, type2, type3) \ + IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 3, 0, (type1, type2, type3), ()) + +#define IPC_MESSAGE_CONTROL4(msg_class, type1, type2, type3, type4) \ + IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 4, 0, (type1, type2, type3, type4), ()) + +#define IPC_MESSAGE_CONTROL5(msg_class, type1, type2, type3, type4, type5) \ + IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 5, 0, (type1, type2, type3, type4, type5), ()) + +#define IPC_MESSAGE_CONTROL6(msg_class, type1, type2, type3, type4, type5, type6) \ + IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 6, 0, (type1, type2, type3, type4, type5, type6), ()) + + +#define IPC_MESSAGE_ROUTED0(msg_class) \ + IPC_MESSAGE_DECL(EMPTY, ROUTED, msg_class, 0, 0, (), ()) + +#define IPC_MESSAGE_ROUTED1(msg_class, type1) \ + IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 1, 0, (type1), ()) + +#define IPC_MESSAGE_ROUTED2(msg_class, type1, type2) \ + IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 2, 0, (type1, type2), ()) + +#define IPC_MESSAGE_ROUTED3(msg_class, type1, type2, type3) \ + IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 3, 0, (type1, type2, type3), ()) + +#define IPC_MESSAGE_ROUTED4(msg_class, type1, type2, type3, type4) \ + IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 4, 0, (type1, type2, type3, type4), ()) + +#define IPC_MESSAGE_ROUTED5(msg_class, type1, type2, type3, type4, type5) \ + IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 5, 0, (type1, type2, type3, type4, type5), ()) + +#define IPC_MESSAGE_ROUTED6(msg_class, type1, type2, type3, type4, type5, type6) \ + IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 6, 0, (type1, type2, type3, type4, type5, type6), ()) + +#define IPC_SYNC_MESSAGE_CONTROL0_0(msg_class) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 0, 0, (), ()) + +#define IPC_SYNC_MESSAGE_CONTROL0_1(msg_class, type1_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 0, 1, (), (type1_out)) + +#define IPC_SYNC_MESSAGE_CONTROL0_2(msg_class, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 0, 2, (), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_CONTROL0_3(msg_class, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 0, 3, (), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_CONTROL0_4(msg_class, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 0, 4, (), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_CONTROL1_0(msg_class, type1_in) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 1, 0, (type1_in), ()) + +#define IPC_SYNC_MESSAGE_CONTROL1_1(msg_class, type1_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 1, 1, (type1_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_CONTROL1_2(msg_class, type1_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 1, 2, (type1_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_CONTROL1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 1, 3, (type1_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_CONTROL1_4(msg_class, type1_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 1, 4, (type1_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_CONTROL2_0(msg_class, type1_in, type2_in) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 2, 0, (type1_in, type2_in), ()) + +#define IPC_SYNC_MESSAGE_CONTROL2_1(msg_class, type1_in, type2_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 2, 1, (type1_in, type2_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_CONTROL2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 2, 2, (type1_in, type2_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_CONTROL2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 2, 3, (type1_in, type2_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_CONTROL2_4(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 2, 4, (type1_in, type2_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_CONTROL3_0(msg_class, type1_in, type2_in, type3_in) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 3, 0, (type1_in, type2_in, type3_in), ()) + +#define IPC_SYNC_MESSAGE_CONTROL3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 3, 1, (type1_in, type2_in, type3_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_CONTROL3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 3, 2, (type1_in, type2_in, type3_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_CONTROL3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 3, 3, (type1_in, type2_in, type3_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_CONTROL3_4(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 3, 4, (type1_in, type2_in, type3_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_CONTROL4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 4, 0, (type1_in, type2_in, type3_in, type4_in), ()) + +#define IPC_SYNC_MESSAGE_CONTROL4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 4, 1, (type1_in, type2_in, type3_in, type4_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_CONTROL4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 4, 2, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_CONTROL4_3(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 4, 3, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_CONTROL4_4(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 4, 4, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_CONTROL5_0(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 5, 0, (type1_in, type2_in, type3_in, type4_in, type5_in), ()) + +#define IPC_SYNC_MESSAGE_CONTROL5_1(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 5, 1, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_CONTROL5_2(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 5, 2, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_CONTROL5_3(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 5, 3, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_CONTROL5_4(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 5, 4, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_CONTROL6_0(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type6_in) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 6, 0, (type1_in, type2_in, type3_in, type4_in, type5_in, type6_in), ()) + +#define IPC_SYNC_MESSAGE_CONTROL6_1(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type6_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 6, 1, (type1_in, type2_in, type3_in, type4_in, type5_in, type6_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_ROUTED0_0(msg_class) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 0, 0, (), ()) + +#define IPC_SYNC_MESSAGE_ROUTED0_1(msg_class, type1_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 0, 1, (), (type1_out)) + +#define IPC_SYNC_MESSAGE_ROUTED0_2(msg_class, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 0, 2, (), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_ROUTED0_3(msg_class, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 0, 3, (), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_ROUTED0_4(msg_class, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 0, 4, (), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_ROUTED1_0(msg_class, type1_in) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 1, 0, (type1_in), ()) + +#define IPC_SYNC_MESSAGE_ROUTED1_1(msg_class, type1_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 1, 1, (type1_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_ROUTED1_2(msg_class, type1_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 1, 2, (type1_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_ROUTED1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 1, 3, (type1_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_ROUTED1_4(msg_class, type1_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 1, 4, (type1_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_ROUTED2_0(msg_class, type1_in, type2_in) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 2, 0, (type1_in, type2_in), ()) + +#define IPC_SYNC_MESSAGE_ROUTED2_1(msg_class, type1_in, type2_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 2, 1, (type1_in, type2_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_ROUTED2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 2, 2, (type1_in, type2_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_ROUTED2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 2, 3, (type1_in, type2_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_ROUTED2_4(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 2, 4, (type1_in, type2_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_ROUTED3_0(msg_class, type1_in, type2_in, type3_in) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 3, 0, (type1_in, type2_in, type3_in), ()) + +#define IPC_SYNC_MESSAGE_ROUTED3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 3, 1, (type1_in, type2_in, type3_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_ROUTED3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 3, 2, (type1_in, type2_in, type3_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_ROUTED3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 3, 3, (type1_in, type2_in, type3_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_ROUTED3_4(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 3, 4, (type1_in, type2_in, type3_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_ROUTED4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 4, 1, (type1_in, type2_in, type3_in, type4_in), ()) + +#define IPC_SYNC_MESSAGE_ROUTED4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 4, 1, (type1_in, type2_in, type3_in, type4_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_ROUTED4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 4, 2, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_ROUTED4_3(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 4, 3, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_ROUTED4_4(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 4, 4, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_ROUTED5_0(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 5, 1, (type1_in, type2_in, type3_in, type4_in, type5_in), ()) + +#define IPC_SYNC_MESSAGE_ROUTED5_1(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 5, 1, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_ROUTED5_2(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 5, 2, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_ROUTED5_3(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 5, 3, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_ROUTED5_4(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 5, 4, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out, type3_out, type4_out)) + +// Common message macro which dispatches into one of the 6 (sync x kind) +// routines. There is a way that these 6 cases can be lumped together, +// but the macros get very complicated in that case. +// Note: intended be redefined to generate other information. +#define IPC_MESSAGE_DECL(sync, kind, msg_class, \ + in_cnt, out_cnt, in_list, out_list) \ + IPC_##sync##_##kind##_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + IPC_MESSAGE_EXTRA(sync, kind, msg_class, in_cnt, out_cnt, in_list, out_list) + +#define IPC_EMPTY_CONTROL_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + class __attribute__((visibility("default"))) msg_class : public IPC::Message\ + { \ + public: \ + enum { ID = IPC_MESSAGE_ID() }; \ + msg_class() : IPC::Message(MSG_ROUTING_CONTROL, ID, PRIORITY_NORMAL) {} \ + }; + +#define IPC_EMPTY_ROUTED_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + class __attribute__((visibility("default"))) msg_class : public IPC::Message\ + { \ + public: \ + enum { ID = IPC_MESSAGE_ID() }; \ + msg_class(int32 routing_id) \ + : IPC::Message(routing_id, ID, PRIORITY_NORMAL) {} \ + }; + +#define IPC_ASYNC_CONTROL_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + class __attribute__((visibility("default"))) msg_class : \ + public IPC::MessageWithTuple<IPC_TUPLE_IN_##in_cnt in_list> { \ + public: \ + enum { ID = IPC_MESSAGE_ID() }; \ + msg_class(IPC_TYPE_IN_##in_cnt in_list); \ + virtual ~msg_class(); \ + static void Log(std::string* name, const Message* msg, std::string* l); \ + }; + +#define IPC_ASYNC_ROUTED_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + class __attribute__((visibility("default"))) msg_class : \ + public IPC::MessageWithTuple<IPC_TUPLE_IN_##in_cnt in_list> { \ + public: \ + enum { ID = IPC_MESSAGE_ID() }; \ + msg_class(int32 routing_id IPC_COMMA_##in_cnt \ + IPC_TYPE_IN_##in_cnt in_list); \ + virtual ~msg_class(); \ + static void Log(std::string* name, const Message* msg, std::string* l); \ + }; + +#define IPC_SYNC_CONTROL_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + class __attribute__((visibility("default"))) msg_class : \ + public IPC::MessageWithReply<IPC_TUPLE_IN_##in_cnt in_list, \ + IPC_TUPLE_OUT_##out_cnt out_list> { \ + public: \ + enum { ID = IPC_MESSAGE_ID() }; \ + msg_class(IPC_TYPE_IN_##in_cnt in_list \ + IPC_COMMA_AND_##in_cnt(IPC_COMMA_##out_cnt) \ + IPC_TYPE_OUT_##out_cnt out_list); \ + virtual ~msg_class(); \ + static void Log(std::string* name, const Message* msg, std::string* l); \ + }; + +#define IPC_SYNC_ROUTED_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + class __attribute__((visibility("default"))) msg_class : \ + public IPC::MessageWithReply<IPC_TUPLE_IN_##in_cnt in_list, \ + IPC_TUPLE_OUT_##out_cnt out_list> { \ + public: \ + enum { ID = IPC_MESSAGE_ID() }; \ + msg_class(int32 routing_id \ + IPC_COMMA_OR_##in_cnt(IPC_COMMA_##out_cnt) \ + IPC_TYPE_IN_##in_cnt in_list \ + IPC_COMMA_AND_##in_cnt(IPC_COMMA_##out_cnt) \ + IPC_TYPE_OUT_##out_cnt out_list); \ + virtual ~msg_class(); \ + static void Log(std::string* name, const Message* msg, std::string* l); \ + }; + +#if defined(IPC_MESSAGE_IMPL) + +// "Implementation" inclusion produces constructors, destructors, and +// logging functions, except for the no-arg special cases, where the +// implementation occurs in the declaration, and there is no special +// logging function. +#define IPC_MESSAGE_EXTRA(sync, kind, msg_class, \ + in_cnt, out_cnt, in_list, out_list) \ + IPC_##sync##_##kind##_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + IPC_##sync##_MESSAGE_LOG(msg_class) + +#define IPC_EMPTY_CONTROL_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) +#define IPC_EMPTY_ROUTED_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) + +#define IPC_ASYNC_CONTROL_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + msg_class::msg_class(IPC_TYPE_IN_##in_cnt in_list) : \ + IPC::MessageWithTuple<IPC_TUPLE_IN_##in_cnt in_list> \ + (MSG_ROUTING_CONTROL, ID, IPC_NAME_IN_##in_cnt in_list) \ + {} \ + msg_class::~msg_class() {} + +#define IPC_ASYNC_ROUTED_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + msg_class::msg_class(int32 routing_id IPC_COMMA_##in_cnt \ + IPC_TYPE_IN_##in_cnt in_list) : \ + IPC::MessageWithTuple<IPC_TUPLE_IN_##in_cnt in_list> \ + (routing_id, ID, IPC_NAME_IN_##in_cnt in_list) \ + {} \ + msg_class::~msg_class() {} + +#define IPC_SYNC_CONTROL_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + msg_class::msg_class(IPC_TYPE_IN_##in_cnt in_list \ + IPC_COMMA_AND_##in_cnt(IPC_COMMA_##out_cnt) \ + IPC_TYPE_OUT_##out_cnt out_list) : \ + IPC::MessageWithReply<IPC_TUPLE_IN_##in_cnt in_list, \ + IPC_TUPLE_OUT_##out_cnt out_list> \ + (MSG_ROUTING_CONTROL, ID, \ + IPC_NAME_IN_##in_cnt in_list, \ + IPC_NAME_OUT_##out_cnt out_list) \ + { \ + } \ + msg_class::~msg_class() {} + +#define IPC_SYNC_ROUTED_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + msg_class::msg_class(int32 routing_id \ + IPC_COMMA_OR_##in_cnt(IPC_COMMA_##out_cnt) \ + IPC_TYPE_IN_##in_cnt in_list \ + IPC_COMMA_AND_##in_cnt(IPC_COMMA_##out_cnt) \ + IPC_TYPE_OUT_##out_cnt out_list) : \ + IPC::MessageWithReply<IPC_TUPLE_IN_##in_cnt in_list, \ + IPC_TUPLE_OUT_##out_cnt out_list> \ + (routing_id, ID, \ + IPC_NAME_IN_##in_cnt in_list, \ + IPC_NAME_OUT_##out_cnt out_list) \ + {} \ + msg_class::~msg_class() {} + +#define IPC_EMPTY_MESSAGE_LOG(msg_class) + +#define IPC_ASYNC_MESSAGE_LOG(msg_class) \ + void msg_class::Log(std::string* name, \ + const Message* msg, \ + std::string* l) { \ + if (name) \ + { \ + *name = #msg_class; \ + } \ + if (!msg || !l) \ + { \ + return; \ + } \ + Param p; \ + if (Read(msg, &p)) \ + { \ + IPC::LogParam(p, l); \ + } \ + } + +#define IPC_SYNC_MESSAGE_LOG(msg_class) \ + void msg_class::Log(std::string* name, \ + const Message* msg, \ + std::string* l) { \ + if (name) \ + { \ + *name = #msg_class; \ + } \ + if (!msg || !l) \ + { \ + return; \ + } \ + if (msg->is_sync()) { \ + TupleTypes<SendParam>::ValueTuple p; \ + if (ReadSendParam(msg, &p)) \ + { \ + IPC::LogParam(p, l); \ + } \ + AddOutputParamsToLog(msg, l); \ + } else { \ + TupleTypes<ReplyParam>::ValueTuple p; \ + if (ReadReplyParam(msg, &p)) \ + { \ + IPC::LogParam(p, l); \ + } \ + } \ + } + +#elif defined(IPC_MESSAGE_MACROS_LOG_ENABLED) + +#ifndef IPC_LOG_TABLE_CREATED +#define IPC_LOG_TABLE_CREATED + +#include "base/hash_tables.h" + +typedef void (*LogFunction)(std::string* name, + const IPC::Message* msg, + std::string* params); + +typedef base::hash_map<uint32, LogFunction > LogFunctionMap; +LogFunctionMap g_log_function_mapping; + +#endif // IPC_LOG_TABLE_CREATED + +// "Log table" inclusion produces extra logging registration code. +#define IPC_MESSAGE_EXTRA(sync, kind, msg_class, \ + in_cnt, out_cnt, in_list, out_list) \ + class __attribute__((visibility("default"))) LoggerRegisterHelper##msg_class { \ + public: \ + LoggerRegisterHelper##msg_class() { \ + g_log_function_mapping[msg_class::ID] = msg_class::Log; \ + } \ + }; \ + LoggerRegisterHelper##msg_class g_LoggerRegisterHelper##msg_class; + +#else + +// Normal inclusion produces nothing extra. +#define IPC_MESSAGE_EXTRA(sync, kind, msg_class, \ + in_cnt, out_cnt, in_list, out_list) + +#endif // defined(IPC_MESSAGE_IMPL) + +// Handle variable sized argument lists. These are usually invoked by token +// pasting against the argument counts. +#define IPC_TYPE_IN_0() +#define IPC_TYPE_IN_1(t1) const t1& arg1 +#define IPC_TYPE_IN_2(t1, t2) const t1& arg1, const t2& arg2 +#define IPC_TYPE_IN_3(t1, t2, t3) const t1& arg1, const t2& arg2, const t3& arg3 +#define IPC_TYPE_IN_4(t1, t2, t3, t4) const t1& arg1, const t2& arg2, const t3& arg3, const t4& arg4 +#define IPC_TYPE_IN_5(t1, t2, t3, t4, t5) const t1& arg1, const t2& arg2, const t3& arg3, const t4& arg4, const t5& arg5 +#define IPC_TYPE_IN_6(t1, t2, t3, t4, t5, t6) const t1& arg1, const t2& arg2, const t3& arg3, const t4& arg4, const t5& arg5, const t6& arg6 + +#define IPC_TYPE_OUT_0() +#define IPC_TYPE_OUT_1(t1) t1* arg7 +#define IPC_TYPE_OUT_2(t1, t2) t1* arg7, t2* arg8 +#define IPC_TYPE_OUT_3(t1, t2, t3) t1* arg7, t2* arg8, t3* arg9 +#define IPC_TYPE_OUT_4(t1, t2, t3, t4) t1* arg7, t2* arg8, t3* arg9, t4* arg10 + +#define IPC_TUPLE_IN_0() Tuple0 +#define IPC_TUPLE_IN_1(t1) Tuple1<t1> +#define IPC_TUPLE_IN_2(t1, t2) Tuple2<t1, t2> +#define IPC_TUPLE_IN_3(t1, t2, t3) Tuple3<t1, t2, t3> +#define IPC_TUPLE_IN_4(t1, t2, t3, t4) Tuple4<t1, t2, t3, t4> +#define IPC_TUPLE_IN_5(t1, t2, t3, t4, t5) Tuple5<t1, t2, t3, t4, t5> +#define IPC_TUPLE_IN_6(t1, t2, t3, t4, t5, t6) Tuple6<t1, t2, t3, t4, t5, t6> + +#define IPC_TUPLE_OUT_0() Tuple0 +#define IPC_TUPLE_OUT_1(t1) Tuple1<t1&> +#define IPC_TUPLE_OUT_2(t1, t2) Tuple2<t1&, t2&> +#define IPC_TUPLE_OUT_3(t1, t2, t3) Tuple3<t1&, t2&, t3&> +#define IPC_TUPLE_OUT_4(t1, t2, t3, t4) Tuple4<t1&, t2&, t3&, t4&> + +#define IPC_NAME_IN_0() MakeTuple() +#define IPC_NAME_IN_1(t1) MakeRefTuple(arg1) +#define IPC_NAME_IN_2(t1, t2) MakeRefTuple(arg1, arg2) +#define IPC_NAME_IN_3(t1, t2, t3) MakeRefTuple(arg1, arg2, arg3) +#define IPC_NAME_IN_4(t1, t2, t3, t4) MakeRefTuple(arg1, arg2, arg3, arg4) +#define IPC_NAME_IN_5(t1, t2, t3, t4, t5) MakeRefTuple(arg1, arg2, arg3, arg4, arg5) +#define IPC_NAME_IN_6(t1, t2, t3, t4, t5, t6) MakeRefTuple(arg1, arg2, arg3, arg4, arg5, arg6) + +#define IPC_NAME_OUT_0() MakeTuple() +#define IPC_NAME_OUT_1(t1) MakeRefTuple(*arg7) +#define IPC_NAME_OUT_2(t1, t2) MakeRefTuple(*arg7, *arg8) +#define IPC_NAME_OUT_3(t1, t2, t3) MakeRefTuple(*arg7, *arg8, *arg9) +#define IPC_NAME_OUT_4(t1, t2, t3, t4) MakeRefTuple(*arg7, *arg8, *arg9, *arg10) + +// There are places where the syntax requires a comma if there are input args, +// if there are input args and output args, or if there are input args or +// output args. These macros allow generation of the comma as needed; invoke +// by token pasting against the argument counts. +#define IPC_COMMA_0 +#define IPC_COMMA_1 , +#define IPC_COMMA_2 , +#define IPC_COMMA_3 , +#define IPC_COMMA_4 , +#define IPC_COMMA_5 , +#define IPC_COMMA_6 , + +#define IPC_COMMA_AND_0(x) +#define IPC_COMMA_AND_1(x) x +#define IPC_COMMA_AND_2(x) x +#define IPC_COMMA_AND_3(x) x +#define IPC_COMMA_AND_4(x) x +#define IPC_COMMA_AND_5(x) x +#define IPC_COMMA_AND_6(x) x + +#define IPC_COMMA_OR_0(x) x +#define IPC_COMMA_OR_1(x) , +#define IPC_COMMA_OR_2(x) , +#define IPC_COMMA_OR_3(x) , +#define IPC_COMMA_OR_4(x) , +#define IPC_COMMA_OR_5(x) , +#define IPC_COMMA_OR_6(x) , + +// Message IDs +// Note: we currently use __LINE__ to give unique IDs to messages within +// a file. They're globally unique since each file defines its own +// IPC_MESSAGE_START. Ideally, we wouldn't use line numbers (a possibility +// is to instead use the __COUNTER__ macro, but it needs gcc 4.3 and xcode +// doesn't use it yet). +#define IPC_MESSAGE_ID() ((IPC_MESSAGE_START << 16) + __LINE__) +#define IPC_MESSAGE_ID_CLASS(id) ((id) >> 16) +#define IPC_MESSAGE_ID_LINE(id) ((id) & 0xffff) + +// Message crackers and handlers. +// Prefer to use the IPC_BEGIN_MESSAGE_MAP_EX to the older macros since they +// allow you to detect when a message could not be de-serialized. Usage: +// +// bool MyClass::OnMessageReceived(const IPC::Message& msg) { +// bool handled = true; +// bool msg_is_good = false; +// IPC_BEGIN_MESSAGE_MAP_EX(MyClass, msg, msg_is_good) +// IPC_MESSAGE_HANDLER(MsgClassOne, OnMsgClassOne) +// ...more handlers here ... +// IPC_MESSAGE_HANDLER(MsgClassTen, OnMsgClassTen) +// IPC_MESSAGE_UNHANDLED(handled = false) +// IPC_END_MESSAGE_MAP_EX() +// if (!msg_is_good) { +// // Signal error here or terminate offending process. +// } +// return handled; +// } + + +#define IPC_BEGIN_MESSAGE_MAP_EX(class_name, msg, msg_is_ok) \ + { \ + typedef class_name _IpcMessageHandlerClass; \ + const IPC::Message& ipc_message__ = msg; \ + bool& msg_is_ok__ = msg_is_ok; \ + switch (ipc_message__.type()) { \ + +#define IPC_BEGIN_MESSAGE_MAP(class_name, msg) \ + { \ + typedef class_name _IpcMessageHandlerClass; \ + const IPC::Message& ipc_message__ = msg; \ + bool msg_is_ok__ = true; \ + switch (ipc_message__.type()) { \ + +#define IPC_MESSAGE_FORWARD(msg_class, obj, member_func) \ + case msg_class::ID: \ + msg_is_ok__ = msg_class::Dispatch(&ipc_message__, obj, this, &member_func); \ + break; + +#define IPC_MESSAGE_FORWARD_EX(msg_class, obj, sender, member_func) \ + case msg_class::ID: \ + msg_is_ok__ = msg_class::Dispatch(&ipc_message__, obj, sender, &member_func); \ + break; + +#define IPC_MESSAGE_HANDLER(msg_class, member_func, sender) \ + IPC_MESSAGE_FORWARD_EX(msg_class, this, sender, _IpcMessageHandlerClass::member_func) + +#define IPC_MESSAGE_HANDLER_EX(msg_class, sender, member_func) \ + IPC_MESSAGE_FORWARD_EX(msg_class, this, sender, _IpcMessageHandlerClass::member_func) + +#define IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, obj, member_func) \ + case msg_class::ID: \ + msg_is_ok__ = msg_class::DispatchDelayReply(&ipc_message__, obj, &member_func); \ + break; + +#define IPC_MESSAGE_HANDLER_DELAY_REPLY(msg_class, member_func) \ + IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, this, \ + _IpcMessageHandlerClass::member_func) + +#define IPC_MESSAGE_HANDLER_GENERIC(msg_class, code) \ + case msg_class::ID: \ + code; \ + break; + +#define IPC_REPLY_HANDLER(func) \ + case IPC_REPLY_ID: \ + func(ipc_message__); \ + break; + + +#define IPC_MESSAGE_UNHANDLED(code) \ + default: \ + code; \ + break; + +#define IPC_MESSAGE_UNHANDLED_ERROR() \ + IPC_MESSAGE_UNHANDLED(NOTREACHED() << \ + "Invalid message with type = " << \ + ipc_message__.type()) + +#define IPC_END_MESSAGE_MAP() \ + } \ +} + +#define IPC_END_MESSAGE_MAP_EX() \ + } \ +} + +// This corresponds to an enum value from IPCMessageStart. +#define IPC_MESSAGE_CLASS(message) \ + IPC_MESSAGE_ID_CLASS(message.type()) + +#endif // IPC_IPC_MESSAGE_MACROS_H_ + +// Clean up IPC_MESSAGE_START in this unguarded section so that the +// XXX_messages.h files need not do so themselves. This makes the +// XXX_messages.h files easier to write. +#undef IPC_MESSAGE_START diff --git a/ipc/ipc_message_null_macros.h b/ipc/ipc_message_null_macros.h new file mode 100644 index 000000000000..7cd02f55569d --- /dev/null +++ b/ipc/ipc_message_null_macros.h @@ -0,0 +1,29 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// No include guard, may be included multiple times. + +// NULL out all the macros that need NULLing, so that multiple includes of +// the XXXX_messages_internal.h files will not generate noise. +#undef IPC_STRUCT_BEGIN +#undef IPC_STRUCT_MEMBER +#undef IPC_STRUCT_END +#undef IPC_STRUCT_TRAITS_BEGIN +#undef IPC_STRUCT_TRAITS_MEMBER +#undef IPC_STRUCT_TRAITS_PARENT +#undef IPC_STRUCT_TRAITS_END +#undef IPC_ENUM_TRAITS +#undef IPC_MESSAGE_DECL + +#define IPC_STRUCT_BEGIN(struct_name) +#define IPC_STRUCT_MEMBER(type, name) +#define IPC_STRUCT_END() +#define IPC_STRUCT_TRAITS_BEGIN(struct_name) +#define IPC_STRUCT_TRAITS_MEMBER(name) +#define IPC_STRUCT_TRAITS_PARENT(type) +#define IPC_STRUCT_TRAITS_END() +#define IPC_ENUM_TRAITS(enum_name) +#define IPC_MESSAGE_DECL(sync, kind, msg_class, \ + in_cnt, out_cnt, in_list, out_list) + diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc new file mode 100644 index 000000000000..2e3f04253fb8 --- /dev/null +++ b/ipc/ipc_message_utils.cc @@ -0,0 +1,485 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ipc/ipc_message_utils.h" + +//#include "base/file_path.h" +//#include "base/json/json_writer.h" +//#include "base/memory/scoped_ptr.h" +//#include "base/nullable_string16.h" +#include "base/string_number_conversions.h" +#include "base/time.h" +//#include "base/utf_string_conversions.h" +#include "base/values.h" +#if defined(OS_POSIX) +//#include "ipc/file_descriptor_set_posix.h" +#endif +//#include "ipc/ipc_channel_handle.h" + +namespace IPC { + +const int kMaxRecursionDepth = 100; + +// Value serialization + +/* +static bool ReadValue(const Message* m, void** iter, Value** value, + int recursion); + +static void WriteValue(Message* m, const Value* value, int recursion) { + if (recursion > kMaxRecursionDepth) { + LOG(WARNING) << "Max recursion depth hit in WriteValue."; + return; + } + + m->WriteInt(value->GetType()); + + switch (value->GetType()) { + case Value::TYPE_NULL: + break; + case Value::TYPE_BOOLEAN: { + bool val; + value->GetAsBoolean(&val); + WriteParam(m, val); + break; + } + case Value::TYPE_INTEGER: { + int val; + value->GetAsInteger(&val); + WriteParam(m, val); + break; + } + case Value::TYPE_DOUBLE: { + double val; + value->GetAsDouble(&val); + WriteParam(m, val); + break; + } + case Value::TYPE_STRING: { + std::string val; + value->GetAsString(&val); + WriteParam(m, val); + break; + } + case Value::TYPE_BINARY: { + const base::BinaryValue* binary = + static_cast<const base::BinaryValue*>(value); + m->WriteData(binary->GetBuffer(), static_cast<int>(binary->GetSize())); + break; + } + case Value::TYPE_DICTIONARY: { + const DictionaryValue* dict = static_cast<const DictionaryValue*>(value); + + WriteParam(m, static_cast<int>(dict->size())); + + for (DictionaryValue::key_iterator it = dict->begin_keys(); + it != dict->end_keys(); ++it) { + Value* subval; + if (dict->GetWithoutPathExpansion(*it, &subval)) { + WriteParam(m, *it); + WriteValue(m, subval, recursion + 1); + } else { + NOTREACHED() << "DictionaryValue iterators are filthy liars."; + } + } + break; + } + case Value::TYPE_LIST: { + const ListValue* list = static_cast<const ListValue*>(value); + WriteParam(m, static_cast<int>(list->GetSize())); + for (size_t i = 0; i < list->GetSize(); ++i) { + Value* subval; + if (list->Get(i, &subval)) { + WriteValue(m, subval, recursion + 1); + } else { + NOTREACHED() << "ListValue::GetSize is a filthy liar."; + } + } + break; + } + } +} + +// Helper for ReadValue that reads a DictionaryValue into a pre-allocated +// object. +static bool ReadDictionaryValue(const Message* m, void** iter, + DictionaryValue* value, int recursion) { + int size; + if (!ReadParam(m, iter, &size)) + return false; + + for (int i = 0; i < size; ++i) { + std::string key; + Value* subval; + if (!ReadParam(m, iter, &key) || + !ReadValue(m, iter, &subval, recursion + 1)) + return false; + value->Set(key, subval); + } + + return true; +} + +// Helper for ReadValue that reads a ReadListValue into a pre-allocated +// object. +static bool ReadListValue(const Message* m, void** iter, + ListValue* value, int recursion) { + int size; + if (!ReadParam(m, iter, &size)) + return false; + + for (int i = 0; i < size; ++i) { + Value* subval; + if (!ReadValue(m, iter, &subval, recursion + 1)) + return false; + value->Set(i, subval); + } + + return true; +} + +static bool ReadValue(const Message* m, void** iter, Value** value, + int recursion) { + if (recursion > kMaxRecursionDepth) { + LOG(WARNING) << "Max recursion depth hit in ReadValue."; + return false; + } + + int type; + if (!ReadParam(m, iter, &type)) + return false; + + switch (type) { + case Value::TYPE_NULL: + *value = Value::CreateNullValue(); + break; + case Value::TYPE_BOOLEAN: { + bool val; + if (!ReadParam(m, iter, &val)) + return false; + *value = Value::CreateBooleanValue(val); + break; + } + case Value::TYPE_INTEGER: { + int val; + if (!ReadParam(m, iter, &val)) + return false; + *value = Value::CreateIntegerValue(val); + break; + } + case Value::TYPE_DOUBLE: { + double val; + if (!ReadParam(m, iter, &val)) + return false; + *value = Value::CreateDoubleValue(val); + break; + } + case Value::TYPE_STRING: { + std::string val; + if (!ReadParam(m, iter, &val)) + return false; + *value = Value::CreateStringValue(val); + break; + } + case Value::TYPE_BINARY: { + const char* data; + int length; + if (!m->ReadData(iter, &data, &length)) + return false; + *value = base::BinaryValue::CreateWithCopiedBuffer(data, length); + break; + } + case Value::TYPE_DICTIONARY: { + scoped_ptr<DictionaryValue> val(new DictionaryValue()); + if (!ReadDictionaryValue(m, iter, val.get(), recursion)) + return false; + *value = val.release(); + break; + } + case Value::TYPE_LIST: { + scoped_ptr<ListValue> val(new ListValue()); + if (!ReadListValue(m, iter, val.get(), recursion)) + return false; + *value = val.release(); + break; + } + default: + return false; + } + + return true; +} +*/ + +void ParamTraits<int>::Log(const param_type& p, std::string* l) { + //l->append(base::IntToString(p)); +} + +void ParamTraits<unsigned int>::Log(const param_type& p, std::string* l) { + //l->append(base::UintToString(p)); +} + +void ParamTraits<long>::Log(const param_type& p, std::string* l) { + //l->append(base::Int64ToString(static_cast<int64>(p))); +} + +void ParamTraits<unsigned long>::Log(const param_type& p, std::string* l) { + //l->append(base::Uint64ToString(static_cast<uint64>(p))); +} + +void ParamTraits<long long>::Log(const param_type& p, std::string* l) { + //l->append(base::Int64ToString(static_cast<int64>(p))); +} + +void ParamTraits<unsigned long long>::Log(const param_type& p, std::string* l) { + //l->append(base::Uint64ToString(p)); +} + + +void ParamTraits<unsigned short>::Write(Message* m, const param_type& p) { + m->WriteBytes(&p, sizeof(param_type)); +} + +bool ParamTraits<unsigned short>::Read(const Message* m, void** iter, + param_type* r) { + const char* data; + if (!m->ReadBytes(iter, &data, sizeof(param_type))) + return false; + memcpy(r, data, sizeof(param_type)); + return true; +} + +/* +void ParamTraits<unsigned short>::Log(const param_type& p, std::string* l) { + l->append(base::UintToString(p)); +} + + +void ParamTraits<base::Time>::Write(Message* m, const param_type& p) { + ParamTraits<int64>::Write(m, p.ToInternalValue()); +} + +bool ParamTraits<base::Time>::Read(const Message* m, void** iter, + param_type* r) { + int64 value; + if (!ParamTraits<int64>::Read(m, iter, &value)) + return false; + *r = base::Time::FromInternalValue(value); + return true; +} + +void ParamTraits<base::Time>::Log(const param_type& p, std::string* l) { + ParamTraits<int64>::Log(p.ToInternalValue(), l); +} + +void ParamTraits<base::TimeDelta> ::Write(Message* m, const param_type& p) { + ParamTraits<int64> ::Write(m, p.InMicroseconds()); +} + +bool ParamTraits<base::TimeDelta> ::Read(const Message* m, + void** iter, + param_type* r) { + int64 value; + bool ret = ParamTraits<int64> ::Read(m, iter, &value); + if (ret) + *r = base::TimeDelta::FromMicroseconds(value); + + return ret; +} + +void ParamTraits<base::TimeDelta> ::Log(const param_type& p, std::string* l) { + ParamTraits<int64> ::Log(p.InMicroseconds(), l); +} + +void ParamTraits<DictionaryValue>::Write(Message* m, const param_type& p) { + WriteValue(m, &p, 0); +} + +bool ParamTraits<DictionaryValue>::Read( + const Message* m, void** iter, param_type* r) { + int type; + if (!ReadParam(m, iter, &type) || type != Value::TYPE_DICTIONARY) + return false; + + return ReadDictionaryValue(m, iter, r, 0); +} + +void ParamTraits<DictionaryValue>::Log(const param_type& p, std::string* l) { + std::string json; + base::JSONWriter::Write(&p, false, &json); + l->append(json); +} + +void ParamTraits<ListValue>::Write(Message* m, const param_type& p) { + WriteValue(m, &p, 0); +} + +bool ParamTraits<ListValue>::Read( + const Message* m, void** iter, param_type* r) { + int type; + if (!ReadParam(m, iter, &type) || type != Value::TYPE_LIST) + return false; + + return ReadListValue(m, iter, r, 0); +} + +void ParamTraits<ListValue>::Log(const param_type& p, std::string* l) { + std::string json; + base::JSONWriter::Write(&p, false, &json); + l->append(json); +} + +void ParamTraits<std::wstring>::Log(const param_type& p, std::string* l) { + l->append(WideToUTF8(p)); +} + +void ParamTraits<NullableString16>::Write(Message* m, const param_type& p) { + WriteParam(m, p.string()); + WriteParam(m, p.is_null()); +} + +bool ParamTraits<NullableString16>::Read(const Message* m, void** iter, + param_type* r) { + string16 string; + if (!ReadParam(m, iter, &string)) + return false; + bool is_null; + if (!ReadParam(m, iter, &is_null)) + return false; + *r = NullableString16(string, is_null); + return true; +} + +void ParamTraits<NullableString16>::Log(const param_type& p, std::string* l) { + l->append("("); + LogParam(p.string(), l); + l->append(", "); + LogParam(p.is_null(), l); + l->append(")"); +} + +#if !defined(WCHAR_T_IS_UTF16) +void ParamTraits<string16>::Log(const param_type& p, std::string* l) { + l->append(UTF16ToUTF8(p)); +} +#endif + +*/ + +/* +void ParamTraits<FilePath>::Write(Message* m, const param_type& p) { + ParamTraits<FilePath::StringType>::Write(m, p.value()); +} + +bool ParamTraits<FilePath>::Read(const Message* m, void** iter, param_type* r) { + FilePath::StringType value; + if (!ParamTraits<FilePath::StringType>::Read(m, iter, &value)) + return false; + *r = FilePath(value); + return true; +} + +void ParamTraits<FilePath>::Log(const param_type& p, std::string* l) { + ParamTraits<FilePath::StringType>::Log(p.value(), l); +} + +#if defined(OS_POSIX) +void ParamTraits<base::FileDescriptor>::Write(Message* m, const param_type& p) { + const bool valid = p.fd >= 0; + WriteParam(m, valid); + + if (valid) { + if (!m->WriteFileDescriptor(p)) + NOTREACHED(); + } +} + +bool ParamTraits<base::FileDescriptor>::Read(const Message* m, void** 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); +} + +void ParamTraits<base::FileDescriptor>::Log(const param_type& p, + std::string* l) { + if (p.auto_close) { + l->append(StringPrintf("FD(%d auto-close)", p.fd)); + } else { + l->append(StringPrintf("FD(%d)", p.fd)); + } +} +#endif // defined(OS_POSIX) + +void ParamTraits<IPC::ChannelHandle>::Write(Message* m, const param_type& p) { + WriteParam(m, p.name); +#if defined(OS_POSIX) + WriteParam(m, p.socket); +#endif +} + +bool ParamTraits<IPC::ChannelHandle>::Read(const Message* m, void** iter, + param_type* r) { + return ReadParam(m, iter, &r->name) +#if defined(OS_POSIX) + && ReadParam(m, iter, &r->socket) +#endif + ; +} + +void ParamTraits<IPC::ChannelHandle>::Log(const param_type& p, + std::string* l) { + l->append(StringPrintf("ChannelHandle(%s", p.name.c_str())); +#if defined(OS_POSIX) + ParamTraits<base::FileDescriptor>::Log(p.socket, l); +#endif + l->append(")"); +} + +LogData::LogData() + : routing_id(0), + type(0), + sent(0), + receive(0), + dispatch(0) { +} + +LogData::~LogData() { +} + +void ParamTraits<LogData>::Write(Message* m, const param_type& p) { + WriteParam(m, p.channel); + WriteParam(m, p.routing_id); + WriteParam(m, static_cast<int>(p.type)); + WriteParam(m, p.flags); + WriteParam(m, p.sent); + WriteParam(m, p.receive); + WriteParam(m, p.dispatch); + WriteParam(m, p.params); +} + +bool ParamTraits<LogData>::Read(const Message* m, void** iter, param_type* r) { + int type = -1; + bool result = + ReadParam(m, iter, &r->channel) && + ReadParam(m, iter, &r->routing_id) && + ReadParam(m, iter, &type) && + ReadParam(m, iter, &r->flags) && + ReadParam(m, iter, &r->sent) && + ReadParam(m, iter, &r->receive) && + ReadParam(m, iter, &r->dispatch) && + ReadParam(m, iter, &r->params); + r->type = static_cast<uint16>(type); + return result; +} +*/ +} // namespace IPC diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h new file mode 100755 index 000000000000..8c2014f35056 --- /dev/null +++ b/ipc/ipc_message_utils.h @@ -0,0 +1,1238 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_IPC_MESSAGE_UTILS_H_ +#define IPC_IPC_MESSAGE_UTILS_H_ +#pragma once + +#include <algorithm> +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "base/compiler_specific.h" +//#include "base/format_macros.h" +//#include "base/string16.h" +//#include "base/stringprintf.h" +//#include "base/string_util.h" +#include "base/tuple.h" +#include "ipc/ipc_param_traits.h" +#include "ipc/ipc_sync_message.h" + +#if defined(COMPILER_GCC) +// GCC "helpfully" tries to inline template methods in release mode. Except we +// want the majority of the template junk being expanded once in the +// implementation file (and only provide the definitions in +// ipc_message_utils_impl.h in those files) and exported, instead of expanded +// at every call site. Special note: GCC happily accepts the attribute before +// the method declaration, but only acts on it if it is after. +#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40500 +// Starting in gcc 4.5, the noinline no longer implies the concept covered by +// the introduced noclone attribute, which will create specialized versions of +// functions/methods when certain types are constant. +// www.gnu.org/software/gcc/gcc-4.5/changes.html +#define IPC_MSG_NOINLINE __attribute__((noinline, noclone)); +#else +#define IPC_MSG_NOINLINE __attribute__((noinline)); +#endif +#elif defined(COMPILER_MSVC) +// MSVC++ doesn't do this. +#define IPC_MSG_NOINLINE +#else +#error "Please add the noinline property for your new compiler here." +#endif + +// Used by IPC_BEGIN_MESSAGES so that each message class starts from a unique +// base. Messages have unique IDs across channels in order for the IPC logging +// code to figure out the message class from its ID. +enum IPCMessageStart { + AutomationMsgStart = 0, + PushServiceMsgStart, + CmcStrRegistryManagerStart, + LastIPCMsgStart // Must come last. +}; + +class FilePath; +class NullableString16; + +namespace base { +class DictionaryValue; +class ListValue; +class Time; +class TimeDelta; +struct FileDescriptor; +} + +namespace IPC { + +struct ChannelHandle; + +//----------------------------------------------------------------------------- +// An iterator class for reading the fields contained within a Message. + +class MessageIterator { + public: + explicit MessageIterator(const Message& m) : msg_(m), iter_(NULL) { + } + int NextInt() const { + int val = -1; + if (!msg_.ReadInt(&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; + } +*/ + void NextData(const char** data, int* length) const { + if (!msg_.ReadData(&iter_, data, length)) { + NOTREACHED(); + } + } + private: + const Message& msg_; + mutable void* iter_; +}; + +//----------------------------------------------------------------------------- +// A dummy struct to place first just to allow leading commas for all +// members in the macro-generated constructor initializer lists. +struct NoParams { +}; + +//----------------------------------------------------------------------------- +// ParamTraits specializations, etc. + +template <class P> +static inline void WriteParam(Message* m, const P& p) { + typedef typename SimilarTypeTraits<P>::Type Type; + ParamTraits<Type>::Write(m, static_cast<const Type& >(p)); +} + +template <class P> +static inline bool WARN_UNUSED_RESULT ReadParam(const Message* m, void** iter, + P* p) { + typedef typename SimilarTypeTraits<P>::Type Type; + return ParamTraits<Type>::Read(m, iter, reinterpret_cast<Type* >(p)); +} + + +template <class P> +static inline void LogParam(const P& p, std::string* l) { + typedef typename SimilarTypeTraits<P>::Type Type; + ParamTraits<Type>::Log(static_cast<const Type& >(p), l); +} + + +template <> +struct ParamTraits<bool> { + typedef bool param_type; + static void Write(Message* m, const param_type& p) { + m->WriteBool(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadBool(iter, r); + } + static void Log(const param_type& p, std::string* l) { + l->append(p ? "true" : "false"); + } +}; + +template <> +struct ParamTraits<int> { + typedef int param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadInt(iter, r); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits<unsigned int> { + typedef unsigned int param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadInt(iter, reinterpret_cast<int*>(r)); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits<long> { + typedef long param_type; + static void Write(Message* m, const param_type& p) { + m->WriteLong(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadLong(iter, r); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits<unsigned long> { + typedef unsigned long param_type; + static void Write(Message* m, const param_type& p) { + m->WriteLong(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadLong(iter, reinterpret_cast<long*>(r)); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits<long long> { + typedef long long param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt64(static_cast<int64>(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadInt64(iter, reinterpret_cast<int64*>(r)); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits<unsigned long long> { + typedef unsigned long long param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt64(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadInt64(iter, reinterpret_cast<int64*>(r)); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; + +template <> +struct IPC_EXPORT ParamTraits<unsigned short> { + typedef unsigned short param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +// Note that the IPC layer doesn't sanitize NaNs and +/- INF values. Clients +// should be sure to check the sanity of these values after receiving them over +// IPC. +template <> +struct ParamTraits<float> { + typedef float param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast<const char*>(&p), sizeof(param_type)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size; + if (!m->ReadData(iter, &data, &data_size) || + data_size != sizeof(param_type)) { + NOTREACHED(); + return false; + } + memcpy(r, data, sizeof(param_type)); + return true; + } + + static void Log(const param_type& p, std::string* l) { + //l->append(StringPrintf("%e", p)); + } + +}; + +template <> +struct ParamTraits<double> { + typedef double param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast<const char*>(&p), sizeof(param_type)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size; + if (!m->ReadData(iter, &data, &data_size) || + data_size != sizeof(param_type)) { + NOTREACHED(); + return false; + } + memcpy(r, data, sizeof(param_type)); + return true; + } + + static void Log(const param_type& p, std::string* l) { + //l->append(StringPrintf("%e", p)); + } + +}; + +/* +template <> +struct IPC_EXPORT ParamTraits<base::Time> { + typedef base::Time param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct IPC_EXPORT ParamTraits<base::TimeDelta> { + typedef base::TimeDelta param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +#if defined(OS_WIN) +template <> +struct ParamTraits<LOGFONT> { + typedef LOGFONT param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast<const char*>(&p), sizeof(LOGFONT)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(LOGFONT)) { + memcpy(r, data, sizeof(LOGFONT)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::string* l) { +// l->append(StringPrintf("<LOGFONT>")); + } +}; + +template <> +struct ParamTraits<MSG> { + typedef MSG param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast<const char*>(&p), sizeof(MSG)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(MSG)) { + memcpy(r, data, sizeof(MSG)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::string* l) { + l->append("<MSG>"); + } +}; +#endif // defined(OS_WIN) + +template <> +struct IPC_EXPORT ParamTraits<base::DictionaryValue> { + typedef base::DictionaryValue param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct IPC_EXPORT ParamTraits<base::ListValue> { + typedef base::ListValue param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +*/ +template <> +struct ParamTraits<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, void** iter, param_type* r) { + return m->ReadString(iter, r); + } + static void Log(const param_type& p, std::string* l) { + l->append(p); + } +}; + +/* +template<typename CharType> +static void LogBytes(const std::vector<CharType>& data, std::string* out) { +#if defined(OS_WIN) + // Windows has a GUI for logging, which can handle arbitrary binary data. + for (size_t i = 0; i < data.size(); ++i) + out->push_back(data[i]); +#else + // On POSIX, we log to stdout, which we assume can display ASCII. + static const size_t kMaxBytesToLog = 100; + for (size_t i = 0; i < std::min(data.size(), kMaxBytesToLog); ++i) { + if (isprint(data[i])) + out->push_back(data[i]); + else + { + // out->append(StringPrintf("[%02X]", static_cast<unsigned char>(data[i]))); + } + } + if (data.size() > kMaxBytesToLog) { +// out->append( +// StringPrintf(" and %u more bytes", +// static_cast<unsigned>(data.size() - kMaxBytesToLog))); + } +#endif +} +*/ + +template <> +struct ParamTraits<std::vector<unsigned char> > { + typedef std::vector<unsigned char> param_type; + static void Write(Message* m, const param_type& p) { + if (p.empty()) { + m->WriteData(NULL, 0); + } else { + m->WriteData(reinterpret_cast<const char*>(&p.front()), + static_cast<int>(p.size())); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + if (!m->ReadData(iter, &data, &data_size) || data_size < 0) + return false; + r->resize(data_size); + if (data_size) + memcpy(&r->front(), data, data_size); + return true; + } + static void Log(const param_type& p, std::string* l) { + // LogBytes(p, l); + } +}; + +template <> +struct ParamTraits<std::vector<char> > { + typedef std::vector<char> param_type; + static void Write(Message* m, const param_type& p) { + if (p.empty()) { + m->WriteData(NULL, 0); + } else { + m->WriteData(&p.front(), static_cast<int>(p.size())); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + if (!m->ReadData(iter, &data, &data_size) || data_size < 0) + return false; + r->resize(data_size); + if (data_size) + memcpy(&r->front(), data, data_size); + return true; + } + static void Log(const param_type& p, std::string* l) { + //LogBytes(p, l); + } +}; + +template <class P> +struct ParamTraits<std::vector<P> > { + typedef std::vector<P> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast<int>(p.size())); + for (size_t i = 0; i < p.size(); i++) + WriteParam(m, p[i]); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int size; + // ReadLength() checks for < 0 itself. + if (!m->ReadLength(iter, &size)) + return false; + // Resizing beforehand is not safe, see BUG 1006367 for details. + if (INT_MAX / sizeof(P) <= static_cast<size_t>(size)) + return false; + r->resize(size); + for (int i = 0; i < size; i++) { + if (!ReadParam(m, iter, &(*r)[i])) + return false; + } + return true; + } + static void Log(const param_type& p, std::string* l) { +// for (size_t i = 0; i < p.size(); ++i) { +// if (i != 0) +// l->append(" "); +// LogParam((p[i]), l); +// } + } +}; + +/* +template <class P> +struct ParamTraits<std::set<P> > { + typedef std::set<P> 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); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int size; + if (!m->ReadLength(iter, &size)) + return false; + for (int i = 0; i < size; ++i) { + P item; + if (!ReadParam(m, iter, &item)) + return false; + r->insert(item); + } + return true; + } + static void Log(const param_type& p, std::string* l) { + l->append("<std::set>"); + } +}; + + +template <class K, class V> +struct ParamTraits<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, void** 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::string* l) { + l->append("<std::map>"); + } +}; + + +template <> +struct ParamTraits<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, void** iter, param_type* r) { + return m->ReadWString(iter, r); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; + +template <class A, class B> +struct ParamTraits<std::pair<A, B> > { + typedef std::pair<A, B> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.first); + WriteParam(m, p.second); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return ReadParam(m, iter, &r->first) && ReadParam(m, iter, &r->second); + } + static void Log(const param_type& p, std::string* l) { + l->append("("); + LogParam(p.first, l); + l->append(", "); + LogParam(p.second, l); + l->append(")"); + } +}; + +template <> +struct IPC_EXPORT ParamTraits<NullableString16> { + typedef NullableString16 param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +// If WCHAR_T_IS_UTF16 is defined, then string16 is a std::wstring so we don't +// need this trait. +#if !defined(WCHAR_T_IS_UTF16) +template <> +struct ParamTraits<string16> { + typedef string16 param_type; + static void Write(Message* m, const param_type& p) { + m->WriteString16(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadString16(iter, r); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; +#endif + +// and, a few more useful types... +#if defined(OS_WIN) +template <> +struct ParamTraits<HANDLE> { + typedef HANDLE param_type; + static void Write(Message* m, const param_type& p) { + // Note that HWNDs/HANDLE/HCURSOR/HACCEL etc are always 32 bits, even on 64 + // bit systems. + m->WriteUInt32(reinterpret_cast<uint32>(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(uint32)); + return m->ReadUInt32(iter, reinterpret_cast<uint32*>(r)); + } + static void Log(const param_type& p, std::string* l) { +// l->append(StringPrintf("0x%X", p)); + } +}; + +template <> +struct ParamTraits<HCURSOR> { + typedef HCURSOR param_type; + static void Write(Message* m, const param_type& p) { + m->WriteUInt32(reinterpret_cast<uint32>(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(uint32)); + return m->ReadUInt32(iter, reinterpret_cast<uint32*>(r)); + } + static void Log(const param_type& p, std::string* l) { +// l->append(StringPrintf("0x%X", p)); + } +}; + +template <> +struct ParamTraits<HACCEL> { + typedef HACCEL param_type; + static void Write(Message* m, const param_type& p) { + m->WriteUInt32(reinterpret_cast<uint32>(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(uint32)); + return m->ReadUInt32(iter, reinterpret_cast<uint32*>(r)); + } +}; + +template <> +struct ParamTraits<POINT> { + typedef POINT param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p.x); + m->WriteInt(p.y); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int x, y; + if (!m->ReadInt(iter, &x) || !m->ReadInt(iter, &y)) + return false; + r->x = x; + r->y = y; + return true; + } + static void Log(const param_type& p, std::string* l) { +// l->append(StringPrintf("(%d, %d)", p.x, p.y)); + } +}; +#endif // defined(OS_WIN) + +template <> +struct IPC_EXPORT ParamTraits<FilePath> { + typedef FilePath param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +#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 IPC_EXPORT ParamTraits<base::FileDescriptor> { + typedef base::FileDescriptor param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; +#endif // defined(OS_POSIX) + +// A ChannelHandle is basically a platform-inspecific wrapper around the +// fact that IPC endpoints are handled specially on POSIX. See above comments +// on FileDescriptor for more background. +template<> +struct IPC_EXPORT ParamTraits<IPC::ChannelHandle> { + typedef ChannelHandle param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +#if defined(OS_WIN) +template <> +struct ParamTraits<XFORM> { + typedef XFORM param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast<const char*>(&p), sizeof(XFORM)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(XFORM)) { + memcpy(r, data, sizeof(XFORM)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::string* l) { + l->append("<XFORM>"); + } +}; +#endif // defined(OS_WIN) + +struct IPC_EXPORT LogData { + LogData(); + ~LogData(); + + std::string channel; + int32 routing_id; + uint32 type; // "User-defined" message type, from ipc_message.h. + std::string flags; + int64 sent; // Time that the message was sent (i.e. at Send()). + int64 receive; // Time before it was dispatched (i.e. before calling + // OnMessageReceived). + int64 dispatch; // Time after it was dispatched (i.e. after calling + // OnMessageReceived). + std::string message_name; + std::string params; +}; + +template <> +struct IPC_EXPORT ParamTraits<LogData> { + typedef LogData param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l) { + // Doesn't make sense to implement this! + } +}; + +*/ +template <> +struct ParamTraits<Message> { + static void Write(Message* m, const Message& p) { + DCHECK(p.size() <= INT_MAX); + int message_size = static_cast<int>(p.size()); + m->WriteInt(message_size); + m->WriteData(reinterpret_cast<const char*>(p.data()), message_size); + } + static bool Read(const Message* m, void** iter, Message* r) { + int size; + if (!m->ReadInt(iter, &size)) + return false; + const char* data; + if (!m->ReadData(iter, &data, &size)) + return false; + *r = Message(data, size); + return true; + } + static void Log(const Message& p, std::string* l) { + l->append("<IPC::Message>"); + } +}; + +template <> +struct ParamTraits<Tuple0> { + typedef Tuple0 param_type; + static void Write(Message* m, const param_type& p) { + } + static bool Read(const Message* m, void** iter, param_type* r) { + return true; + } + static void Log(const param_type& p, std::string* l) { + } +}; + +template <class A> +struct ParamTraits< Tuple1<A> > { + typedef Tuple1<A> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return ReadParam(m, iter, &r->a); + } + static void Log(const param_type& p, std::string* l) { + LogParam(p.a, l); + } +}; + +template <class A, class B> +struct ParamTraits< Tuple2<A, B> > { + typedef Tuple2<A, B> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b)); + } + static void Log(const param_type& p, std::string* l) { + LogParam(p.a, l); + l->append(", "); + LogParam(p.b, l); + } +}; + +template <class A, class B, class C> +struct ParamTraits< Tuple3<A, B, C> > { + typedef Tuple3<A, B, C> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + WriteParam(m, p.c); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b) && + ReadParam(m, iter, &r->c)); + } + static void Log(const param_type& p, std::string* l) { + LogParam(p.a, l); + l->append(", "); + LogParam(p.b, l); + l->append(", "); + LogParam(p.c, l); + } +}; + +template <class A, class B, class C, class D> +struct ParamTraits< Tuple4<A, B, C, D> > { + typedef Tuple4<A, B, C, D> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + WriteParam(m, p.c); + WriteParam(m, p.d); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b) && + ReadParam(m, iter, &r->c) && + ReadParam(m, iter, &r->d)); + } + static void Log(const param_type& p, std::string* l) { + LogParam(p.a, l); + l->append(", "); + LogParam(p.b, l); + l->append(", "); + LogParam(p.c, l); + l->append(", "); + LogParam(p.d, l); + } +}; + +template <class A, class B, class C, class D, class E> +struct ParamTraits< Tuple5<A, B, C, D, E> > { + typedef Tuple5<A, B, C, D, E> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + WriteParam(m, p.c); + WriteParam(m, p.d); + WriteParam(m, p.e); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b) && + ReadParam(m, iter, &r->c) && + ReadParam(m, iter, &r->d) && + ReadParam(m, iter, &r->e)); + } + static void Log(const param_type& p, std::string* l) { + LogParam(p.a, l); + l->append(", "); + LogParam(p.b, l); + l->append(", "); + LogParam(p.c, l); + l->append(", "); + LogParam(p.d, l); + l->append(", "); + LogParam(p.e, l); + } +}; + +template <class A, class B, class C, class D, class E, class F> +struct ParamTraits< Tuple6<A, B, C, D, E, F> > { +typedef Tuple6<A, B, C, D, E, F> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + WriteParam(m, p.c); + WriteParam(m, p.d); + WriteParam(m, p.e); + WriteParam(m, p.f); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b) && + ReadParam(m, iter, &r->c) && + ReadParam(m, iter, &r->d) && + ReadParam(m, iter, &r->e) && + ReadParam(m, iter, &r->f)); + } + static void Log(const param_type& p, std::string* l) { + LogParam(p.a, l); + l->append(", "); + LogParam(p.b, l); + l->append(", "); + LogParam(p.c, l); + l->append(", "); + LogParam(p.d, l); + l->append(", "); + LogParam(p.e, l); + l->append(", "); + LogParam(p.f, l); + } +}; + +//----------------------------------------------------------------------------- +// Generic message subclasses + +// Used for asynchronous messages. +template <class ParamType> +class __attribute__((visibility("default"))) MessageWithTuple : public Message { + public: + typedef ParamType Param; + typedef typename TupleTypes<ParamType>::ParamTuple RefParam; + + // The constructor and the Read() method's templated implementations are in + // ipc_message_utils_impl.h. The subclass constructor and Log() methods call + // the templated versions of these and make sure there are instantiations in + // those translation units. + MessageWithTuple(int32 routing_id, uint32 type, const RefParam& p); + + static bool Read(const Message* msg, Param* p) IPC_MSG_NOINLINE; + + // Generic dispatcher. Should cover most cases. + template<class T, class S, class Method> + static bool Dispatch(const Message* msg, T* obj, S* sender, Method func) { + Param p; + if (Read(msg, &p)) { + DispatchToMethod(obj, func, p); + return true; + } + return false; + } + + // The following dispatchers exist for the case where the callback function + // needs the message as well. They assume that "Param" is a type of Tuple + // (except the one arg case, as there is no Tuple1). + template<class T, class S, typename TA> + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)(const Message&, TA)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a); + return true; + } + return false; + } + + template<class T, class S, typename TA, typename TB> + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)(const Message&, TA, TB)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b); + return true; + } + return false; + } + + template<class T, class S, typename TA, typename TB, typename TC> + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)(const Message&, TA, TB, TC)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b, p.c); + return true; + } + return false; + } + + template<class T, class S, typename TA, typename TB, typename TC, typename TD> + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)(const Message&, TA, TB, TC, TD)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b, p.c, p.d); + return true; + } + return false; + } + + template<class T, class S, typename TA, typename TB, typename TC, typename TD, + typename TE> + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)(const Message&, TA, TB, TC, TD, TE)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b, p.c, p.d, p.e); + return true; + } + return false; + } + + // Functions used to do manual unpacking. Only used by the automation code, + // these should go away once that code uses SyncChannel. + template<typename TA, typename TB> + static bool Read(const IPC::Message* msg, TA* a, TB* b) { + ParamType params; + if (!Read(msg, ¶ms)) + return false; + *a = params.a; + *b = params.b; + return true; + } + + template<typename TA, typename TB, typename TC> + static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c) { + ParamType params; + if (!Read(msg, ¶ms)) + return false; + *a = params.a; + *b = params.b; + *c = params.c; + return true; + } + + template<typename TA, typename TB, typename TC, typename TD> + static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c, TD* d) { + ParamType params; + if (!Read(msg, ¶ms)) + return false; + *a = params.a; + *b = params.b; + *c = params.c; + *d = params.d; + return true; + } + + template<typename TA, typename TB, typename TC, typename TD, typename TE> + static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c, TD* d, TE* e) { + ParamType params; + if (!Read(msg, ¶ms)) + return false; + *a = params.a; + *b = params.b; + *c = params.c; + *d = params.d; + *e = params.e; + return true; + } +}; + +// defined in ipc_logging.cc +IPC_EXPORT void GenerateLogData(const std::string& channel, + const Message& message, + LogData* data); + + +#if defined(IPC_MESSAGE_LOG_ENABLED) +inline void AddOutputParamsToLog(const Message* msg, std::string* l) { + const std::string& output_params = msg->output_params(); + if (!l->empty() && !output_params.empty()) + l->append(", "); + + l->append(output_params); +} + +template <class ReplyParamType> +inline void LogReplyParamsToMessage(const ReplyParamType& reply_params, + const Message* msg) { + if (msg->received_time() != 0) { + std::string output_params; + LogParam(reply_params, &output_params); + msg->set_output_params(output_params); + } +} + +inline void ConnectMessageAndReply(const Message* msg, Message* reply) { + if (msg->sent_time()) { + // Don't log the sync message after dispatch, as we don't have the + // output parameters at that point. Instead, save its data and log it + // with the outgoing reply message when it's sent. + LogData* data = new LogData; + GenerateLogData("", *msg, data); + msg->set_dont_log(); + reply->set_sync_log_data(data); + } +} +#else +inline void AddOutputParamsToLog(const Message* msg, std::string* l) {} + +template <class ReplyParamType> +inline void LogReplyParamsToMessage(const ReplyParamType& reply_params, + const Message* msg) {} + +inline void ConnectMessageAndReply(const Message* msg, Message* reply) {} +#endif + +// This class assumes that its template argument is a RefTuple (a Tuple with +// reference elements). This would go into ipc_message_utils_impl.h, but it is +// also used by chrome_frame. +template <class RefTuple> +class ParamDeserializer : public MessageReplyDeserializer { + public: + explicit ParamDeserializer(const RefTuple& out) : out_(out) { } + + bool SerializeOutputParameters(const IPC::Message& msg, void* iter) { + return ReadParam(&msg, &iter, &out_); + } + + RefTuple out_; +}; + +// Used for synchronous messages. +template <class SendParamType, class ReplyParamType> +class __attribute__((visibility("default"))) MessageWithReply : public SyncMessage { + public: + typedef SendParamType SendParam; + typedef typename TupleTypes<SendParam>::ParamTuple RefSendParam; + typedef ReplyParamType ReplyParam; + + MessageWithReply(int32 routing_id, uint32 type, + const RefSendParam& send, const ReplyParam& reply); + static bool ReadSendParam(const Message* msg, SendParam* p) IPC_MSG_NOINLINE; + static bool ReadReplyParam( + const Message* msg, + typename TupleTypes<ReplyParam>::ValueTuple* p) IPC_MSG_NOINLINE; + + template<class T, class S, class Method> + static bool Dispatch(const Message* msg, T* obj, S* sender, Method func) { + SendParam send_params; + Message* reply = GenerateReply(msg); + bool error; + if (ReadSendParam(msg, &send_params)) { + typename TupleTypes<ReplyParam>::ValueTuple reply_params; + DispatchToMethod(obj, func, send_params, &reply_params); + WriteParam(reply, reply_params); + error = false; + LogReplyParamsToMessage(reply_params, msg); + } else { + NOTREACHED() << "Error deserializing message " << msg->type(); + reply->set_reply_error(); + error = true; + } + + sender->Send(reply); + return !error; + } + + template<class T, class Method> + static bool DispatchDelayReply(const Message* msg, T* obj, Method func) { + SendParam send_params; + Message* reply = GenerateReply(msg); + bool error; + if (ReadSendParam(msg, &send_params)) { + Tuple1<Message&> t = MakeRefTuple(*reply); + ConnectMessageAndReply(msg, reply); + DispatchToMethod(obj, func, send_params, &t); + error = false; + } else { + NOTREACHED() << "Error deserializing message " << msg->type(); + reply->set_reply_error(); + obj->Send(reply); + error = true; + } + return !error; + } + + template<typename TA> + static void WriteReplyParams(Message* reply, TA a) { + ReplyParam p(a); + WriteParam(reply, p); + } + + template<typename TA, typename TB> + static void WriteReplyParams(Message* reply, TA a, TB b) { + ReplyParam p(a, b); + WriteParam(reply, p); + } + + template<typename TA, typename TB, typename TC> + static void WriteReplyParams(Message* reply, TA a, TB b, TC c) { + ReplyParam p(a, b, c); + WriteParam(reply, p); + } + + template<typename TA, typename TB, typename TC, typename TD> + static void WriteReplyParams(Message* reply, TA a, TB b, TC c, TD d) { + ReplyParam p(a, b, c, d); + WriteParam(reply, p); + } + + template<typename TA, typename TB, typename TC, typename TD, typename TE> + static void WriteReplyParams(Message* reply, TA a, TB b, TC c, TD d, TE e) { + ReplyParam p(a, b, c, d, e); + WriteParam(reply, p); + } +}; + +//----------------------------------------------------------------------------- + +} // namespace IPC + +#endif // IPC_IPC_MESSAGE_UTILS_H_ diff --git a/ipc/ipc_message_utils_impl.h b/ipc/ipc_message_utils_impl.h new file mode 100644 index 000000000000..7e512a62c28b --- /dev/null +++ b/ipc/ipc_message_utils_impl.h @@ -0,0 +1,61 @@ +// Copyright (c) 2010 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. +// +// This file contains templates forward declared (but not defined) in +// ipc_message_utils.h so that they are only instantiated in certain files, +// notably a few IPC unit tests. + +#ifndef IPC_IPC_MESSAGE_UTILS_IMPL_H_ +#define IPC_IPC_MESSAGE_UTILS_IMPL_H_ + +namespace IPC { + +template <class ParamType> +MessageWithTuple<ParamType>::MessageWithTuple( + int32 routing_id, uint32 type, const RefParam& p) + : Message(routing_id, type, PRIORITY_NORMAL) { + WriteParam(this, p); +} + +template <class ParamType> +bool MessageWithTuple<ParamType>::Read(const Message* msg, Param* p) { + void* iter = NULL; + if (ReadParam(msg, &iter, p)) + return true; + NOTREACHED() << "Error deserializing message " << msg->type(); + return false; +} + +// We can't migrate the template for Log() to MessageWithTuple, because each +// subclass needs to have Log() to call Read(), which instantiates the above +// template. + +template <class SendParamType, class ReplyParamType> +MessageWithReply<SendParamType, ReplyParamType>::MessageWithReply( + int32 routing_id, uint32 type, + const RefSendParam& send, + const ReplyParam& reply) + : SyncMessage(routing_id, type, PRIORITY_NORMAL, + new ParamDeserializer<ReplyParam>(reply)) +{ + WriteParam(this, send); +} + +template <class SendParamType, class ReplyParamType> +bool MessageWithReply<SendParamType, ReplyParamType>::ReadSendParam( + const Message* msg, SendParam* p) { + void* iter = SyncMessage::GetDataIterator(msg); + return ReadParam(msg, &iter, p); +} + +template <class SendParamType, class ReplyParamType> +bool MessageWithReply<SendParamType, ReplyParamType>::ReadReplyParam( + const Message* msg, typename TupleTypes<ReplyParam>::ValueTuple* p) { + void* iter = SyncMessage::GetDataIterator(msg); + return ReadParam(msg, &iter, p); +} + +} // namespace IPC + +#endif // IPC_IPC_MESSAGE_UTILS_IMPL_H_ diff --git a/ipc/ipc_param_traits.h b/ipc/ipc_param_traits.h new file mode 100644 index 000000000000..e90be18a1b21 --- /dev/null +++ b/ipc/ipc_param_traits.h @@ -0,0 +1,24 @@ +// Copyright (c) 2010 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 IPC_IPC_PARAM_TRAITS_H_ +#define IPC_IPC_PARAM_TRAITS_H_ +#pragma once + +// Our IPC system uses the following partially specialized header to define how +// a data type is read, written and logged in the IPC system. + +namespace IPC { + +template <class P> struct ParamTraits { +}; + +template <class P> +struct SimilarTypeTraits { + typedef P Type; +}; + +} // namespace IPC + +#endif // IPC_IPC_PARAM_TRAITS_H_ diff --git a/ipc/ipc_platform_file.cc b/ipc/ipc_platform_file.cc new file mode 100644 index 000000000000..2b15cebfb885 --- /dev/null +++ b/ipc/ipc_platform_file.cc @@ -0,0 +1,43 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ipc/ipc_platform_file.h" + +namespace IPC { + +PlatformFileForTransit GetFileHandleForProcess(base::PlatformFile handle, + base::ProcessHandle process, + bool close_source_handle) { + IPC::PlatformFileForTransit out_handle; +#if defined(OS_WIN) + DWORD options = DUPLICATE_SAME_ACCESS; + if (close_source_handle) + options |= DUPLICATE_CLOSE_SOURCE; + if (!::DuplicateHandle(::GetCurrentProcess(), + handle, + process, + &out_handle, + 0, + FALSE, + options)) { + out_handle = IPC::InvalidPlatformFileForTransit(); + } +#elif defined(OS_POSIX) + // If asked to close the source, we can simply re-use the source fd instead of + // dup()ing and close()ing. + // When we're not closing the source, we need to duplicate the handle and take + // ownership of that. The reason is that this function is often used to + // generate IPC messages, and the handle must remain valid until it's sent to + // the other process from the I/O thread. Without the dup, calling code might + // close the source handle before the message is sent, creating a race + // condition. + int fd = close_source_handle ? handle : ::dup(handle); + out_handle = base::FileDescriptor(fd, true); +#else + #error Not implemented. +#endif + return out_handle; +} + +} // namespace IPC diff --git a/ipc/ipc_platform_file.h b/ipc/ipc_platform_file.h new file mode 100644 index 000000000000..daaf21cb14b2 --- /dev/null +++ b/ipc/ipc_platform_file.h @@ -0,0 +1,51 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_IPC_PLATFORM_FILE_H_ +#define IPC_IPC_PLATFORM_FILE_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/platform_file.h" +#include "base/process.h" +#include "ipc/ipc_export.h" + +#if defined(OS_POSIX) +#include "base/file_descriptor_posix.h" +#endif + +namespace IPC { + +#if defined(OS_WIN) +typedef base::PlatformFile PlatformFileForTransit; +#elif defined(OS_POSIX) +typedef base::FileDescriptor PlatformFileForTransit; +#endif + +inline PlatformFileForTransit InvalidPlatformFileForTransit() { +#if defined(OS_WIN) + return base::kInvalidPlatformFileValue; +#elif defined(OS_POSIX) + return base::FileDescriptor(); +#endif +} + +inline base::PlatformFile PlatformFileForTransitToPlatformFile( + const PlatformFileForTransit& transit) { +#if defined(OS_WIN) + return transit; +#elif defined(OS_POSIX) + return transit.fd; +#endif +} + +// Returns a file handle equivalent to |file| that can be used in |process|. +IPC_EXPORT PlatformFileForTransit GetFileHandleForProcess( + base::PlatformFile file, + base::ProcessHandle process, + bool close_source_handle); + +} // namespace IPC + +#endif // IPC_IPC_PLATFORM_FILE_H_ diff --git a/ipc/ipc_switches.cc b/ipc/ipc_switches.cc new file mode 100644 index 000000000000..bcb12256e545 --- /dev/null +++ b/ipc/ipc_switches.cc @@ -0,0 +1,24 @@ +// 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 "ipc/ipc_switches.h" + +#include "base/base_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 char kProcessChannelID[] = "channel"; + +// Will add kDebugOnStart to every child processes. If a value is passed, it +// will be used as a filter to determine if the child process should have the +// kDebugOnStart flag passed on or not. +const char kDebugChildren[] = "debug-children"; + +} // namespace switches + diff --git a/ipc/ipc_switches.h b/ipc/ipc_switches.h new file mode 100644 index 000000000000..7c7a833028df --- /dev/null +++ b/ipc/ipc_switches.h @@ -0,0 +1,20 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines all the command-line switches used by the IPC infrastructure. + +#ifndef IPC_IPC_SWITCHES_H_ +#define IPC_IPC_SWITCHES_H_ +#pragma once + +#include "ipc/ipc_export.h" + +namespace switches { + +IPC_EXPORT extern const char kProcessChannelID[]; +IPC_EXPORT extern const char kDebugChildren[]; + +} // namespace switches + +#endif // IPC_IPC_SWITCHES_H_ diff --git a/ipc/ipc_sync_channel.cc b/ipc/ipc_sync_channel.cc new file mode 100644 index 000000000000..c135ef38eb3f --- /dev/null +++ b/ipc/ipc_sync_channel.cc @@ -0,0 +1,513 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ipc/ipc_sync_channel.h" + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/threading/thread_local.h" +#include "base/synchronization/waitable_event.h" +#include "base/synchronization/waitable_event_watcher.h" +#include "ipc/ipc_sync_message.h" + +using base::TimeDelta; +using base::TimeTicks; +using base::WaitableEvent; + +namespace IPC { +// When we're blocked in a Send(), we need to process incoming synchronous +// messages right away because it could be blocking our reply (either +// directly from the same object we're calling, or indirectly through one or +// more other channels). That means that in SyncContext's OnMessageReceived, +// we need to process sync message right away if we're blocked. However a +// simple check isn't sufficient, because the listener thread can be in the +// process of calling Send. +// To work around this, when SyncChannel filters a sync message, it sets +// an event that the listener thread waits on during its Send() call. This +// allows us to dispatch incoming sync messages when blocked. The race +// condition is handled because if Send is in the process of being called, it +// will check the event. In case the listener thread isn't sending a message, +// we queue a task on the listener thread to dispatch the received messages. +// The messages are stored in this queue object that's shared among all +// SyncChannel objects on the same thread (since one object can receive a +// sync message while another one is blocked). + +class SyncChannel::ReceivedSyncMsgQueue : + public base::RefCountedThreadSafe<ReceivedSyncMsgQueue> { + public: + // Returns the ReceivedSyncMsgQueue instance for this thread, creating one + // if necessary. Call RemoveContext on the same thread when done. + static ReceivedSyncMsgQueue* AddContext() { + // We want one ReceivedSyncMsgQueue per listener thread (i.e. since multiple + // SyncChannel objects can block the same thread). + ReceivedSyncMsgQueue* rv = lazy_tls_ptr_.Pointer()->Get(); + if (!rv) { + rv = new ReceivedSyncMsgQueue(); + ReceivedSyncMsgQueue::lazy_tls_ptr_.Pointer()->Set(rv); + } + rv->listener_count_++; + return rv; + } + + // Called on IPC thread when a synchronous message or reply arrives. + void QueueMessage(const Message& msg, SyncChannel::SyncContext* context) { + bool was_task_pending; + { + base::AutoLock auto_lock(message_lock_); + + was_task_pending = task_pending_; + task_pending_ = true; + + // We set the event in case the listener thread is blocked (or is about + // to). In case it's not, the PostTask dispatches the messages. + message_queue_.push_back(QueuedMessage(new Message(msg), context)); + } + + dispatch_event_.Signal(); + if (!was_task_pending) { + listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, + &ReceivedSyncMsgQueue::DispatchMessagesTask, + scoped_refptr<SyncContext>(context))); + } + } + + void QueueReply(const Message &msg, SyncChannel::SyncContext* context) { + received_replies_.push_back(QueuedMessage(new Message(msg), context)); + } + + // Called on the listener's thread to process any queues synchronous + // messages. + void DispatchMessagesTask(SyncContext* context) { + { + base::AutoLock auto_lock(message_lock_); + task_pending_ = false; + } + context->DispatchMessages(); + } + + void DispatchMessages(SyncContext* dispatching_context) { + SyncMessageQueue delayed_queue; + while (true) { + Message* message; + scoped_refptr<SyncChannel::SyncContext> context; + { + base::AutoLock auto_lock(message_lock_); + if (message_queue_.empty()) { + message_queue_ = delayed_queue; + break; + } + + message = message_queue_.front().message; + context = message_queue_.front().context; + message_queue_.pop_front(); + } + if (context->restrict_dispatch() && context != dispatching_context) { + delayed_queue.push_back(QueuedMessage(message, context)); + } else { + context->OnDispatchMessage(*message); + delete message; + } + } + } + + // SyncChannel calls this in its destructor. + void RemoveContext(SyncContext* context) { + base::AutoLock auto_lock(message_lock_); + + SyncMessageQueue::iterator iter = message_queue_.begin(); + while (iter != message_queue_.end()) { + if (iter->context == context) { + delete iter->message; + iter = message_queue_.erase(iter); + } else { + iter++; + } + } + + if (--listener_count_ == 0) { + DCHECK(lazy_tls_ptr_.Pointer()->Get()); + lazy_tls_ptr_.Pointer()->Set(NULL); + } + } + + WaitableEvent* dispatch_event() { return &dispatch_event_; } + base::MessageLoopProxy* listener_message_loop() { + return listener_message_loop_; + } + + // Holds a pointer to the per-thread ReceivedSyncMsgQueue object. + static base::LazyInstance<base::ThreadLocalPointer<ReceivedSyncMsgQueue> > + lazy_tls_ptr_; + + // Called on the ipc thread to check if we can unblock any current Send() + // calls based on a queued reply. + void DispatchReplies() { + for (size_t i = 0; i < received_replies_.size(); ++i) { + Message* message = received_replies_[i].message; + if (received_replies_[i].context->TryToUnblockListener(message)) { + delete message; + received_replies_.erase(received_replies_.begin() + i); + return; + } + } + } + + base::WaitableEventWatcher* top_send_done_watcher() { + return top_send_done_watcher_; + } + + void set_top_send_done_watcher(base::WaitableEventWatcher* watcher) { + top_send_done_watcher_ = watcher; + } + + private: + friend class base::RefCountedThreadSafe<ReceivedSyncMsgQueue>; + + // See the comment in SyncChannel::SyncChannel for why this event is created + // as manual reset. + ReceivedSyncMsgQueue() : + dispatch_event_(true, false), + listener_message_loop_(base::MessageLoopProxy::current()), + task_pending_(false), + listener_count_(0), + top_send_done_watcher_(NULL) { + } + + ~ReceivedSyncMsgQueue() {} + + // Holds information about a queued synchronous message or reply. + struct QueuedMessage { + QueuedMessage(Message* m, SyncContext* c) : message(m), context(c) { } + Message* message; + scoped_refptr<SyncChannel::SyncContext> context; + }; + + typedef std::deque<QueuedMessage> SyncMessageQueue; + SyncMessageQueue message_queue_; + + std::vector<QueuedMessage> received_replies_; + + // Set when we got a synchronous message that we must respond to as the + // sender needs its reply before it can reply to our original synchronous + // message. + WaitableEvent dispatch_event_; + scoped_refptr<base::MessageLoopProxy> listener_message_loop_; + base::Lock message_lock_; + bool task_pending_; + int listener_count_; + + // The current send done event watcher for this thread. Used to maintain + // a local global stack of send done watchers to ensure that nested sync + // message loops complete correctly. + base::WaitableEventWatcher* top_send_done_watcher_; +}; + +base::LazyInstance<base::ThreadLocalPointer<SyncChannel::ReceivedSyncMsgQueue> > + SyncChannel::ReceivedSyncMsgQueue::lazy_tls_ptr_(base::LINKER_INITIALIZED); + +SyncChannel::SyncContext::SyncContext( + Channel::Listener* listener, + base::MessageLoopProxy* ipc_thread, + WaitableEvent* shutdown_event) + : ChannelProxy::Context(listener, ipc_thread), + received_sync_msgs_(ReceivedSyncMsgQueue::AddContext()), + shutdown_event_(shutdown_event), + restrict_dispatch_(false) { +} + +SyncChannel::SyncContext::~SyncContext() { + while (!deserializers_.empty()) + Pop(); +} + +// Adds information about an outgoing sync message to the context so that +// we know how to deserialize the reply. Returns a handle that's set when +// the reply has arrived. +void SyncChannel::SyncContext::Push(SyncMessage* sync_msg) { + // Create the tracking information for this message. This object is stored + // by value since all members are pointers that are cheap to copy. These + // pointers are cleaned up in the Pop() function. + // + // The event is created as manual reset because in between Signal and + // OnObjectSignalled, another Send can happen which would stop the watcher + // from being called. The event would get watched later, when the nested + // Send completes, so the event will need to remain set. + PendingSyncMsg pending(SyncMessage::GetMessageId(*sync_msg), + sync_msg->GetReplyDeserializer(), + new WaitableEvent(true, false)); + base::AutoLock auto_lock(deserializers_lock_); + deserializers_.push_back(pending); +} + +bool SyncChannel::SyncContext::Pop() { + bool result; + { + base::AutoLock auto_lock(deserializers_lock_); + PendingSyncMsg msg = deserializers_.back(); + delete msg.deserializer; + delete msg.done_event; + msg.done_event = NULL; + deserializers_.pop_back(); + result = msg.send_result; + } + + // We got a reply to a synchronous Send() call that's blocking the listener + // thread. However, further down the call stack there could be another + // blocking Send() call, whose reply we received after we made this last + // Send() call. So check if we have any queued replies available that + // can now unblock the listener thread. + ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + received_sync_msgs_.get(), &ReceivedSyncMsgQueue::DispatchReplies)); + + return result; +} + +WaitableEvent* SyncChannel::SyncContext::GetSendDoneEvent() { + base::AutoLock auto_lock(deserializers_lock_); + return deserializers_.back().done_event; +} + +WaitableEvent* SyncChannel::SyncContext::GetDispatchEvent() { + return received_sync_msgs_->dispatch_event(); +} + +void SyncChannel::SyncContext::DispatchMessages() { + received_sync_msgs_->DispatchMessages(this); +} + +bool SyncChannel::SyncContext::TryToUnblockListener(const Message* msg) { + base::AutoLock auto_lock(deserializers_lock_); + if (deserializers_.empty() || + !SyncMessage::IsMessageReplyTo(*msg, deserializers_.back().id)) { + return false; + } + + if (!msg->is_reply_error()) { + deserializers_.back().send_result = deserializers_.back().deserializer-> + SerializeOutputParameters(*msg); + } + deserializers_.back().done_event->Signal(); + + return true; +} + +void SyncChannel::SyncContext::Clear() { + CancelPendingSends(); + received_sync_msgs_->RemoveContext(this); + Context::Clear(); +} + +bool SyncChannel::SyncContext::OnMessageReceived(const Message& msg) { + // Give the filters a chance at processing this message. + if (TryFilters(msg)) + return true; + + if (TryToUnblockListener(&msg)) + return true; + + if (msg.should_unblock()) { + received_sync_msgs_->QueueMessage(msg, this); + return true; + } + + if (msg.is_reply()) { + received_sync_msgs_->QueueReply(msg, this); + return true; + } + + return Context::OnMessageReceivedNoFilter(msg); +} + +void SyncChannel::SyncContext::OnChannelError() { + CancelPendingSends(); + shutdown_watcher_.StopWatching(); + Context::OnChannelError(); +} + +void SyncChannel::SyncContext::OnChannelOpened() { + shutdown_watcher_.StartWatching(shutdown_event_, this); + Context::OnChannelOpened(); +} + +void SyncChannel::SyncContext::OnChannelClosed() { + CancelPendingSends(); + shutdown_watcher_.StopWatching(); + Context::OnChannelClosed(); +} + +void SyncChannel::SyncContext::OnSendTimeout(int message_id) { + base::AutoLock auto_lock(deserializers_lock_); + PendingSyncMessageQueue::iterator iter; + for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) { + if (iter->id == message_id) { + iter->done_event->Signal(); + break; + } + } +} + +void SyncChannel::SyncContext::CancelPendingSends() { + base::AutoLock auto_lock(deserializers_lock_); + PendingSyncMessageQueue::iterator iter; + for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) + iter->done_event->Signal(); +} + +void SyncChannel::SyncContext::OnWaitableEventSignaled(WaitableEvent* event) { + if (event == shutdown_event_) { + // Process shut down before we can get a reply to a synchronous message. + // Cancel pending Send calls, which will end up setting the send done event. + CancelPendingSends(); + } else { + // We got the reply, timed out or the process shutdown. + DCHECK_EQ(GetSendDoneEvent(), event); + MessageLoop::current()->QuitNow(); + } +} + + +SyncChannel::SyncChannel( + const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + Channel::Listener* listener, + base::MessageLoopProxy* ipc_message_loop, + bool create_pipe_now, + WaitableEvent* shutdown_event) + : ChannelProxy( + channel_handle, mode, ipc_message_loop, + new SyncContext(listener, ipc_message_loop, shutdown_event), + create_pipe_now), + sync_messages_with_no_timeout_allowed_(true) { + // Ideally we only want to watch this object when running a nested message + // loop. However, we don't know when it exits if there's another nested + // message loop running under it or not, so we wouldn't know whether to + // stop or keep watching. So we always watch it, and create the event as + // manual reset since the object watcher might otherwise reset the event + // when we're doing a WaitMany. + dispatch_watcher_.StartWatching(sync_context()->GetDispatchEvent(), this); +} + +SyncChannel::~SyncChannel() { +} + +void SyncChannel::SetRestrictDispatchToSameChannel(bool value) { + sync_context()->set_restrict_dispatch(value); +} + +bool SyncChannel::Send(Message* message) { + return SendWithTimeout(message, base::kNoTimeout); +} + +bool SyncChannel::SendWithTimeout(Message* message, int timeout_ms) { + if (!message->is_sync()) { + ChannelProxy::Send(message); + return true; + } + + // *this* might get deleted in WaitForReply. + scoped_refptr<SyncContext> context(sync_context()); + if (context->shutdown_event()->IsSignaled()) { + delete message; + return false; + } + + DCHECK(sync_messages_with_no_timeout_allowed_ || + timeout_ms != base::kNoTimeout); + SyncMessage* sync_msg = static_cast<SyncMessage*>(message); + context->Push(sync_msg); + int message_id = SyncMessage::GetMessageId(*sync_msg); + WaitableEvent* pump_messages_event = sync_msg->pump_messages_event(); + + ChannelProxy::Send(message); + + if (timeout_ms != base::kNoTimeout) { + // We use the sync message id so that when a message times out, we don't + // confuse it with another send that is either above/below this Send in + // the call stack. + context->ipc_message_loop()->PostDelayedTask(FROM_HERE, + NewRunnableMethod(context.get(), + &SyncContext::OnSendTimeout, message_id), timeout_ms); + } + + // Wait for reply, or for any other incoming synchronous messages. + // *this* might get deleted, so only call static functions at this point. + WaitForReply(context, pump_messages_event); + + return context->Pop(); +} + +void SyncChannel::WaitForReply( + SyncContext* context, WaitableEvent* pump_messages_event) { + context->DispatchMessages(); + while (true) { + WaitableEvent* objects[] = { + context->GetDispatchEvent(), + context->GetSendDoneEvent(), + pump_messages_event + }; + + unsigned count = pump_messages_event ? 3: 2; + size_t result = WaitableEvent::WaitMany(objects, count); + if (result == 0 /* dispatch event */) { + // We're waiting for a reply, but we received a blocking synchronous + // call. We must process it or otherwise a deadlock might occur. + context->GetDispatchEvent()->Reset(); + context->DispatchMessages(); + continue; + } + + if (result == 2 /* pump_messages_event */) + WaitForReplyWithNestedMessageLoop(context); // Run a nested message loop. + + break; + } +} + +void SyncChannel::WaitForReplyWithNestedMessageLoop(SyncContext* context) { + base::WaitableEventWatcher send_done_watcher; + + ReceivedSyncMsgQueue* sync_msg_queue = context->received_sync_msgs(); + DCHECK(sync_msg_queue != NULL); + + base::WaitableEventWatcher* old_send_done_event_watcher = + sync_msg_queue->top_send_done_watcher(); + + base::WaitableEventWatcher::Delegate* old_delegate = NULL; + base::WaitableEvent* old_event = NULL; + + // Maintain a local global stack of send done delegates to ensure that + // nested sync calls complete in the correct sequence, i.e. the + // outermost call completes first, etc. + if (old_send_done_event_watcher) { + old_delegate = old_send_done_event_watcher->delegate(); + old_event = old_send_done_event_watcher->GetWatchedEvent(); + old_send_done_event_watcher->StopWatching(); + } + + sync_msg_queue->set_top_send_done_watcher(&send_done_watcher); + + send_done_watcher.StartWatching(context->GetSendDoneEvent(), context); + bool old_state = MessageLoop::current()->NestableTasksAllowed(); + + MessageLoop::current()->SetNestableTasksAllowed(true); + MessageLoop::current()->Run(); + MessageLoop::current()->SetNestableTasksAllowed(old_state); + + sync_msg_queue->set_top_send_done_watcher(old_send_done_event_watcher); + if (old_send_done_event_watcher && old_event) { + old_send_done_event_watcher->StartWatching(old_event, old_delegate); + } +} + +void SyncChannel::OnWaitableEventSignaled(WaitableEvent* event) { + DCHECK(event == sync_context()->GetDispatchEvent()); + // The call to DispatchMessages might delete this object, so reregister + // the object watcher first. + event->Reset(); + dispatch_watcher_.StartWatching(event, this); + sync_context()->DispatchMessages(); +} + +} // namespace IPC diff --git a/ipc/ipc_sync_channel.h b/ipc/ipc_sync_channel.h new file mode 100644 index 000000000000..29844da5629a --- /dev/null +++ b/ipc/ipc_sync_channel.h @@ -0,0 +1,200 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_IPC_SYNC_SENDER_H__ +#define IPC_IPC_SYNC_SENDER_H__ +#pragma once + +#include <string> +#include <deque> + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event_watcher.h" +#include "ipc/ipc_channel_handle.h" +#include "ipc/ipc_channel_proxy.h" +#include "ipc/ipc_sync_message.h" + +namespace base { +class WaitableEvent; +}; + +namespace IPC { + +class SyncMessage; +class MessageReplyDeserializer; + +// This is similar to ChannelProxy, with the added feature of supporting sending +// synchronous messages. +// +// Overview of how the sync channel works +// -------------------------------------- +// When the sending thread sends a synchronous message, we create a bunch +// of tracking info (created in SendWithTimeout, stored in the PendingSyncMsg +// structure) associated with the message that we identify by the unique +// "MessageId" on the SyncMessage. Among the things we save is the +// "Deserializer" which is provided by the sync message. This object is in +// charge of reading the parameters from the reply message and putting them in +// the output variables provided by its caller. +// +// The info gets stashed in a queue since we could have a nested stack of sync +// messages (each side could send sync messages in response to sync messages, +// so it works like calling a function). The message is sent to the I/O thread +// for dispatch and the original thread blocks waiting for the reply. +// +// SyncContext maintains the queue in a threadsafe way and listens for replies +// on the I/O thread. When a reply comes in that matches one of the messages +// it's looking for (using the unique message ID), it will execute the +// deserializer stashed from before, and unblock the original thread. +// +// +// Significant complexity results from the fact that messages are still coming +// in while the original thread is blocked. Normal async messages are queued +// and dispatched after the blocking call is complete. Sync messages must +// be dispatched in a reentrant manner to avoid deadlock. +// +// +// Note that care must be taken that the lifetime of the ipc_thread argument +// is more than this object. If the message loop goes away while this object +// is running and it's used to send a message, then it will use the invalid +// message loop pointer to proxy it to the ipc thread. +class IPC_EXPORT SyncChannel : public ChannelProxy, + public base::WaitableEventWatcher::Delegate { + public: + SyncChannel(const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + Channel::Listener* listener, + base::MessageLoopProxy* ipc_message_loop, + bool create_pipe_now, + base::WaitableEvent* shutdown_event); + virtual ~SyncChannel(); + + virtual bool Send(Message* message); + virtual bool SendWithTimeout(Message* message, int timeout_ms); + + // Whether we allow sending messages with no time-out. + void set_sync_messages_with_no_timeout_allowed(bool value) { + sync_messages_with_no_timeout_allowed_ = value; + } + + // Sets this channel to only dispatch its incoming unblocking messages when it + // is itself blocked on sending a sync message, not when other channels are. + // + // Normally, any unblocking message coming from any channel can be dispatched + // when any (possibly other) channel is blocked on sending a message. This is + // needed in some cases to unblock certain loops (e.g. necessary when some + // processes share a window hierarchy), but may cause re-entrancy issues in + // some cases where such loops are not possible. This flags allows the tagging + // of some particular channels to not re-enter in such cases. + void SetRestrictDispatchToSameChannel(bool value); + + protected: + class ReceivedSyncMsgQueue; + friend class ReceivedSyncMsgQueue; + + // SyncContext holds the per object data for SyncChannel, so that SyncChannel + // can be deleted while it's being used in a different thread. See + // ChannelProxy::Context for more information. + class SyncContext : public Context, + public base::WaitableEventWatcher::Delegate { + public: + SyncContext(Channel::Listener* listener, + base::MessageLoopProxy* ipc_thread, + base::WaitableEvent* shutdown_event); + + // Adds information about an outgoing sync message to the context so that + // we know how to deserialize the reply. + void Push(SyncMessage* sync_msg); + + // Cleanly remove the top deserializer (and throw it away). Returns the + // result of the Send call for that message. + bool Pop(); + + // Returns an event that's set when the send is complete, timed out or the + // process shut down. + base::WaitableEvent* GetSendDoneEvent(); + + // Returns an event that's set when an incoming message that's not the reply + // needs to get dispatched (by calling SyncContext::DispatchMessages). + base::WaitableEvent* GetDispatchEvent(); + + void DispatchMessages(); + + // Checks if the given message is blocking the listener thread because of a + // synchronous send. If it is, the thread is unblocked and true is + // returned. Otherwise the function returns false. + bool TryToUnblockListener(const Message* msg); + + // Called on the IPC thread when a sync send that runs a nested message loop + // times out. + void OnSendTimeout(int message_id); + + base::WaitableEvent* shutdown_event() { return shutdown_event_; } + + ReceivedSyncMsgQueue* received_sync_msgs() { + return received_sync_msgs_; + } + + void set_restrict_dispatch(bool value) { restrict_dispatch_ = value; } + bool restrict_dispatch() const { return restrict_dispatch_; } + + private: + virtual ~SyncContext(); + // ChannelProxy methods that we override. + + // Called on the listener thread. + virtual void Clear(); + + // Called on the IPC thread. + virtual bool OnMessageReceived(const Message& msg); + virtual void OnChannelError(); + virtual void OnChannelOpened(); + virtual void OnChannelClosed(); + + // Cancels all pending Send calls. + void CancelPendingSends(); + + // WaitableEventWatcher::Delegate implementation. + virtual void OnWaitableEventSignaled(base::WaitableEvent* arg); + + typedef std::deque<PendingSyncMsg> PendingSyncMessageQueue; + PendingSyncMessageQueue deserializers_; + base::Lock deserializers_lock_; + + scoped_refptr<ReceivedSyncMsgQueue> received_sync_msgs_; + + base::WaitableEvent* shutdown_event_; + base::WaitableEventWatcher shutdown_watcher_; + bool restrict_dispatch_; + }; + + private: + // WaitableEventWatcher::Delegate implementation. + virtual void OnWaitableEventSignaled(base::WaitableEvent* arg); + + SyncContext* sync_context() { + return reinterpret_cast<SyncContext*>(context()); + } + + // Both these functions wait for a reply, timeout or process shutdown. The + // latter one also runs a nested message loop in the meantime. + static void WaitForReply( + SyncContext* context, base::WaitableEvent* pump_messages_event); + + // Runs a nested message loop until a reply arrives, times out, or the process + // shuts down. + static void WaitForReplyWithNestedMessageLoop(SyncContext* context); + + bool sync_messages_with_no_timeout_allowed_; + + // Used to signal events between the IPC and listener threads. + base::WaitableEventWatcher dispatch_watcher_; + + DISALLOW_COPY_AND_ASSIGN(SyncChannel); +}; + +} // namespace IPC + +#endif // IPC_IPC_SYNC_SENDER_H__ diff --git a/ipc/ipc_sync_message.cc b/ipc/ipc_sync_message.cc new file mode 100644 index 000000000000..3a36bf327160 --- /dev/null +++ b/ipc/ipc_sync_message.cc @@ -0,0 +1,130 @@ +// Copyright (c) 2010 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 "build/build_config.h" + +#if defined(OS_WIN) +#include <windows.h> +#endif +#include <stack> + +#include "base/atomic_sequence_num.h" +//#include "base/logging.h" +#include "log.h" +#include "base/synchronization/waitable_event.h" +#include "ipc/ipc_sync_message.h" + +namespace IPC { + +#define kSyncMessageHeaderSize 4 + +static base::AtomicSequenceNumber g_next_id(base::LINKER_INITIALIZED); + + static base::WaitableEvent* dummy_event = NULL; //new base::WaitableEvent(true, true); + +SyncMessage::SyncMessage( + int32 routing_id, + uint32 type, + PriorityValue priority, + MessageReplyDeserializer* deserializer) + : Message(routing_id, type, priority), + deserializer_(deserializer), + pump_messages_event_(NULL) + { + set_sync(); + set_unblock(true); + + + // Add synchronous message data before the message payload. + SyncHeader header; + header.message_id = g_next_id.GetNext(); + WriteSyncHeader(this, header); +} + +MessageReplyDeserializer* SyncMessage::GetReplyDeserializer() { + MessageReplyDeserializer* rv = deserializer_; + DCHECK(rv); + deserializer_ = NULL; + return rv; +} + +void SyncMessage::EnableMessagePumping() { + DCHECK(!pump_messages_event_); + set_pump_messages_event(dummy_event); +} + +bool SyncMessage::IsMessageReplyTo(const Message& msg, int request_id) { + if (!msg.is_reply()) + return false; + + return GetMessageId(msg) == request_id; +} + +void* SyncMessage::GetDataIterator(const Message* msg) { + void* iter = const_cast<char*>(msg->payload()); + UpdateIter(&iter, kSyncMessageHeaderSize); + return iter; +} + +int SyncMessage::GetMessageId(const Message& msg) { + if (!msg.is_sync() && !msg.is_reply()) + return 0; + + SyncHeader header; + if (!ReadSyncHeader(msg, &header)) + return 0; + + return header.message_id; +} + +Message* SyncMessage::GenerateReply(const Message* msg) { + DCHECK(msg->is_sync()) << "is_sync(" << msg->is_sync() <<")"<<std::endl; + + Message* reply = new Message(msg->routing_id(), IPC_REPLY_ID, + msg->priority()); + reply->set_reply(); + + SyncHeader header; + + // use the same message id, but this time reply bit is set + header.message_id = GetMessageId(*msg); + WriteSyncHeader(reply, header); + + return reply; +} + +bool SyncMessage::ReadSyncHeader(const Message& msg, SyncHeader* header) { + DCHECK(msg.is_sync() || msg.is_reply()); + + void* iter = NULL; + bool result = msg.ReadInt(&iter, &header->message_id); + if (!result) { + NOTREACHED(); + return false; + } + + return true; +} + +bool SyncMessage::WriteSyncHeader(Message* msg, const SyncHeader& header) { + DCHECK(msg->is_sync() || msg->is_reply()); + DCHECK(msg->payload_size() == 0); + bool result = msg->WriteInt(header.message_id); + if (!result) { + NOTREACHED(); + return false; + } + + // Note: if you add anything here, you need to update kSyncMessageHeaderSize. + DCHECK(kSyncMessageHeaderSize == msg->payload_size()); + + return true; +} + + +bool MessageReplyDeserializer::SerializeOutputParameters(const Message& msg) { + return SerializeOutputParameters(msg, SyncMessage::GetDataIterator(&msg)); +} + +} // namespace IPC diff --git a/ipc/ipc_sync_message.h b/ipc/ipc_sync_message.h new file mode 100644 index 000000000000..2c6ed8b7c483 --- /dev/null +++ b/ipc/ipc_sync_message.h @@ -0,0 +1,109 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_IPC_SYNC_MESSAGE_H_ +#define IPC_IPC_SYNC_MESSAGE_H_ +#pragma once + +#if defined(OS_WIN) +#include <windows.h> +#endif +#include <string> +#include "base/basictypes.h" +#include "ipc/ipc_message.h" + +namespace base { +class WaitableEvent; +} + +namespace IPC { + +class MessageReplyDeserializer; + +class IPC_EXPORT SyncMessage : public Message { + public: + SyncMessage(int32 routing_id, uint32 type, PriorityValue priority, + MessageReplyDeserializer* deserializer); + + // Call this to get a deserializer for the output parameters. + // Note that this can only be called once, and the caller is responsible + // for deleting the deserializer when they're done. + MessageReplyDeserializer* GetReplyDeserializer(); + + // If this message can cause the receiver to block while waiting for user + // input (i.e. by calling MessageBox), then the caller needs to pump window + // messages and dispatch asynchronous messages while waiting for the reply. + // If this event is passed in, then window messages will start being pumped + // when it's set. Note that this behavior will continue even if the event is + // later reset. The event must be valid until after the Send call returns. + void set_pump_messages_event(base::WaitableEvent* event) { + pump_messages_event_ = event; + if (event) { + header()->flags |= PUMPING_MSGS_BIT; + } else { + header()->flags &= ~PUMPING_MSGS_BIT; + } + } + + // Call this if you always want to pump messages. You can call this method + // or set_pump_messages_event but not both. + void EnableMessagePumping(); + + base::WaitableEvent* pump_messages_event() const { + return pump_messages_event_; + } + + // Returns true if the message is a reply to the given request id. + static bool IsMessageReplyTo(const Message& msg, int request_id); + + // Given a reply message, returns an iterator to the beginning of the data + // (i.e. skips over the synchronous specific data). + static void* GetDataIterator(const Message* msg); + + // Given a synchronous message (or its reply), returns its id. + static int GetMessageId(const Message& msg); + + // Generates a reply message to the given message. + static Message* GenerateReply(const Message* msg); + + private: + struct SyncHeader { + // unique ID (unique per sender) + int message_id; + }; + + static bool ReadSyncHeader(const Message& msg, SyncHeader* header); + static bool WriteSyncHeader(Message* msg, const SyncHeader& header); + + MessageReplyDeserializer* deserializer_; + base::WaitableEvent* pump_messages_event_; +}; + +// Used to deserialize parameters from a reply to a synchronous message +class IPC_EXPORT MessageReplyDeserializer { + public: + virtual ~MessageReplyDeserializer() {} + bool SerializeOutputParameters(const Message& msg); + private: + // Derived classes need to implement this, using the given iterator (which + // is skipped past the header for synchronous messages). + virtual bool SerializeOutputParameters(const Message& msg, void* iter) = 0; +}; + +// When sending a synchronous message, this structure contains an object +// that knows how to deserialize the response. +struct PendingSyncMsg { + PendingSyncMsg(int id, + MessageReplyDeserializer* d, + base::WaitableEvent* e) + : id(id), deserializer(d), done_event(e), send_result(false) { } + int id; + MessageReplyDeserializer* deserializer; + base::WaitableEvent* done_event; + bool send_result; +}; + +} // namespace IPC + +#endif // IPC_IPC_SYNC_MESSAGE_H_ diff --git a/ipc/ipc_sync_message_filter.cc b/ipc/ipc_sync_message_filter.cc new file mode 100644 index 000000000000..cffbaa674aed --- /dev/null +++ b/ipc/ipc_sync_message_filter.cc @@ -0,0 +1,127 @@ +// Copyright (c) 2010 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 "ipc/ipc_sync_message_filter.h" + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/synchronization/waitable_event.h" +#include "ipc/ipc_sync_message.h" + +namespace IPC { + +SyncMessageFilter::SyncMessageFilter(base::WaitableEvent* shutdown_event) + : channel_(NULL), + listener_loop_(MessageLoop::current()), + io_loop_(NULL), + shutdown_event_(shutdown_event) { +} + +SyncMessageFilter::~SyncMessageFilter() { +} + +bool SyncMessageFilter::Send(Message* message) { + { + base::AutoLock auto_lock(lock_); + if (!io_loop_) { + delete message; + return false; + } + } + + if (!message->is_sync()) { + io_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &SyncMessageFilter::SendOnIOThread, message)); + return true; + } + + base::WaitableEvent done_event(true, false); + PendingSyncMsg pending_message( + SyncMessage::GetMessageId(*message), + reinterpret_cast<SyncMessage*>(message)->GetReplyDeserializer(), + &done_event); + + { + base::AutoLock auto_lock(lock_); + // Can't use this class on the main thread or else it can lead to deadlocks. + // Also by definition, can't use this on IO thread since we're blocking it. + DCHECK(MessageLoop::current() != listener_loop_); + DCHECK(MessageLoop::current() != io_loop_); + pending_sync_messages_.insert(&pending_message); + } + + io_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &SyncMessageFilter::SendOnIOThread, message)); + + base::WaitableEvent* events[2] = { shutdown_event_, &done_event }; + base::WaitableEvent::WaitMany(events, 2); + + { + base::AutoLock auto_lock(lock_); + delete pending_message.deserializer; + pending_sync_messages_.erase(&pending_message); + } + + return pending_message.send_result; +} + +void SyncMessageFilter::SendOnIOThread(Message* message) { + if (channel_) { + channel_->Send(message); + return; + } + + if (message->is_sync()) { + // We don't know which thread sent it, but it doesn't matter, just signal + // them all. + SignalAllEvents(); + } + + delete message; +} + +void SyncMessageFilter::SignalAllEvents() { + base::AutoLock auto_lock(lock_); + for (PendingSyncMessages::iterator iter = pending_sync_messages_.begin(); + iter != pending_sync_messages_.end(); ++iter) { + (*iter)->done_event->Signal(); + } +} + +void SyncMessageFilter::OnFilterAdded(Channel* channel) { + channel_ = channel; + base::AutoLock auto_lock(lock_); + io_loop_ = MessageLoop::current(); +} + +void SyncMessageFilter::OnChannelError() { + channel_ = NULL; + SignalAllEvents(); +} + +void SyncMessageFilter::OnChannelClosing() { + channel_ = NULL; + SignalAllEvents(); +} + +bool SyncMessageFilter::OnMessageReceived(const Message& message) { + base::AutoLock auto_lock(lock_); + for (PendingSyncMessages::iterator iter = pending_sync_messages_.begin(); + iter != pending_sync_messages_.end(); ++iter) { + if (SyncMessage::IsMessageReplyTo(message, (*iter)->id)) { + if (!message.is_reply_error()) { + (*iter)->send_result = + (*iter)->deserializer->SerializeOutputParameters(message); + } + (*iter)->done_event->Signal(); + return true; + } + } + + return false; +} + +} // namespace IPC diff --git a/ipc/ipc_sync_message_filter.h b/ipc/ipc_sync_message_filter.h new file mode 100644 index 000000000000..0dc471adacd3 --- /dev/null +++ b/ipc/ipc_sync_message_filter.h @@ -0,0 +1,70 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_IPC_SYNC_MESSAGE_FILTER_H_ +#define IPC_IPC_SYNC_MESSAGE_FILTER_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "ipc/ipc_channel_proxy.h" +#include "ipc/ipc_sync_message.h" +#include <set> + +namespace base { +class WaitableEvent; +} + +class MessageLoop; + +namespace IPC { + +class MessageReplyDeserializer; + +// This MessageFilter allows sending synchronous IPC messages from a thread +// other than the listener thread associated with the SyncChannel. It does not +// support fancy features that SyncChannel does, such as handling recursion or +// receiving messages while waiting for a response. Note that this object can +// be used to send simultaneous synchronous messages from different threads. +class IPC_EXPORT SyncMessageFilter : public ChannelProxy::MessageFilter, + public Message::Sender { + public: + explicit SyncMessageFilter(base::WaitableEvent* shutdown_event); + virtual ~SyncMessageFilter(); + + // Message::Sender implementation. + virtual bool Send(Message* message); + + // ChannelProxy::MessageFilter implementation. + virtual void OnFilterAdded(Channel* channel); + virtual void OnChannelError(); + virtual void OnChannelClosing(); + virtual bool OnMessageReceived(const Message& message); + + private: + void SendOnIOThread(Message* message); + // Signal all the pending sends as done, used in an error condition. + void SignalAllEvents(); + + // The channel to which this filter was added. + Channel* channel_; + + MessageLoop* listener_loop_; // The process's main thread. + MessageLoop* io_loop_; // The message loop where the Channel lives. + + typedef std::set<PendingSyncMsg*> PendingSyncMessages; + PendingSyncMessages pending_sync_messages_; + + // Locks data members above. + base::Lock lock_; + + base::WaitableEvent* shutdown_event_; + + DISALLOW_COPY_AND_ASSIGN(SyncMessageFilter); +}; + +} // namespace IPC + +#endif // IPC_IPC_SYNC_MESSAGE_FILTER_H_ diff --git a/ipc/param_traits_log_macros.h b/ipc/param_traits_log_macros.h new file mode 100644 index 000000000000..8259a0d0a0bd --- /dev/null +++ b/ipc/param_traits_log_macros.h @@ -0,0 +1,52 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_PARAM_TRAITS_LOG_MACROS_H_ +#define IPC_PARAM_TRAITS_LOG_MACROS_H_ +#pragma once + +#include <string> + +// Null out all the macros that need nulling. +#include "ipc/ipc_message_null_macros.h" + +// STRUCT declarations cause corresponding STRUCT_TRAITS declarations to occur. +#undef IPC_STRUCT_BEGIN +#undef IPC_STRUCT_MEMBER +#undef IPC_STRUCT_END +#define IPC_STRUCT_BEGIN(struct_name) IPC_STRUCT_TRAITS_BEGIN(struct_name) +#define IPC_STRUCT_MEMBER(type, name) IPC_STRUCT_TRAITS_MEMBER(name) +#define IPC_STRUCT_END() IPC_STRUCT_TRAITS_END() + +// Set up so next include will generate log methods. +#undef IPC_STRUCT_TRAITS_BEGIN +#undef IPC_STRUCT_TRAITS_MEMBER +#undef IPC_STRUCT_TRAITS_PARENT +#undef IPC_STRUCT_TRAITS_END +#define IPC_STRUCT_TRAITS_BEGIN(struct_name) \ + void ParamTraits<struct_name>::Log(const param_type& p, std::string* l) { \ + bool needs_comma = false; \ + l->append("("); +#define IPC_STRUCT_TRAITS_MEMBER(name) \ + if (needs_comma) \ + l->append(", "); \ + LogParam(p.name, l); \ + needs_comma = true; +#define IPC_STRUCT_TRAITS_PARENT(type) \ + if (needs_comma) \ + l->append(", "); \ + ParamTraits<type>::Log(p, l); \ + needs_comma = true; +#define IPC_STRUCT_TRAITS_END() \ + l->append(")"); \ + } + +#undef IPC_ENUM_TRAITS +#define IPC_ENUM_TRAITS(enum_name) \ + void ParamTraits<enum_name>::Log(const param_type& p, std::string* l) { \ + LogParam(static_cast<int>(p), l); \ + } + +#endif // IPC_PARAM_TRAITS_LOG_MACROS_H_ + diff --git a/ipc/param_traits_macros.h b/ipc/param_traits_macros.h new file mode 100644 index 000000000000..df57adfb32e0 --- /dev/null +++ b/ipc/param_traits_macros.h @@ -0,0 +1,39 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_PARAM_TRAITS_MACROS_H_ +#define IPC_PARAM_TRAITS_MACROS_H_ + +#include <string> + +// Traits generation for structs. +#define IPC_STRUCT_TRAITS_BEGIN(struct_name) \ + namespace IPC { \ + template <> \ + struct ParamTraits<struct_name> { \ + typedef struct_name param_type; \ + static void Write(Message* m, const param_type& p); \ + static bool Read(const Message* m, void** iter, param_type* p); \ + static void Log(const param_type& p, std::string* l); \ + }; \ + } + +#define IPC_STRUCT_TRAITS_MEMBER(name) +#define IPC_STRUCT_TRAITS_PARENT(type) +#define IPC_STRUCT_TRAITS_END() + +// Traits generation for enums. +#define IPC_ENUM_TRAITS(enum_name) \ + namespace IPC { \ + template <> \ + struct ParamTraits<enum_name> { \ + typedef enum_name param_type; \ + static void Write(Message* m, const param_type& p); \ + static bool Read(const Message* m, void** iter, param_type* p); \ + static void Log(const param_type& p, std::string* l); \ + }; \ + } + +#endif // IPC_PARAM_TRAITS_MACROS_H_ + diff --git a/ipc/param_traits_read_macros.h b/ipc/param_traits_read_macros.h new file mode 100644 index 000000000000..f0b88bc7ebe3 --- /dev/null +++ b/ipc/param_traits_read_macros.h @@ -0,0 +1,45 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_PARAM_TRAITS_READ_MACROS_H_ +#define IPC_PARAM_TRAITS_READ_MACROS_H_ +#pragma once + +// Null out all the macros that need nulling. +#include "ipc/ipc_message_null_macros.h" + +// STRUCT declarations cause corresponding STRUCT_TRAITS declarations to occur. +#undef IPC_STRUCT_BEGIN +#undef IPC_STRUCT_MEMBER +#undef IPC_STRUCT_END +#define IPC_STRUCT_BEGIN(struct_name) IPC_STRUCT_TRAITS_BEGIN(struct_name) +#define IPC_STRUCT_MEMBER(type, name) IPC_STRUCT_TRAITS_MEMBER(name) +#define IPC_STRUCT_END() IPC_STRUCT_TRAITS_END() + +// Set up so next include will generate read methods. +#undef IPC_STRUCT_TRAITS_BEGIN +#undef IPC_STRUCT_TRAITS_MEMBER +#undef IPC_STRUCT_TRAITS_PARENT +#undef IPC_STRUCT_TRAITS_END +#define IPC_STRUCT_TRAITS_BEGIN(struct_name) \ + bool ParamTraits<struct_name>:: \ + Read(const Message* m, void** iter, param_type* p) { \ + return +#define IPC_STRUCT_TRAITS_MEMBER(name) ReadParam(m, iter, &p->name) && +#define IPC_STRUCT_TRAITS_PARENT(type) ParamTraits<type>::Read(m, iter, p) && +#define IPC_STRUCT_TRAITS_END() 1; } + +#undef IPC_ENUM_TRAITS +#define IPC_ENUM_TRAITS(enum_name) \ + bool ParamTraits<enum_name>:: \ + Read(const Message* m, void** iter, param_type* p) { \ + int type; \ + if (!m->ReadInt(iter, &type)) \ + return false; \ + *p = static_cast<param_type>(type); \ + return true; \ + } + +#endif // IPC_PARAM_TRAITS_READ_MACROS_H_ + diff --git a/ipc/param_traits_write_macros.h b/ipc/param_traits_write_macros.h new file mode 100644 index 000000000000..844c3855eb72 --- /dev/null +++ b/ipc/param_traits_write_macros.h @@ -0,0 +1,38 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_PARAM_TRAITS_WRITE_MACROS_H_ +#define IPC_PARAM_TRAITS_WRITE_MACROS_H_ +#pragma once + +// Null out all the macros that need nulling. +#include "ipc/ipc_message_null_macros.h" + +// STRUCT declarations cause corresponding STRUCT_TRAITS declarations to occur. +#undef IPC_STRUCT_BEGIN +#undef IPC_STRUCT_MEMBER +#undef IPC_STRUCT_END +#define IPC_STRUCT_BEGIN(struct_name) IPC_STRUCT_TRAITS_BEGIN(struct_name) +#define IPC_STRUCT_MEMBER(type, name) IPC_STRUCT_TRAITS_MEMBER(name) +#define IPC_STRUCT_END() IPC_STRUCT_TRAITS_END() + +// Set up so next include will generate write methods. +#undef IPC_STRUCT_TRAITS_BEGIN +#undef IPC_STRUCT_TRAITS_MEMBER +#undef IPC_STRUCT_TRAITS_PARENT +#undef IPC_STRUCT_TRAITS_END +#define IPC_STRUCT_TRAITS_BEGIN(struct_name) \ + void ParamTraits<struct_name>::Write(Message* m, const param_type& p) { +#define IPC_STRUCT_TRAITS_MEMBER(name) WriteParam(m, p.name); +#define IPC_STRUCT_TRAITS_PARENT(type) ParamTraits<type>::Write(m, p); +#define IPC_STRUCT_TRAITS_END() } + +#undef IPC_ENUM_TRAITS +#define IPC_ENUM_TRAITS(enum_name) \ + void ParamTraits<enum_name>::Write(Message* m, const param_type& p) { \ + m->WriteInt(static_cast<int>(p)); \ + } + +#endif // IPC_PARAM_TRAITS_WRITE_MACROS_H_ + diff --git a/ipc/struct_constructor_macros.h b/ipc/struct_constructor_macros.h new file mode 100644 index 000000000000..67bfcfb6e2ae --- /dev/null +++ b/ipc/struct_constructor_macros.h @@ -0,0 +1,20 @@ +// Copyright (c) 2010 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 IPC_STRUCT_CONSTRUCTOR_MACROS_H_ +#define IPC_STRUCT_CONSTRUCTOR_MACROS_H_ + +// Null out all the macros that need nulling. +#include "ipc/ipc_message_null_macros.h" + +// Set up so next include will generate constructors. +#undef IPC_STRUCT_BEGIN +#undef IPC_STRUCT_MEMBER +#undef IPC_STRUCT_END +#define IPC_STRUCT_BEGIN(struct_name) struct_name::struct_name() : NoParams() +#define IPC_STRUCT_MEMBER(type, name) , name() +#define IPC_STRUCT_END() {} + +#endif // IPC_STRUCT_CONSTRUCTOR_MACROS_H_ + diff --git a/ipc/struct_destructor_macros.h b/ipc/struct_destructor_macros.h new file mode 100644 index 000000000000..bf3dc953bcb6 --- /dev/null +++ b/ipc/struct_destructor_macros.h @@ -0,0 +1,16 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IPC_STRUCT_DESTRUCTOR_MACROS_H_ +#define IPC_STRUCT_DESTRUCTOR_MACROS_H_ + +// Null out all the macros that need nulling. +#include "ipc/ipc_message_null_macros.h" + +// Set up so next include will generate destructors. +#undef IPC_STRUCT_BEGIN +#define IPC_STRUCT_BEGIN(struct_name) struct_name::~struct_name() {} + +#endif // IPC_STRUCT_DESTRUCTOR_MACROS_H_ + diff --git a/ipc/sync_socket_unittest.cc b/ipc/sync_socket_unittest.cc new file mode 100644 index 000000000000..1a4ae13d89cd --- /dev/null +++ b/ipc/sync_socket_unittest.cc @@ -0,0 +1,250 @@ +// Copyright (c) 2010 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 "base/sync_socket.h" + +#include <stdio.h> +#include <string> +#include <sstream> + +#include "base/message_loop.h" +#include "base/process_util.h" +#include "build/build_config.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_channel_proxy.h" +#include "ipc/ipc_message_macros.h" +#include "ipc/ipc_message_utils.h" +#include "ipc/ipc_message_utils_impl.h" +#include "ipc/ipc_tests.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/multiprocess_func_list.h" + +#if defined(OS_POSIX) +#include "base/file_descriptor_posix.h" +#endif + +enum IPCMessageIds { + UNUSED_IPC_TYPE, + SERVER_FIRST_IPC_TYPE, // SetHandle message sent to server. + SERVER_SECOND_IPC_TYPE, // Shutdown message sent to server. + CLIENT_FIRST_IPC_TYPE // Response message sent to client. +}; + +namespace { +const char kHelloString[] = "Hello, SyncSocket Client"; +const size_t kHelloStringLength = arraysize(kHelloString); +} // namespace + +// Message class to pass a base::SyncSocket::Handle to another process. +// This is not as easy as it sounds, because of the differences in transferring +// Windows HANDLEs versus posix file descriptors. +#if defined(OS_WIN) +class MsgClassSetHandle + : public IPC::MessageWithTuple< Tuple1<base::SyncSocket::Handle> > { + public: + enum { ID = SERVER_FIRST_IPC_TYPE }; + explicit MsgClassSetHandle(const base::SyncSocket::Handle arg1) + : IPC::MessageWithTuple< Tuple1<base::SyncSocket::Handle> >( + MSG_ROUTING_CONTROL, ID, MakeRefTuple(arg1)) {} + + private: + DISALLOW_COPY_AND_ASSIGN(MsgClassSetHandle); +}; +#elif defined(OS_POSIX) +class MsgClassSetHandle + : public IPC::MessageWithTuple< Tuple1<base::FileDescriptor> > { + public: + enum { ID = SERVER_FIRST_IPC_TYPE }; + explicit MsgClassSetHandle(const base::FileDescriptor& arg1) + : IPC::MessageWithTuple< Tuple1<base::FileDescriptor> >( + MSG_ROUTING_CONTROL, ID, MakeRefTuple(arg1)) {} + + private: + DISALLOW_COPY_AND_ASSIGN(MsgClassSetHandle); +}; +#else +# error "What platform?" +#endif // defined(OS_WIN) + +// Message class to pass a response to the server. +class MsgClassResponse + : public IPC::MessageWithTuple< Tuple1<std::string> > { + public: + enum { ID = CLIENT_FIRST_IPC_TYPE }; + explicit MsgClassResponse(const std::string& arg1) + : IPC::MessageWithTuple< Tuple1<std::string> >( + MSG_ROUTING_CONTROL, ID, MakeRefTuple(arg1)) {} + + private: + DISALLOW_COPY_AND_ASSIGN(MsgClassResponse); +}; + +// Message class to tell the server to shut down. +class MsgClassShutdown + : public IPC::MessageWithTuple< Tuple0 > { + public: + enum { ID = SERVER_SECOND_IPC_TYPE }; + MsgClassShutdown() + : IPC::MessageWithTuple< Tuple0 >( + MSG_ROUTING_CONTROL, ID, MakeTuple()) {} + + private: + DISALLOW_COPY_AND_ASSIGN(MsgClassShutdown); +}; + +// The SyncSocket server listener class processes two sorts of +// messages from the client. +class SyncSocketServerListener : public IPC::Channel::Listener { + public: + SyncSocketServerListener() : chan_(NULL) { + } + + void Init(IPC::Channel* chan) { + chan_ = chan; + } + + virtual bool OnMessageReceived(const IPC::Message& msg) { + if (msg.routing_id() == MSG_ROUTING_CONTROL) { + IPC_BEGIN_MESSAGE_MAP(SyncSocketServerListener, msg) + IPC_MESSAGE_HANDLER(MsgClassSetHandle, OnMsgClassSetHandle) + IPC_MESSAGE_HANDLER(MsgClassShutdown, OnMsgClassShutdown) + IPC_END_MESSAGE_MAP() + } + return true; + } + + private: + // This sort of message is sent first, causing the transfer of + // the handle for the SyncSocket. This message sends a buffer + // on the SyncSocket and then sends a response to the client. +#if defined(OS_WIN) + void OnMsgClassSetHandle(const base::SyncSocket::Handle handle) { + SetHandle(handle); + } +#elif defined(OS_POSIX) + void OnMsgClassSetHandle(const base::FileDescriptor& fd_struct) { + SetHandle(fd_struct.fd); + } +#else +# error "What platform?" +#endif // defined(OS_WIN) + + void SetHandle(base::SyncSocket::Handle handle) { + base::SyncSocket sync_socket(handle); + EXPECT_EQ(sync_socket.Send(static_cast<const void*>(kHelloString), + kHelloStringLength), kHelloStringLength); + IPC::Message* msg = new MsgClassResponse(kHelloString); + EXPECT_TRUE(chan_->Send(msg)); + } + + // When the client responds, it sends back a shutdown message, + // which causes the message loop to exit. + void OnMsgClassShutdown() { + MessageLoop::current()->Quit(); + } + + IPC::Channel* chan_; + + DISALLOW_COPY_AND_ASSIGN(SyncSocketServerListener); +}; + +// Runs the fuzzing server child mode. Returns when the preset number +// of messages have been received. +MULTIPROCESS_TEST_MAIN(RunSyncSocketServer) { + MessageLoopForIO main_message_loop; + SyncSocketServerListener listener; + IPC::Channel chan(kSyncSocketChannel, IPC::Channel::MODE_CLIENT, &listener); + EXPECT_TRUE(chan.Connect()); + listener.Init(&chan); + MessageLoop::current()->Run(); + return 0; +} + +// The SyncSocket client listener only processes one sort of message, +// a response from the server. +class SyncSocketClientListener : public IPC::Channel::Listener { + public: + SyncSocketClientListener() { + } + + void Init(base::SyncSocket* socket, IPC::Channel* chan) { + socket_ = socket; + chan_ = chan; + } + + virtual bool OnMessageReceived(const IPC::Message& msg) { + if (msg.routing_id() == MSG_ROUTING_CONTROL) { + IPC_BEGIN_MESSAGE_MAP(SyncSocketClientListener, msg) + IPC_MESSAGE_HANDLER(MsgClassResponse, OnMsgClassResponse) + IPC_END_MESSAGE_MAP() + } + return true; + } + + private: + // When a response is received from the server, it sends the same + // string as was written on the SyncSocket. These are compared + // and a shutdown message is sent back to the server. + void OnMsgClassResponse(const std::string& str) { + // We rely on the order of sync_socket.Send() and chan_->Send() in + // the SyncSocketServerListener object. + EXPECT_EQ(kHelloStringLength, socket_->Peek()); + char buf[kHelloStringLength]; + socket_->Receive(static_cast<void*>(buf), kHelloStringLength); + EXPECT_EQ(strcmp(str.c_str(), buf), 0); + // After receiving from the socket there should be no bytes left. + EXPECT_EQ(0U, socket_->Peek()); + IPC::Message* msg = new MsgClassShutdown(); + EXPECT_TRUE(chan_->Send(msg)); + MessageLoop::current()->Quit(); + } + + base::SyncSocket* socket_; + IPC::Channel* chan_; + + DISALLOW_COPY_AND_ASSIGN(SyncSocketClientListener); +}; + +class SyncSocketTest : public IPCChannelTest { +}; + +TEST_F(SyncSocketTest, SanityTest) { + SyncSocketClientListener listener; + IPC::Channel chan(kSyncSocketChannel, IPC::Channel::MODE_SERVER, + &listener); + base::ProcessHandle server_process = SpawnChild(SYNC_SOCKET_SERVER, &chan); + ASSERT_TRUE(server_process); + // Create a pair of SyncSockets. + base::SyncSocket* pair[2]; + base::SyncSocket::CreatePair(pair); + // Immediately after creation there should be no pending bytes. + EXPECT_EQ(0U, pair[0]->Peek()); + EXPECT_EQ(0U, pair[1]->Peek()); + base::SyncSocket::Handle target_handle; + // Connect the channel and listener. + ASSERT_TRUE(chan.Connect()); + listener.Init(pair[0], &chan); +#if defined(OS_WIN) + // On windows we need to duplicate the handle into the server process. + BOOL retval = DuplicateHandle(GetCurrentProcess(), pair[1]->handle(), + server_process, &target_handle, + 0, FALSE, DUPLICATE_SAME_ACCESS); + EXPECT_TRUE(retval); + // Set up a message to pass the handle to the server. + IPC::Message* msg = new MsgClassSetHandle(target_handle); +#else + target_handle = pair[1]->handle(); + // Set up a message to pass the handle to the server. + base::FileDescriptor filedesc(target_handle, false); + IPC::Message* msg = new MsgClassSetHandle(filedesc); +#endif // defined(OS_WIN) + EXPECT_TRUE(chan.Send(msg)); + // Use the current thread as the I/O thread. + MessageLoop::current()->Run(); + // Shut down. + delete pair[0]; + delete pair[1]; + EXPECT_TRUE(base::WaitForSingleProcess(server_process, 5000)); + base::CloseProcessHandle(server_process); +} |