diff options
-rw-r--r-- | ipc/file_descriptor_set_posix_unittest.cc | 3 | ||||
-rw-r--r-- | ipc/ipc.gyp | 1 | ||||
-rw-r--r-- | ipc/ipc_channel.h | 58 | ||||
-rw-r--r-- | ipc/ipc_channel_posix.cc | 619 | ||||
-rw-r--r-- | ipc/ipc_channel_posix.h | 31 | ||||
-rw-r--r-- | ipc/ipc_channel_posix_unittest.cc | 334 | ||||
-rw-r--r-- | ipc/ipc_channel_win.cc | 16 | ||||
-rw-r--r-- | ipc/ipc_message_unittest.cc | 3 | ||||
-rw-r--r-- | ipc/ipc_switches.cc | 4 | ||||
-rw-r--r-- | ipc/ipc_switches.h | 1 | ||||
-rw-r--r-- | ipc/ipc_sync_channel_unittest.cc | 3 | ||||
-rw-r--r-- | ipc/sync_socket_unittest.cc | 3 |
12 files changed, 316 insertions, 760 deletions
diff --git a/ipc/file_descriptor_set_posix_unittest.cc b/ipc/file_descriptor_set_posix_unittest.cc index 5fc8c50..31b8bf4 100644 --- a/ipc/file_descriptor_set_posix_unittest.cc +++ b/ipc/file_descriptor_set_posix_unittest.cc @@ -4,13 +4,12 @@ // 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 "ipc/file_descriptor_set_posix.h" #include "testing/gtest/include/gtest/gtest.h" namespace { diff --git a/ipc/ipc.gyp b/ipc/ipc.gyp index 6865ee3..ebc590a 100644 --- a/ipc/ipc.gyp +++ b/ipc/ipc.gyp @@ -46,7 +46,6 @@ ], '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', diff --git a/ipc/ipc_channel.h b/ipc/ipc_channel.h index c257a8e..132b494 100644 --- a/ipc/ipc_channel.h +++ b/ipc/ipc_channel.h @@ -13,21 +13,6 @@ 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 Channel : public Message::Sender { // Security tests need access to the pipe handle. @@ -49,29 +34,12 @@ class Channel : public Message::Sender { // 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 }; enum Mode { MODE_NONE, MODE_SERVER, - MODE_CLIENT, - // 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 == MODE_SERVER and - // MODE_NAMED_CLIENT == MODE_CLIENT. - MODE_NAMED_SERVER, - MODE_NAMED_CLIENT, + MODE_CLIENT }; enum { @@ -109,10 +77,6 @@ class Channel : public Message::Sender { 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. @@ -128,23 +92,11 @@ class Channel : public Message::Sender { // 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. + // + // 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. 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; - - // Closes any currently connected socket, and returns to a listening state - // for more connections. - void ResetToAcceptingConnectionState(); #endif // defined(OS_POSIX) protected: diff --git a/ipc/ipc_channel_posix.cc b/ipc/ipc_channel_posix.cc index a82e591..0d0160b 100644 --- a/ipc/ipc_channel_posix.cc +++ b/ipc/ipc_channel_posix.cc @@ -33,18 +33,15 @@ 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. +// channel ids as the pipe names. Channels on POSIX use anonymous +// Unix domain sockets created via socketpair() as pipes. These don't +// quite line up. // -// When creating a child subprocess we use a socket pair and the parent side of -// the fork arranges it such that the initial control channel ends up on the +// When creating a child subprocess, the parent side of the fork +// arranges it such that the initial control channel ends up on the // magic file descriptor 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 { @@ -131,6 +128,22 @@ class PipeMap { friend struct DefaultSingletonTraits<PipeMap>; }; +// 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 = PipeMap::GetInstance()->Lookup(channel_id); + + if (fd != -1) { + int dup_fd = dup(fd); + if (dup_fd < 0) + PLOG(FATAL) << "dup(" << fd << ") " << channel_id; + return dup_fd; + } + + return fd; +} + //------------------------------------------------------------------------------ // The standard size on linux is 108, mac is 104. To maintain consistency // across platforms we standardize on the smaller value. @@ -138,10 +151,8 @@ const size_t kMaxPipeNameLength = 104; 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) { +// Creates a Fifo with the specified name ready to listen on. +bool CreateServerFifo(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); @@ -158,7 +169,7 @@ bool CreateServerUnixDomainSocket(const std::string& pipe_name, // Make socket non-blocking if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { - PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name; + PLOG(ERROR) << "fcntl " << pipe_name; if (HANDLE_EINTR(close(fd)) < 0) PLOG(ERROR) << "close " << pipe_name; return false; @@ -197,15 +208,15 @@ bool CreateServerUnixDomainSocket(const std::string& pipe_name, return true; } -// Accept a connection on a socket we are listening to. -bool ServerAcceptConnection(int server_listen_fd, int* server_socket) { +// Accept a connection on a fifo. +bool ServerAcceptFifoConnection(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; + PLOG(ERROR) << "fcntl " << accept_fd; if (HANDLE_EINTR(close(accept_fd)) < 0) PLOG(ERROR) << "close " << accept_fd; return false; @@ -215,8 +226,7 @@ bool ServerAcceptConnection(int server_listen_fd, int* server_socket) { return true; } -bool CreateClientUnixDomainSocket(const std::string& pipe_name, - int* client_socket) { +bool ClientConnectToFifo(const std::string &pipe_name, int* client_socket) { DCHECK(client_socket); DCHECK_GT(pipe_name.length(), 0u); DCHECK_LT(pipe_name.length(), kMaxPipeNameLength); @@ -234,7 +244,7 @@ bool CreateClientUnixDomainSocket(const std::string& pipe_name, // Make socket non-blocking if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { - PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name; + PLOG(ERROR) << "fcntl " << pipe_name; if (HANDLE_EINTR(close(fd)) < 0) PLOG(ERROR) << "close " << pipe_name; return false; @@ -277,7 +287,7 @@ bool SocketWriteErrorIsRecoverable() { return errno == EAGAIN || errno == EMSGSIZE; #else return errno == EAGAIN; -#endif // OS_MACOSX +#endif } } // namespace @@ -287,36 +297,24 @@ 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), + uses_fifo_(CommandLine::ForCurrentProcess()->HasSwitch( + switches::kIPCUseFIFO)), 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), +#endif listener_(listener), + waiting_connect_(true), factory_(this) { - // Check to see if we want to implement using domain sockets. - bool uses_domain_socket = false; - bool listening_socket = false; - if (mode_ == MODE_NAMED_SERVER) { - uses_domain_socket = true; - listening_socket = true; - mode_ = MODE_SERVER; - } else if (mode_ == MODE_NAMED_CLIENT) { - uses_domain_socket = true; - mode_ = MODE_CLIENT; - } - if (!CreatePipe(channel_handle, uses_domain_socket, listening_socket)) { - // The pipe may have been closed already. - const char *modestr = (mode_ == MODE_SERVER - || mode_ == MODE_NAMED_SERVER) ? "server" : "client"; + if (!CreatePipe(channel_handle, mode_)) { // The pipe may have been closed already. LOG(WARNING) << "Unable to create pipe named \"" << channel_handle.name - << "\" in " << modestr << " mode"; + << "\" in " << (mode_ == MODE_SERVER ? "server" : "client") + << " mode"; } } @@ -324,6 +322,22 @@ Channel::ChannelImpl::~ChannelImpl() { Close(); } +// static +void AddChannelSocket(const std::string& name, int socket) { + PipeMap::GetInstance()->Insert(name, socket); +} + +// static +void RemoveAndCloseChannelSocket(const std::string& name) { + PipeMap::GetInstance()->RemoveAndClose(name); +} + +// static +bool ChannelSocketExists(const std::string& name) { + return PipeMap::GetInstance()->Lookup(name) != -1; +} + +// static bool SocketPair(int* fd1, int* fd2) { int pipe_fds[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) != 0) { @@ -348,58 +362,43 @@ bool SocketPair(int* fd1, int* fd2) { return true; } -bool Channel::ChannelImpl::CreatePipe(const IPC::ChannelHandle& channel_handle, - bool uses_domain_sockets, - bool listening_socket) { +bool Channel::ChannelImpl::CreatePipe(const IPC::ChannelHandle &channel_handle, + Mode mode) { 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. - - if (channel_handle.socket.fd != -1) { - // Case 1 from comment above. - 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(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 (uses_domain_sockets) { - // Case 2 from comment above. - must_unlink_ = true; - if (mode_ == MODE_SERVER) { - if (!CreateServerUnixDomainSocket(pipe_name_, &pipe_)) { + pipe_name_ = channel_handle.name; + pipe_ = channel_handle.socket.fd; + 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. + // TODO(playmobil): Cleanup any stale fifos. + if (mode == MODE_SERVER) { + if (!CreateServerFifo(pipe_name_, &server_listen_pipe_)) { return false; } - } else if (mode_ == MODE_CLIENT) { - if (!CreateClientUnixDomainSocket(pipe_name_, &pipe_)) { + } else { + if (!ClientConnectToFifo(pipe_name_, &pipe_)) { return false; } + waiting_connect_ = false; } } else { - pipe_ = PipeMap::GetInstance()->Lookup(pipe_name_); - if (mode_ == MODE_CLIENT) { - if (pipe_ != -1) { - // Case 3 from comment above. - // We only allow one connection. - pipe_ = HANDLE_EINTR(dup(pipe_)); - PipeMap::GetInstance()->RemoveAndClose(pipe_name_); + // 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. + if (pipe_ < 0) { + 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 { - // 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. @@ -413,51 +412,42 @@ bool Channel::ChannelImpl::CreatePipe(const IPC::ChannelHandle& channel_handle, pipe_ = base::GlobalDescriptors::GetInstance()->Get(kPrimaryIPCChannel); } - } else if (mode_ == MODE_SERVER) { - // Case 4b from comment above. - if (pipe_ != -1) { - LOG(ERROR) << "Server already exists for " << pipe_name_; - return false; - } - if (!SocketPair(&pipe_, &client_pipe_)) - return false; - PipeMap::GetInstance()->Insert(pipe_name_, client_pipe_); } else { - LOG(FATAL) << "Unknown mode " << mode_; - return false; + waiting_connect_ = mode == MODE_SERVER; } } - if (mode_ == MODE_SERVER) { - if (listening_socket) { - server_listen_pipe_ = pipe_; - pipe_ = -1; + // Create the Hello message to be sent when Connect is called + scoped_ptr<Message> msg(new Message(MSG_ROUTING_NONE, + HELLO_MESSAGE_TYPE, + IPC::Message::PRIORITY_NORMAL)); + #if defined(IPC_USES_READWRITE) + if (!uses_fifo_) { + // Create a dedicated socketpair() for exchanging file descriptors. + // See comments for IPC_USES_READWRITE for details. + if (mode == MODE_SERVER) { + fd_pipe_ = -1; + } else if (remote_fd_pipe_ == -1) { + if (!SocketPair(&fd_pipe_, &remote_fd_pipe_)) { + 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) { - if (!SocketPair(&fd_pipe_, &remote_fd_pipe_)) { - return false; - } + #endif + if (!msg->WriteInt(base::GetCurrentProcId())) { + Close(); + return false; } -#endif // IPC_USES_READWRITE + output_queue_.push(msg.release()); return true; } bool Channel::ChannelImpl::Connect() { - if (server_listen_pipe_ == -1 && pipe_ == -1) { - NOTREACHED() << "Must call create on a channel before calling connect"; - return false; - } - - bool did_connect = true; - if (server_listen_pipe_ != -1) { - // Watch the pipe for connections, and turn any connections into - // active sockets. + if (mode_ == MODE_SERVER && uses_fifo_) { + if (server_listen_pipe_ == -1) { + return false; + } MessageLoopForIO::current()->WatchFileDescriptor( server_listen_pipe_, true, @@ -465,9 +455,21 @@ bool Channel::ChannelImpl::Connect() { &server_listen_connection_watcher_, this); } else { - did_connect = AcceptConnection(); + if (pipe_ == -1) { + return false; + } + MessageLoopForIO::current()->WatchFileDescriptor( + pipe_, + true, + MessageLoopForIO::WATCH_READ, + &read_watcher_, + this); + waiting_connect_ = mode_ == MODE_SERVER; } - return did_connect; + + if (!waiting_connect_) + return ProcessOutgoingMessages(); + return true; } bool Channel::ChannelImpl::ProcessIncomingMessages() { @@ -495,7 +497,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { Channel::kReadBufferSize)); msg.msg_controllen = 0; } else -#endif // IPC_USES_READWRITE +#endif { msg.msg_controllen = sizeof(input_cmsg_buf_); bytes_read = HANDLE_EINTR(recvmsg(pipe_, &msg, MSG_DONTWAIT)); @@ -509,7 +511,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { // treat this as a special case to prevent spurious error messages // to the console. return false; -#endif // OS_MACOSX +#endif // defined(OS_MACOSX) } else if (errno == ECONNRESET || errno == EPIPE) { return false; } else { @@ -564,7 +566,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { << " fd:" << pipe_; for (unsigned i = 0; i < num_wire_fds; ++i) if (HANDLE_EINTR(close(wire_fds[i])) < 0) - PLOG(ERROR) << "close " << i; + PLOG(ERROR) << "close" << i; return false; } break; @@ -623,49 +625,51 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { // 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(payload_len % sizeof(int) == 0); - wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); - num_wire_fds = payload_len / 4; - - if (msg.msg_flags & MSG_CTRUNC) { - 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; + if (!uses_fifo_) { + 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(payload_len % sizeof(int) == 0); + wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); + num_wire_fds = payload_len / 4; + + if (msg.msg_flags & MSG_CTRUNC) { + 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; } - 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)); + 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(); } - fds = &input_overflow_fds_[0]; - num_fds = input_overflow_fds_.size(); } } if (header_fds > num_fds - fds_i) -#endif // IPC_USES_READWRITE +#endif error = "Message needs unreceived descriptors"; } @@ -686,11 +690,11 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { 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 +#endif // 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; + PLOG(ERROR) << "close" << i; input_overflow_fds_.clear(); // abort the connection return false; @@ -702,7 +706,8 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { } DVLOG(2) << "received message on channel @" << this << " with type " << m.type() << " on fd " << pipe_; - if (IsHelloMessage(&m)) { + if (m.routing_id() == MSG_ROUTING_NONE && + m.type() == HELLO_MESSAGE_TYPE) { // The Hello message contains only the process id. void *iter = NULL; int pid; @@ -710,9 +715,9 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { NOTREACHED(); } #if defined(IPC_USES_READWRITE) - if (mode_ == MODE_SERVER) { - // With IPC_USES_READWRITE, the Hello message from the client to the - // server also contains the fd_pipe_, which will be used for all + if (mode_ == MODE_SERVER && !uses_fifo_) { + // On non-Mac, 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; @@ -722,7 +727,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { fd_pipe_ = descriptor.fd; CHECK(descriptor.auto_close); } -#endif // IPC_USES_READWRITE +#endif listener_->OnChannelConnected(pid); } else { listener_->OnMessageReceived(m); @@ -750,22 +755,52 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { bytes_read = 0; // Get more data. } + + return true; } bool Channel::ChannelImpl::ProcessOutgoingMessages() { DCHECK(!waiting_connect_); // Why are we trying to send messages if there's // no connection? - if (output_queue_.empty()) + is_blocked_on_write_ = false; + + if (output_queue_.empty()) { return true; + } - if (pipe_ == -1) + 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(); +#if defined(IPC_USES_READWRITE) + scoped_ptr<Message> hello; + if (remote_fd_pipe_ != -1 && + msg->routing_id() == MSG_ROUTING_NONE && + msg->type() == HELLO_MESSAGE_TYPE) { + hello.reset(new Message(MSG_ROUTING_NONE, + HELLO_MESSAGE_TYPE, + IPC::Message::PRIORITY_NORMAL)); + void* iter = NULL; + int pid; + if (!msg->ReadInt(&iter, &pid) || + !hello->WriteInt(pid)) { + NOTREACHED(); + } + DCHECK_EQ(hello->size(), msg->size()); + if (!hello->WriteFileDescriptor(base::FileDescriptor(remote_fd_pipe_, + false))) { + NOTREACHED(); + } + msg = hello.get(); + DCHECK_EQ(msg->file_descriptor_set()->size(), 1U); + } +#endif + size_t amt_to_write = msg->size() - message_send_bytes_written_; DCHECK(amt_to_write != 0); const char* out_bytes = reinterpret_cast<const char*>(msg->data()) + @@ -813,7 +848,9 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() { msg->header()->num_fds = static_cast<uint16>(num_fds); #if defined(IPC_USES_READWRITE) - if (!IsHelloMessage(msg)) { + if (!uses_fifo_ && + (msg->routing_id() != MSG_ROUTING_NONE || + msg->type() != HELLO_MESSAGE_TYPE)) { // 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. @@ -827,19 +864,21 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() { msg->file_descriptor_set()->CommitAll(); } } -#endif // IPC_USES_READWRITE +#endif } if (bytes_written == 1) { fd_written = pipe_; #if defined(IPC_USES_READWRITE) - if (mode_ != MODE_SERVER && IsHelloMessage(msg)) { + if (mode_ != MODE_SERVER && !uses_fifo_ && + msg->routing_id() == MSG_ROUTING_NONE && + msg->type() == HELLO_MESSAGE_TYPE) { DCHECK_EQ(msg->file_descriptor_set()->size(), 1U); } - if (!msgh.msg_controllen) { + if (!uses_fifo_ && !msgh.msg_controllen) { bytes_written = HANDLE_EINTR(write(pipe_, out_bytes, amt_to_write)); } else -#endif // IPC_USES_READWRITE +#endif { bytes_written = HANDLE_EINTR(sendmsg(pipe_, &msgh, MSG_DONTWAIT)); } @@ -901,11 +940,14 @@ bool Channel::ChannelImpl::Send(Message* message) { #ifdef IPC_MESSAGE_LOG_ENABLED Logging::GetInstance()->OnSendMessage(message, ""); -#endif // IPC_MESSAGE_LOG_ENABLED +#endif output_queue_.push(message); - if (!is_blocked_on_write_ && !waiting_connect_) { - return ProcessOutgoingMessages(); + if (!waiting_connect_) { + if (!is_blocked_on_write_) { + if (!ProcessOutgoingMessages()) + return false; + } } return true; @@ -915,89 +957,43 @@ 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; -} - -void Channel::ChannelImpl::ResetToAcceptingConnectionState() { - if (pipe_ == -1) - return; - - // Unregister libevent for the unix domain socket and close it. - read_watcher_.StopWatchingFileDescriptor(); - write_watcher_.StopWatchingFileDescriptor(); - 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(); -} - -// Called by libevent when we can read from the pipe without blocking. +// Called by libevent when we can read from th 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 (waiting_connect_ && mode_ == MODE_SERVER) { + if (uses_fifo_) { + if (!ServerAcceptFifoConnection(server_listen_pipe_, &pipe_)) { + Close(); + } - 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; + // No need to watch the listening socket any longer since only one client + // can connect. So unregister with libevent. + server_listen_connection_watcher_.StopWatchingFileDescriptor(); + + // Start watching our end of the socket. + MessageLoopForIO::current()->WatchFileDescriptor( + pipe_, + true, + MessageLoopForIO::WATCH_READ, + &read_watcher_, + this); - 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) { - send_server_hello_msg = true; + waiting_connect_ = false; + } else { + // In the case of a socketpair() the server starts listening on its end + // of the pipe in Connect(). waiting_connect_ = false; } + send_server_hello_msg = true; + } + + if (!waiting_connect_ && fd == pipe_) { if (!ProcessIncomingMessages()) { - ClosePipeOnError(); + Close(); + listener_->OnChannelError(); + // The OnChannelError() call may delete this, so we need to exit now. + return; } - } else { - NOTREACHED() << "Unknown pipe " << fd; } // If we're a server and handshaking, then we want to make sure that we @@ -1011,95 +1007,68 @@ void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) { // Called by libevent when we can write to the pipe without blocking. void Channel::ChannelImpl::OnFileCanWriteWithoutBlocking(int fd) { - DCHECK(fd == pipe_); - 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) { - // 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 { - waiting_connect_ = true; - return true; - } -} - -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); + listener_->OnChannelError(); } -#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(); + // Unregister libevent for the listening socket and close it. + server_listen_connection_watcher_.StopWatchingFileDescriptor(); - 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(); } + // Unregister libevent for the FIFO and close it. + read_watcher_.StopWatchingFileDescriptor(); + write_watcher_.StopWatchingFileDescriptor(); + if (pipe_ != -1) { + if (HANDLE_EINTR(close(pipe_)) < 0) + PLOG(ERROR) << "close " << pipe_; + pipe_ = -1; + } if (client_pipe_ != -1) { PipeMap::GetInstance()->RemoveAndClose(pipe_name_); client_pipe_ = -1; } +#if defined(IPC_USES_READWRITE) + if (fd_pipe_ != -1) { + if (HANDLE_EINTR(close(fd_pipe_)) < 0) + PLOG(ERROR) << "close " << fd_pipe_; + fd_pipe_ = -1; + } + if (remote_fd_pipe_ != -1) { + if (HANDLE_EINTR(close(remote_fd_pipe_)) < 0) + PLOG(ERROR) << "close " << remote_fd_pipe_; + remote_fd_pipe_ = -1; + } +#endif + + if (uses_fifo_) { + // Unlink the FIFO + unlink(pipe_name_.c_str()); + } + + 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 " << *i; + } + input_overflow_fds_.clear(); } //------------------------------------------------------------------------------ @@ -1133,16 +1102,4 @@ int Channel::GetClientFileDescriptor() const { return channel_impl_->GetClientFileDescriptor(); } -bool Channel::AcceptsConnections() const { - return channel_impl_->AcceptsConnections(); -} - -bool Channel::HasAcceptedConnection() const { - return channel_impl_->HasAcceptedConnection(); -} - -void Channel::ResetToAcceptingConnectionState() { - channel_impl_->ResetToAcceptingConnectionState(); -} - } // namespace IPC diff --git a/ipc/ipc_channel_posix.h b/ipc/ipc_channel_posix.h index 3b8488a..4ff3de1 100644 --- a/ipc/ipc_channel_posix.h +++ b/ipc/ipc_channel_posix.h @@ -40,10 +40,12 @@ 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 IPC::ChannelHandle& channel_handle, Mode mode, + ChannelImpl(const IPC::ChannelHandle &channel_handle, Mode mode, Listener* listener); ~ChannelImpl(); bool Connect(); @@ -51,23 +53,13 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher { void set_listener(Listener* listener) { listener_ = listener; } bool Send(Message* message); int GetClientFileDescriptor() const; - bool AcceptsConnections() const; - bool HasAcceptedConnection() const; - void ResetToAcceptingConnectionState(); private: - bool CreatePipe(const IPC::ChannelHandle& channel_handle, - bool uses_domain_sockets, - bool listening_socket); + bool CreatePipe(const IPC::ChannelHandle &channel_handle, Mode mode); 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); @@ -82,14 +74,17 @@ class Channel::ChannelImpl : public MessageLoopForIO::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. + // If the kTestingChannelID flag is specified, we use a FIFO instead of + // 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. @@ -137,8 +132,10 @@ class Channel::ChannelImpl : public MessageLoopForIO::Watcher { 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_; + // 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_; ScopedRunnableMethodFactory<ChannelImpl> factory_; diff --git a/ipc/ipc_channel_posix_unittest.cc b/ipc/ipc_channel_posix_unittest.cc deleted file mode 100644 index 4b668c4..0000000 --- a/ipc/ipc_channel_posix_unittest.cc +++ /dev/null @@ -1,334 +0,0 @@ -// 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. - -// 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/message_loop.h" -#include "base/scoped_ptr.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 void OnMessageReceived(const IPC::Message& message) { - EXPECT_EQ(message.type(), QUIT_MESSAGE); - status_ = MESSAGE_RECEIVED; - QuitRunLoop(); - } - - virtual void OnChannelConnected(int32 peer_pid) { - status_ = CONNECTED; - if (!quit_only_on_message_) { - QuitRunLoop(); - } - } - - virtual void OnChannelError() { - status_ = CHANNEL_ERROR; - if (!quit_only_on_message_) { - QuitRunLoop(); - } - } - - virtual void OnChannelDenied() { - status_ = DENIED; - if (!quit_only_on_message_) { - QuitRunLoop(); - } - } - - virtual void OnChannelListenError() { - 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__AdvancedConnected"; - -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 }; - server_address.sun_family = AF_UNIX; - memcpy(server_address.sun_path, name.c_str(), name.length() + 1); - if (mode == IPC::Channel::MODE_NAMED_SERVER) { - // Only one server at a time. Cleanup garbage if it exists. - unlink(name.c_str()); - ASSERT_GE(bind(socket_fd, - reinterpret_cast<struct sockaddr *>(&server_address), - SUN_LEN(&server_address)), 0) << name; - ASSERT_GE(listen(socket_fd, SOMAXCONN), 0) << name; - } else if (mode == IPC::Channel::MODE_NAMED_CLIENT) { - ASSERT_GE(connect(socket_fd, - reinterpret_cast<struct sockaddr *>(&server_address), - SUN_LEN(&server_address)), 0) << name; - } 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"); - 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_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_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_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_timeout_ms()); - ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status()); - ASSERT_TRUE(channel.HasAcceptedConnection()); - base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixFailConnectionProc", - false); - ASSERT_TRUE(handle2); - SpinRunLoop(TestTimeouts::action_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()); -} - -// 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_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_timeout_ms()); - EXPECT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status()); - } else { - EXPECT_EQ(IPCChannelPosixTestListener::DISCONNECTED, listener.status()); - } - return 0; -} - diff --git a/ipc/ipc_channel_win.cc b/ipc/ipc_channel_win.cc index 0e7ba1b..e12c521 100644 --- a/ipc/ipc_channel_win.cc +++ b/ipc/ipc_channel_win.cc @@ -106,22 +106,6 @@ Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle &channel_handle, waiting_connect_(mode == MODE_SERVER), processing_incoming_(false), ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) { - switch(mode) { - case MODE_NONE: - LOG(FATAL) << "Bad mode for " << channel_handle.name; - break; - case MODE_SERVER: - case MODE_CLIENT: - break; - case MODE_NAMED_SERVER: - mode = MODE_SERVER; - break; - case MODE_NAMED_CLIENT: - mode = MODE_CLIENT; - break; - // Intentionally no default case here so that the compiler - // will check that we handle all the cases in the enum. - } if (!CreatePipe(channel_handle, mode)) { // The pipe may have been closed already. LOG(WARNING) << "Unable to create pipe named \"" << channel_handle.name << diff --git a/ipc/ipc_message_unittest.cc b/ipc/ipc_message_unittest.cc index 9480fe6..f66751f 100644 --- a/ipc/ipc_message_unittest.cc +++ b/ipc/ipc_message_unittest.cc @@ -2,12 +2,11 @@ // 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 <string.h> #include "base/scoped_ptr.h" #include "base/values.h" +#include "ipc/ipc_message.h" #include "ipc/ipc_message_utils.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/ipc/ipc_switches.cc b/ipc/ipc_switches.cc index bcb1225..2a8f470 100644 --- a/ipc/ipc_switches.cc +++ b/ipc/ipc_switches.cc @@ -11,6 +11,10 @@ namespace switches { // Can't find the switch you are looking for? try looking in // base/base_switches.cc instead. +// On POSIX only: use FIFO for IPC channels so that "unrelated" process +// can connect to a channel, provided it knows its name. For debugging purposes. +const char kIPCUseFIFO[] = "ipc-use-fifo"; + // 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"; diff --git a/ipc/ipc_switches.h b/ipc/ipc_switches.h index 1b754b4..dc34eeb 100644 --- a/ipc/ipc_switches.h +++ b/ipc/ipc_switches.h @@ -12,6 +12,7 @@ namespace switches { +extern const char kIPCUseFIFO[]; extern const char kProcessChannelID[]; extern const char kDebugChildren[]; diff --git a/ipc/ipc_sync_channel_unittest.cc b/ipc/ipc_sync_channel_unittest.cc index 772df0b..5617146 100644 --- a/ipc/ipc_sync_channel_unittest.cc +++ b/ipc/ipc_sync_channel_unittest.cc @@ -4,8 +4,6 @@ // // Unit test for SyncChannel. -#include "ipc/ipc_sync_channel.h" - #include <string> #include <vector> @@ -20,6 +18,7 @@ #include "base/thread.h" #include "base/waitable_event.h" #include "ipc/ipc_message.h" +#include "ipc/ipc_sync_channel.h" #include "ipc/ipc_sync_message_filter.h" #include "ipc/ipc_sync_message_unittest.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/ipc/sync_socket_unittest.cc b/ipc/sync_socket_unittest.cc index 451f307..9448487 100644 --- a/ipc/sync_socket_unittest.cc +++ b/ipc/sync_socket_unittest.cc @@ -2,8 +2,6 @@ // 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> @@ -14,6 +12,7 @@ #endif // defined(OS_LINUX) || defined(OS_MACOSX) #include "base/platform_thread.h" #include "base/process_util.h" +#include "base/sync_socket.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_channel_proxy.h" #include "ipc/ipc_message_utils.h" |