summaryrefslogtreecommitdiffstats
path: root/chrome/common
diff options
context:
space:
mode:
authorevan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-19 20:50:09 +0000
committerevan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-19 20:50:09 +0000
commit255a9f6c6f6db2a95fec78c6c2f2c504c655e733 (patch)
tree471d21ae7e41cc2e33a491a5309b3ea77430ebca /chrome/common
parent04b74d13d927794495a1a3a1b89d67f85a104e12 (diff)
downloadchromium_src-255a9f6c6f6db2a95fec78c6c2f2c504c655e733.zip
chromium_src-255a9f6c6f6db2a95fec78c6c2f2c504c655e733.tar.gz
chromium_src-255a9f6c6f6db2a95fec78c6c2f2c504c655e733.tar.bz2
posix: two related changes to make plugin IPC work on POSIX.
* use a new ChannelHandle type when passing IPC channels over IPC The current POSIX code assumes that one end of a channel is always a new child process (a renderer). For plugins we need to be able to construct channels between each of the browser, plugin, and renderer. This change augments the messages related to creating channels to allow passing in a base::FileDescriptor containing the socket. The intent is that the browser process, as the initial interchange between plugin and renderer, creates the socketpair() on their behalf and hands each their respective end of the connection. * register channel endpoint names in the global pipe map The plugin code assumes it can map from a string to a channel endpoint at basically any time. So whenever we get a channel endpoint over IPC, we install it in a global map of channel endpoints. Review URL: http://codereview.chromium.org/113157 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18850 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common')
-rw-r--r--chrome/common/ipc_channel_handle.h45
-rw-r--r--chrome/common/ipc_channel_posix.cc87
-rw-r--r--chrome/common/ipc_channel_posix.h18
-rw-r--r--chrome/common/ipc_message_utils.h29
-rw-r--r--chrome/common/plugin_messages_internal.h15
-rw-r--r--chrome/common/render_messages_internal.h11
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