diff options
Diffstat (limited to 'chrome/common')
-rw-r--r-- | chrome/common/ipc_channel_handle.h | 45 | ||||
-rw-r--r-- | chrome/common/ipc_channel_posix.cc | 87 | ||||
-rw-r--r-- | chrome/common/ipc_channel_posix.h | 18 | ||||
-rw-r--r-- | chrome/common/ipc_message_utils.h | 29 | ||||
-rw-r--r-- | chrome/common/plugin_messages_internal.h | 15 | ||||
-rw-r--r-- | chrome/common/render_messages_internal.h | 11 |
6 files changed, 170 insertions, 35 deletions
diff --git a/chrome/common/ipc_channel_handle.h b/chrome/common/ipc_channel_handle.h new file mode 100644 index 0000000..2bb6380 --- /dev/null +++ b/chrome/common/ipc_channel_handle.h @@ -0,0 +1,45 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_IPC_CHANNEL_HANDLE_H_ +#define CHROME_COMMON_IPC_CHANNEL_HANDLE_H_ + +#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. + std::string name; +#if defined(OS_POSIX) + base::FileDescriptor socket; +#endif + + ChannelHandle() {} +#if defined(OS_POSIX) + ChannelHandle(const std::string& n, const base::FileDescriptor& s) + : name(n), socket(s) {} +#else + ChannelHandle(const std::string& n) : name(n) {} +#endif +}; + +} // namespace IPC + +#endif // CHROME_COMMON_IPC_CHANNEL_HANDLE_H_ diff --git a/chrome/common/ipc_channel_posix.cc b/chrome/common/ipc_channel_posix.cc index babc16c..63f2703 100644 --- a/chrome/common/ipc_channel_posix.cc +++ b/chrome/common/ipc_channel_posix.cc @@ -104,9 +104,9 @@ class PipeMap { DCHECK(fd != -1); ChannelToFDMap::const_iterator i = map_.find(channel_id); - CHECK(i == map_.end()) << "Creating second IPC server for '" - << channel_id - << "' while first still exists"; + CHECK(i == map_.end()) << "Creating second IPC server (fd " << fd << ") " + << "for '" << channel_id << "' while first " + << "(fd " << i->second << ") still exists"; map_[channel_id] = fd; } @@ -116,16 +116,20 @@ class PipeMap { ChannelToFDMap map_; }; -// Used to map a channel name to the equivalent FD # in the client process. -int ChannelNameToClientFD(const std::string& channel_id) { +// Used to map a channel name to the equivalent FD # in the current process. +// Returns -1 if the channel is unknown. +int ChannelNameToFD(const std::string& channel_id) { // See the large block comment above PipeMap for the reasoning here. const int fd = Singleton<PipeMap>()->Lookup(channel_id); - if (fd != -1) - return dup(fd); - // If we don't find an entry, we assume that the correct value has been - // inserted in the magic slot. - return Singleton<base::GlobalDescriptors>()->Get(kPrimaryIPCChannel); + if (fd != -1) { + int dup_fd = dup(fd); + if (dup_fd < 0) + LOG(FATAL) << "dup(" << fd << "): " << strerror(errno); + return dup_fd; + } + + return fd; } //------------------------------------------------------------------------------ @@ -261,6 +265,34 @@ Channel::ChannelImpl::ChannelImpl(const std::string& channel_id, Mode mode, } } +// static +void AddChannelSocket(const std::string& name, int socket) { + Singleton<PipeMap>()->Insert(name, socket); +} + +// static +bool SocketPair(int* fd1, int* fd2) { + int pipe_fds[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) != 0) { + LOG(ERROR) << "socketpair(): " << strerror(errno); + return false; + } + + // Set both ends to be non-blocking. + if (fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK) == -1 || + fcntl(pipe_fds[1], F_SETFL, O_NONBLOCK) == -1) { + LOG(ERROR) << "fcntl(O_NONBLOCK): " << strerror(errno); + HANDLE_EINTR(close(pipe_fds[0])); + HANDLE_EINTR(close(pipe_fds[1])); + return false; + } + + *fd1 = pipe_fds[0]; + *fd2 = pipe_fds[1]; + + return true; +} + bool Channel::ChannelImpl::CreatePipe(const std::string& channel_id, Mode mode) { DCHECK(server_listen_pipe_ == -1 && pipe_ == -1); @@ -282,27 +314,24 @@ bool Channel::ChannelImpl::CreatePipe(const std::string& channel_id, waiting_connect_ = false; } } else { - // socketpair() + // This is the normal (non-unit-test) case, where we're using sockets. + // Three possible cases: + // 1) It's for a channel we already have a pipe for; reuse it. + // 2) It's the initial IPC channel: + // 2a) Server side: create the pipe. + // 2b) Client side: Pull the pipe out of the GlobalDescriptors set. pipe_name_ = channel_id; - if (mode == MODE_SERVER) { - int pipe_fds[2]; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) != 0) { - 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) { - HANDLE_EINTR(close(pipe_fds[0])); - HANDLE_EINTR(close(pipe_fds[1])); - return false; + pipe_ = ChannelNameToFD(pipe_name_); + if (pipe_ < 0) { + // Initial IPC channel. + if (mode == MODE_SERVER) { + if (!SocketPair(&pipe_, &client_pipe_)) + return false; + AddChannelSocket(pipe_name_, client_pipe_); + } else { + pipe_ = Singleton<base::GlobalDescriptors>()->Get(kPrimaryIPCChannel); } - pipe_ = pipe_fds[0]; - client_pipe_ = pipe_fds[1]; - - Singleton<PipeMap>()->Insert(pipe_name_, client_pipe_); } else { - pipe_ = ChannelNameToClientFD(pipe_name_); - DCHECK(pipe_ > 0); waiting_connect_ = false; } } @@ -612,7 +641,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() { return false; } #endif // OS_MACOSX - LOG(ERROR) << "pipe error: " << strerror(errno); + LOG(ERROR) << "pipe error on " << pipe_ << ": " << strerror(errno); return false; } diff --git a/chrome/common/ipc_channel_posix.h b/chrome/common/ipc_channel_posix.h index ed3d727..5e8d977 100644 --- a/chrome/common/ipc_channel_posix.h +++ b/chrome/common/ipc_channel_posix.h @@ -18,6 +18,15 @@ namespace IPC { +// Store that channel name |name| is available via socket |socket|. +// Used when the channel has been precreated by another process on +// our behalf and they've just shipped us the socket. +void AddChannelSocket(const std::string& name, int socket); + +// Construct a socket pair appropriate for IPC: UNIX domain, nonblocking. +// Returns false on error. +bool SocketPair(int* fd1, int* fd2); + // An implementation of ChannelImpl for POSIX systems that works via // socketpairs. See the .cc file for an overview of the implementation. class Channel::ChannelImpl : public MessageLoopForIO::Watcher { @@ -60,9 +69,16 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher { // a socketpair(). bool uses_fifo_; + // File descriptor we're listening on for new connections in the FIFO case; + // unused otherwise. int server_listen_pipe_; + + // The pipe used for communication. int pipe_; - int client_pipe_; // The client end of our socketpair(). + + // For a server, the client end of our socketpair() -- the other end of our + // pipe_ that is passed to the client. + int client_pipe_; // 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. diff --git a/chrome/common/ipc_message_utils.h b/chrome/common/ipc_message_utils.h index 3f0f7b2..2ae2ab5 100644 --- a/chrome/common/ipc_message_utils.h +++ b/chrome/common/ipc_message_utils.h @@ -16,6 +16,7 @@ #if defined(OS_POSIX) #include "chrome/common/file_descriptor_set_posix.h" #endif +#include "chrome/common/ipc_channel_handle.h" #include "chrome/common/ipc_sync_message.h" #include "chrome/common/thumbnail_score.h" #include "chrome/common/transport_dib.h" @@ -726,6 +727,34 @@ struct ParamTraits<base::FileDescriptor> { }; #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 ParamTraits<IPC::ChannelHandle> { + typedef ChannelHandle param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.name); +#if defined(OS_POSIX) + WriteParam(m, p.socket); +#endif + } + static bool 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 + ; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"ChannelHandle(%s", p.name.c_str())); +#if defined(OS_POSIX) + ParamTraits<base::FileDescriptor>::Log(p.socket, l); +#endif + l->append(L")"); + } +}; + template<> struct ParamTraits<ThumbnailScore> { typedef ThumbnailScore param_type; diff --git a/chrome/common/plugin_messages_internal.h b/chrome/common/plugin_messages_internal.h index b2805fc..500bd93 100644 --- a/chrome/common/plugin_messages_internal.h +++ b/chrome/common/plugin_messages_internal.h @@ -3,9 +3,14 @@ // found in the LICENSE file. #include "base/shared_memory.h" +#include "build/build_config.h" #include "chrome/common/ipc_message_macros.h" #include "webkit/glue/webcursor.h" +#if defined(OS_POSIX) +#include "base/file_descriptor_posix.h" +#endif + //----------------------------------------------------------------------------- // PluginProcess messages // These are messages sent from the browser to the plugin process. @@ -15,9 +20,19 @@ IPC_BEGIN_MESSAGES(PluginProcess) // PluginProcessHostMsg_ChannelCreated message. The renderer's process_id is // passed so that the plugin process reuses an existing channel to that // process if it exists. + // It would be nice to use #ifdefs inside the parameter list to not need to + // duplicate this across POSIX/Windows but the Visual Studio compiler doesn't + // like that. +#if defined(OS_WIN) IPC_MESSAGE_CONTROL2(PluginProcessMsg_CreateChannel, int /* process_id */, bool /* off_the_record */) +#elif defined(OS_POSIX) + IPC_MESSAGE_CONTROL3(PluginProcessMsg_CreateChannel, + base::FileDescriptor /* socket for new channel */, + int /* process_id */, + bool /* off_the_record */) +#endif // Allows a chrome plugin loaded in the browser process to send arbitrary // data to an instance of the same plugin loaded in a plugin process. diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index d96220d..5debd5f 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -17,6 +17,7 @@ #include "base/gfx/native_widget_types.h" #include "base/shared_memory.h" #include "base/values.h" +#include "chrome/common/ipc_channel_handle.h" #include "chrome/common/ipc_message_macros.h" #include "chrome/common/transport_dib.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -924,16 +925,16 @@ IPC_BEGIN_MESSAGES(ViewHost) std::string /* origin */, std::string /* target */) - // A renderer sends this to the browser process when it wants to create a - // plugin. The browser will create the plugin process if necessary, and - // will return the channel name on success. On error an empty string is - // returned. + // A renderer sends this to the browser process when it wants to + // create a plugin. The browser will create the plugin process if + // necessary, and will return a handle to the channel on success. + // On error an empty string is returned. IPC_SYNC_MESSAGE_CONTROL4_2(ViewHostMsg_OpenChannelToPlugin, GURL /* url */, std::string /* mime_type */, std::string /* clsid */, std::wstring /* locale */, - std::string /* channel_name */, + IPC::ChannelHandle /* handle to channel */, FilePath /* plugin_path */) // Clipboard IPC messages |