Bug 564086, part j: Add IPC::Channel support for getting OS-level pipe info and creating from existing pipe descriptors. r=bent
--- a/ipc/chromium/src/chrome/common/ipc_channel.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel.h
@@ -54,16 +54,30 @@ class Channel : public Message::Sender {
// client mode. In server mode, the Channel is responsible for setting up the
// IPC object, whereas in client mode, the Channel merely connects to the
// already established IPC object.
// |listener| receives a callback on the current thread for each newly
// received message.
//
Channel(const std::wstring& channel_id, Mode mode, Listener* listener);
+#if defined(CHROMIUM_MOZILLA_BUILD)
+ // XXX it would nice not to have yet more platform-specific code in
+ // here but it's just not worth the trouble.
+# if defined(OS_POSIX)
+ // Connect to a pre-created channel |fd| as |mode|.
+ Channel(int fd, Mode mode, Listener* listener);
+# elif defined(OS_WIN)
+ // Connect to a pre-created channel as |mode|. Clients connect to
+ // the pre-existing server pipe, and servers take over |server_pipe|.
+ Channel(const std::wstring& channel_id, void* server_pipe,
+ Mode mode, Listener* listener);
+# endif
+#endif
+
~Channel();
// Connect the pipe. On the server side, this will initiate
// waiting for connections. On the client, it attempts to
// connect to a pre-existing pipe. Note, calling Connect()
// will not block the calling thread and may complete
// asynchronously.
bool Connect();
@@ -94,16 +108,26 @@ class Channel : public Message::Sender {
// FD # for the client end of the socket and the equivalent FD# to use for
// mapping it into the Child process.
// This method may only be called on the server side of a channel.
//
// If the kTestingChannelID flag is specified on the command line then
// a named FIFO is used as the channel transport mechanism rather than a
// socketpair() in which case this method returns -1 for both parameters.
void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
+
+# if defined(CHROMIUM_MOZILLA_BUILD)
+ // Return the server side of the socketpair.
+ int GetServerFileDescriptor() const;
+# endif
+#elif defined(OS_WIN)
+# if defined(CHROMIUM_MOZILLA_BUILD)
+ // Return the server pipe handle.
+ void* GetServerPipeHandle() const;
+# endif
#endif // defined(OS_POSIX)
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
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -252,36 +252,50 @@ bool SetCloseOnExec(int fd) {
}
#endif
} // namespace
//------------------------------------------------------------------------------
Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode,
Listener* listener)
- : mode_(mode),
- is_blocked_on_write_(false),
- message_send_bytes_written_(0),
- uses_fifo_(CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kIPCUseFIFO)),
- server_listen_pipe_(-1),
- pipe_(-1),
- client_pipe_(-1),
- listener_(listener),
- waiting_connect_(true),
- processing_incoming_(false),
- factory_(this) {
+ : factory_(this) {
+ Init(mode, listener);
+ uses_fifo_ = CommandLine::ForCurrentProcess()->HasSwitch(switches::kIPCUseFIFO);
+
if (!CreatePipe(channel_id, mode)) {
// The pipe may have been closed already.
LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
"\" in " << (mode == MODE_SERVER ? "server" : "client") <<
" mode error(" << strerror(errno) << ").";
}
}
+Channel::ChannelImpl::ChannelImpl(int fd, Mode mode, Listener* listener)
+ : factory_(this) {
+ Init(mode, listener);
+ pipe_ = fd;
+ waiting_connect_ = (MODE_SERVER == mode);
+
+ EnqueueHelloMessage();
+}
+
+void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
+ mode_ = mode;
+ is_blocked_on_write_ = false;
+ message_send_bytes_written_ = 0;
+ uses_fifo_ = false;
+ server_listen_pipe_ = -1;
+ pipe_ = -1;
+ client_pipe_ = -1;
+ listener_ = listener;
+ waiting_connect_ = true;
+ processing_incoming_ = false;
+}
+
bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id,
Mode mode) {
DCHECK(server_listen_pipe_ == -1 && pipe_ == -1);
if (uses_fifo_) {
// This only happens in unit tests; see the comment above PipeMap.
// TODO(playmobil): We shouldn't need to create fifos on disk.
// TODO(playmobil): If we do, they should be in the user data directory.
@@ -329,16 +343,20 @@ bool Channel::ChannelImpl::CreatePipe(co
} else {
pipe_ = ChannelNameToClientFD(pipe_name_);
DCHECK(pipe_ > 0);
waiting_connect_ = false;
}
}
// Create the Hello message to be sent when Connect is called
+ return EnqueueHelloMessage();
+}
+
+bool Channel::ChannelImpl::EnqueueHelloMessage() {
scoped_ptr<Message> msg(new Message(MSG_ROUTING_NONE,
HELLO_MESSAGE_TYPE,
IPC::Message::PRIORITY_NORMAL));
if (!msg->WriteInt(base::GetCurrentProcId())) {
Close();
return false;
}
@@ -791,16 +809,22 @@ void Channel::ChannelImpl::Close() {
//------------------------------------------------------------------------------
// Channel's methods simply call through to ChannelImpl.
Channel::Channel(const std::wstring& channel_id, Mode mode,
Listener* listener)
: channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
}
+#if defined(CHROMIUM_MOZILLA_BUILD)
+Channel::Channel(int fd, Mode mode, Listener* listener)
+ : channel_impl_(new ChannelImpl(fd, mode, listener)) {
+}
+#endif
+
Channel::~Channel() {
delete channel_impl_;
}
bool Channel::Connect() {
return channel_impl_->Connect();
}
@@ -821,9 +845,15 @@ void Channel::set_listener(Listener* lis
bool Channel::Send(Message* message) {
return channel_impl_->Send(message);
}
void Channel::GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const {
return channel_impl_->GetClientFileDescriptorMapping(src_fd, dest_fd);
}
+#ifdef CHROMIUM_MOZILLA_BUILD
+int Channel::GetServerFileDescriptor() const {
+ return channel_impl_->GetServerFileDescriptor();
+}
+#endif
+
} // namespace IPC
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.h
@@ -19,33 +19,42 @@
namespace IPC {
// An implementation of ChannelImpl for POSIX systems that works via
// socketpairs. See the .cc file for an overview of the implementation.
class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
public:
// Mirror methods of Channel, see ipc_channel.h for description.
ChannelImpl(const std::wstring& channel_id, Mode mode, Listener* listener);
+ ChannelImpl(int fd, Mode mode, Listener* listener);
~ChannelImpl() { Close(); }
bool Connect();
void Close();
#ifdef CHROMIUM_MOZILLA_BUILD
Listener* set_listener(Listener* listener) {
Listener* old = listener_;
listener_ = listener;
return old;
}
#else
void set_listener(Listener* listener) { listener_ = listener; }
#endif
bool Send(Message* message);
void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
+#ifdef CHROMIUM_MOZILLA_BUILD
+ int GetServerFileDescriptor() const {
+ DCHECK(mode_ == MODE_SERVER);
+ return pipe_;
+ }
+#endif
private:
+ void Init(Mode mode, Listener* listener);
bool CreatePipe(const std::wstring& channel_id, Mode mode);
+ bool EnqueueHelloMessage();
bool ProcessIncomingMessages();
bool ProcessOutgoingMessages();
// MessageLoopForIO::Watcher implementation.
virtual void OnFileCanReadWithoutBlocking(int fd);
virtual void OnFileCanWriteWithoutBlocking(int fd);
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc
@@ -30,28 +30,57 @@ Channel::ChannelImpl::State::~State() {
}
//------------------------------------------------------------------------------
Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode,
Listener* listener)
: ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
- pipe_(INVALID_HANDLE_VALUE),
- listener_(listener),
- waiting_connect_(mode == MODE_SERVER),
- processing_incoming_(false),
ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+ Init(mode, listener);
+
if (!CreatePipe(channel_id, mode)) {
// The pipe may have been closed already.
LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
"\" in " << (mode == 0 ? "server" : "client") << " mode.";
}
}
+#if defined(CHROMIUM_MOZILLA_BUILD)
+Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id,
+ HANDLE server_pipe,
+ Mode mode, Listener* listener)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+ Init(mode, listener);
+
+ if (mode == MODE_SERVER) {
+ // Use the existing handle that was dup'd to us
+ pipe_ = server_pipe;
+ EnqueueHelloMessage();
+ } else {
+ // Take the normal init path to connect to the server pipe
+ CreatePipe(channel_id, mode);
+ }
+}
+
+void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
+ pipe_ = INVALID_HANDLE_VALUE;
+ listener_ = listener;
+ waiting_connect_ = (mode == MODE_SERVER);
+ processing_incoming_ = false;
+}
+
+HANDLE Channel::ChannelImpl::GetServerPipeHandle() const {
+ return pipe_;
+}
+#endif
+
void Channel::ChannelImpl::Close() {
if (thread_check_.get()) {
DCHECK(thread_check_->CalledOnValidThread());
}
bool waited = false;
if (input_state_.is_pending || output_state_.is_pending) {
CancelIo(pipe_);
@@ -159,16 +188,20 @@ bool Channel::ChannelImpl::CreatePipe(co
}
if (pipe_ == INVALID_HANDLE_VALUE) {
// If this process is being closed, the pipe may be gone already.
LOG(WARNING) << "failed to create pipe: " << GetLastError();
return false;
}
// Create the Hello message to be sent when Connect is called
+ return EnqueueHelloMessage();
+}
+
+bool Channel::ChannelImpl::EnqueueHelloMessage() {
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;
}
@@ -421,29 +454,40 @@ void Channel::ChannelImpl::OnIOCompleted
//------------------------------------------------------------------------------
// Channel's methods simply call through to ChannelImpl.
Channel::Channel(const std::wstring& channel_id, Mode mode,
Listener* listener)
: channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
}
+#ifdef CHROMIUM_MOZILLA_BUILD
+Channel::Channel(const std::wstring& channel_id, void* server_pipe,
+ Mode mode, Listener* listener)
+ : channel_impl_(new ChannelImpl(channel_id, server_pipe, mode, listener)) {
+}
+#endif
+
Channel::~Channel() {
delete channel_impl_;
}
bool Channel::Connect() {
return channel_impl_->Connect();
}
void Channel::Close() {
channel_impl_->Close();
}
#ifdef CHROMIUM_MOZILLA_BUILD
+void* Channel::GetServerPipeHandle() const {
+ return channel_impl_->GetServerPipeHandle();
+}
+
Channel::Listener* Channel::set_listener(Listener* listener) {
return channel_impl_->set_listener(listener);
}
#else
void Channel::set_listener(Listener* listener) {
channel_impl_->set_listener(listener);
}
#endif
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.h
@@ -15,36 +15,42 @@
class NonThreadSafe;
namespace IPC {
class Channel::ChannelImpl : public MessageLoopForIO::IOHandler {
public:
// Mirror methods of Channel, see ipc_channel.h for description.
ChannelImpl(const std::wstring& channel_id, Mode mode, Listener* listener);
+ ChannelImpl(const std::wstring& channel_id, HANDLE server_pipe,
+ Mode mode, Listener* listener);
~ChannelImpl() {
if (pipe_ != INVALID_HANDLE_VALUE) {
Close();
}
}
bool Connect();
void Close();
#ifdef CHROMIUM_MOZILLA_BUILD
+ HANDLE GetServerPipeHandle() const;
+
Listener* set_listener(Listener* listener) {
Listener* old = listener_;
listener_ = listener;
return old;
}
#else
void set_listener(Listener* listener) { listener_ = listener; }
#endif
bool Send(Message* message);
private:
+ void Init(Mode mode, Listener* listener);
const std::wstring PipeName(const std::wstring& channel_id) const;
bool CreatePipe(const std::wstring& channel_id, Mode mode);
+ bool EnqueueHelloMessage();
bool ProcessConnection();
bool ProcessIncomingMessages(MessageLoopForIO::IOContext* context,
DWORD bytes_read);
bool ProcessOutgoingMessages(MessageLoopForIO::IOContext* context,
DWORD bytes_written);
// MessageLoop::IOHandler implementation.