diff options
Diffstat (limited to 'ipc/ipc_channel_posix.cc')
-rw-r--r-- | ipc/ipc_channel_posix.cc | 632 |
1 files changed, 338 insertions, 294 deletions
diff --git a/ipc/ipc_channel_posix.cc b/ipc/ipc_channel_posix.cc index 0d0160b..259b467 100644 --- a/ipc/ipc_channel_posix.cc +++ b/ipc/ipc_channel_posix.cc @@ -33,15 +33,18 @@ namespace IPC { // IPC channels on Windows use named pipes (CreateNamedPipe()) with -// 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. +// channel ids as the pipe names. Channels on POSIX use sockets as +// pipes These don't quite line up. // -// When creating a child subprocess, the parent side of the fork -// arranges it such that the initial control channel ends up on the +// 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 { @@ -128,31 +131,15 @@ 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. -const size_t kMaxPipeNameLength = 104; +// Verify that kMaxPipeNameLength is a decent size. COMPILE_ASSERT(sizeof(((sockaddr_un*)0)->sun_path) >= kMaxPipeNameLength, BAD_SUN_PATH_LENGTH); -// Creates a Fifo with the specified name ready to listen on. -bool CreateServerFifo(const std::string& pipe_name, int* server_listen_fd) { +// 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); @@ -169,7 +156,7 @@ bool CreateServerFifo(const std::string& pipe_name, int* server_listen_fd) { // Make socket non-blocking if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { - PLOG(ERROR) << "fcntl " << pipe_name; + PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name; if (HANDLE_EINTR(close(fd)) < 0) PLOG(ERROR) << "close " << pipe_name; return false; @@ -182,7 +169,9 @@ bool CreateServerFifo(const std::string& pipe_name, int* server_listen_fd) { struct sockaddr_un unix_addr; memset(&unix_addr, 0, sizeof(unix_addr)); unix_addr.sun_family = AF_UNIX; - snprintf(unix_addr.sun_path, kMaxPipeNameLength, "%s", pipe_name.c_str()); + DCHECK_EQ(static_cast<int>(pipe_name.length()), + snprintf(unix_addr.sun_path, kMaxPipeNameLength, "%s", + pipe_name.c_str())); size_t unix_addr_len = offsetof(struct sockaddr_un, sun_path) + strlen(unix_addr.sun_path) + 1; @@ -208,15 +197,15 @@ bool CreateServerFifo(const std::string& pipe_name, int* server_listen_fd) { return true; } -// Accept a connection on a fifo. -bool ServerAcceptFifoConnection(int server_listen_fd, int* server_socket) { +// 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 " << accept_fd; + PLOG(ERROR) << "fcntl(O_NONBLOCK) " << accept_fd; if (HANDLE_EINTR(close(accept_fd)) < 0) PLOG(ERROR) << "close " << accept_fd; return false; @@ -226,7 +215,8 @@ bool ServerAcceptFifoConnection(int server_listen_fd, int* server_socket) { return true; } -bool ClientConnectToFifo(const std::string &pipe_name, int* client_socket) { +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); @@ -244,7 +234,7 @@ bool ClientConnectToFifo(const std::string &pipe_name, int* client_socket) { // Make socket non-blocking if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { - PLOG(ERROR) << "fcntl " << pipe_name; + PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name; if (HANDLE_EINTR(close(fd)) < 0) PLOG(ERROR) << "close " << pipe_name; return false; @@ -254,8 +244,9 @@ bool ClientConnectToFifo(const std::string &pipe_name, int* client_socket) { struct sockaddr_un server_unix_addr; memset(&server_unix_addr, 0, sizeof(server_unix_addr)); server_unix_addr.sun_family = AF_UNIX; - snprintf(server_unix_addr.sun_path, kMaxPipeNameLength, "%s", - pipe_name.c_str()); + DCHECK_EQ(static_cast<int>(pipe_name.length()), + snprintf(server_unix_addr.sun_path, kMaxPipeNameLength, "%s", + pipe_name.c_str())); size_t server_unix_addr_len = offsetof(struct sockaddr_un, sun_path) + strlen(server_unix_addr.sun_path) + 1; @@ -287,7 +278,7 @@ bool SocketWriteErrorIsRecoverable() { return errno == EAGAIN || errno == EMSGSIZE; #else return errno == EAGAIN; -#endif +#endif // OS_MACOSX } } // namespace @@ -297,24 +288,37 @@ 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 +#endif // IPC_USES_READWRITE + pipe_name_(channel_handle.name), listener_(listener), - waiting_connect_(true), + must_unlink_(false), factory_(this) { - if (!CreatePipe(channel_handle, mode_)) { + // 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"; // The pipe may have been closed already. LOG(WARNING) << "Unable to create pipe named \"" << channel_handle.name - << "\" in " << (mode_ == MODE_SERVER ? "server" : "client") - << " mode"; + << "\" in " << modestr << " mode"; } } @@ -322,22 +326,6 @@ 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) { @@ -362,43 +350,58 @@ bool SocketPair(int* fd1, int* fd2) { return true; } -bool Channel::ChannelImpl::CreatePipe(const IPC::ChannelHandle &channel_handle, - Mode mode) { +bool Channel::ChannelImpl::CreatePipe(const IPC::ChannelHandle& channel_handle, + bool uses_domain_sockets, + bool listening_socket) { DCHECK(server_listen_pipe_ == -1 && pipe_ == -1); - 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_)) { + + // 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_)) { return false; } - } else { - if (!ClientConnectToFifo(pipe_name_, &pipe_)) { + } else if (mode_ == MODE_CLIENT) { + if (!CreateClientUnixDomainSocket(pipe_name_, &pipe_)) { return false; } - waiting_connect_ = false; } } else { - // 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_); + 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_); } 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. @@ -412,42 +415,51 @@ 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 { - waiting_connect_ = mode == MODE_SERVER; + LOG(FATAL) << "Unknown mode " << mode_; + return false; } } - // 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 (mode_ == MODE_SERVER) { + if (listening_socket) { + server_listen_pipe_ = pipe_; + pipe_ = -1; } } - #endif - if (!msg->WriteInt(base::GetCurrentProcId())) { - Close(); - 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 // IPC_USES_READWRITE - output_queue_.push(msg.release()); return true; } bool Channel::ChannelImpl::Connect() { - if (mode_ == MODE_SERVER && uses_fifo_) { - if (server_listen_pipe_ == -1) { - return false; - } + 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. MessageLoopForIO::current()->WatchFileDescriptor( server_listen_pipe_, true, @@ -455,21 +467,9 @@ bool Channel::ChannelImpl::Connect() { &server_listen_connection_watcher_, this); } else { - if (pipe_ == -1) { - return false; - } - MessageLoopForIO::current()->WatchFileDescriptor( - pipe_, - true, - MessageLoopForIO::WATCH_READ, - &read_watcher_, - this); - waiting_connect_ = mode_ == MODE_SERVER; + did_connect = AcceptConnection(); } - - if (!waiting_connect_) - return ProcessOutgoingMessages(); - return true; + return did_connect; } bool Channel::ChannelImpl::ProcessIncomingMessages() { @@ -497,7 +497,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { Channel::kReadBufferSize)); msg.msg_controllen = 0; } else -#endif +#endif // IPC_USES_READWRITE { msg.msg_controllen = sizeof(input_cmsg_buf_); bytes_read = HANDLE_EINTR(recvmsg(pipe_, &msg, MSG_DONTWAIT)); @@ -511,7 +511,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { // treat this as a special case to prevent spurious error messages // to the console. return false; -#endif // defined(OS_MACOSX) +#endif // OS_MACOSX } else if (errno == ECONNRESET || errno == EPIPE) { return false; } else { @@ -566,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; @@ -625,51 +625,49 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { // the message has been completely received, but we didn't get // enough file descriptors. #if defined(IPC_USES_READWRITE) - 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; + 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; } - 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 (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 +#endif // IPC_USES_READWRITE error = "Message needs unreceived descriptors"; } @@ -690,11 +688,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 +#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; + PLOG(ERROR) << "close " << i; input_overflow_fds_.clear(); // abort the connection return false; @@ -706,8 +704,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { } DVLOG(2) << "received message on channel @" << this << " with type " << m.type() << " on fd " << pipe_; - if (m.routing_id() == MSG_ROUTING_NONE && - m.type() == HELLO_MESSAGE_TYPE) { + if (IsHelloMessage(&m)) { // The Hello message contains only the process id. void *iter = NULL; int pid; @@ -715,9 +712,9 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { NOTREACHED(); } #if defined(IPC_USES_READWRITE) - 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 + 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 // subsequent file descriptor passing. DCHECK_EQ(m.file_descriptor_set()->size(), 1U); base::FileDescriptor descriptor; @@ -727,7 +724,7 @@ bool Channel::ChannelImpl::ProcessIncomingMessages() { fd_pipe_ = descriptor.fd; CHECK(descriptor.auto_close); } -#endif +#endif // IPC_USES_READWRITE listener_->OnChannelConnected(pid); } else { listener_->OnMessageReceived(m); @@ -755,52 +752,22 @@ 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? - is_blocked_on_write_ = false; - - if (output_queue_.empty()) { + 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()) + @@ -848,9 +815,7 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() { msg->header()->num_fds = static_cast<uint16>(num_fds); #if defined(IPC_USES_READWRITE) - if (!uses_fifo_ && - (msg->routing_id() != MSG_ROUTING_NONE || - msg->type() != HELLO_MESSAGE_TYPE)) { + 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. @@ -864,21 +829,19 @@ bool Channel::ChannelImpl::ProcessOutgoingMessages() { msg->file_descriptor_set()->CommitAll(); } } -#endif +#endif // IPC_USES_READWRITE } if (bytes_written == 1) { fd_written = pipe_; #if defined(IPC_USES_READWRITE) - if (mode_ != MODE_SERVER && !uses_fifo_ && - msg->routing_id() == MSG_ROUTING_NONE && - msg->type() == HELLO_MESSAGE_TYPE) { + if (mode_ != MODE_SERVER && IsHelloMessage(msg)) { DCHECK_EQ(msg->file_descriptor_set()->size(), 1U); } - if (!uses_fifo_ && !msgh.msg_controllen) { + if (!msgh.msg_controllen) { bytes_written = HANDLE_EINTR(write(pipe_, out_bytes, amt_to_write)); } else -#endif +#endif // IPC_USES_READWRITE { bytes_written = HANDLE_EINTR(sendmsg(pipe_, &msgh, MSG_DONTWAIT)); } @@ -940,14 +903,11 @@ bool Channel::ChannelImpl::Send(Message* message) { #ifdef IPC_MESSAGE_LOG_ENABLED Logging::GetInstance()->OnSendMessage(message, ""); -#endif +#endif // IPC_MESSAGE_LOG_ENABLED output_queue_.push(message); - if (!waiting_connect_) { - if (!is_blocked_on_write_) { - if (!ProcessOutgoingMessages()) - return false; - } + if (!is_blocked_on_write_ && !waiting_connect_) { + return ProcessOutgoingMessages(); } return true; @@ -957,43 +917,88 @@ int Channel::ChannelImpl::GetClientFileDescriptor() const { return client_pipe_; } -// 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 (waiting_connect_ && mode_ == MODE_SERVER) { - if (uses_fifo_) { - if (!ServerAcceptFifoConnection(server_listen_pipe_, &pipe_)) { - Close(); - } +bool Channel::ChannelImpl::AcceptsConnections() const { + return server_listen_pipe_ != -1; +} - // No need to watch the listening socket any longer since only one client - // can connect. So unregister with libevent. - server_listen_connection_watcher_.StopWatchingFileDescriptor(); +bool Channel::ChannelImpl::HasAcceptedConnection() const { + return AcceptsConnections() && pipe_ != -1; +} - // Start watching our end of the socket. - MessageLoopForIO::current()->WatchFileDescriptor( - pipe_, - true, - MessageLoopForIO::WATCH_READ, - &read_watcher_, - this); +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 - 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; + while (!output_queue_.empty()) { + Message* m = output_queue_.front(); + output_queue_.pop(); + delete m; } - if (!waiting_connect_ && fd == pipe_) { - if (!ProcessIncomingMessages()) { + // 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. +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_->OnChannelError(); - // The OnChannelError() call may delete this, so we need to exit now. + 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 (!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; + } + if (!ProcessIncomingMessages()) { + ClosePipeOnError(); + } + } else { + NOTREACHED() << "Unknown pipe " << fd; } // If we're a server and handshaking, then we want to make sure that we @@ -1007,68 +1012,95 @@ 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()) { - Close(); + 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); + } +#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. - // Unregister libevent for the listening socket and close it. - server_listen_connection_watcher_.StopWatchingFileDescriptor(); + 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(); } - // 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(); } //------------------------------------------------------------------------------ @@ -1102,4 +1134,16 @@ 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 |