Index: ipc/ipc_channel_posix.cc |
=================================================================== |
--- ipc/ipc_channel_posix.cc (revision 69360) |
+++ ipc/ipc_channel_posix.cc (working copy) |
@@ -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 @@ |
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 @@ |
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 @@ |
// 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 @@ |
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 @@ |
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 @@ |
// 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 @@ |
return errno == EAGAIN || errno == EMSGSIZE; |
#else |
return errno == EAGAIN; |
-#endif // OS_MACOSX |
+#endif |
} |
} // namespace |
@@ -287,36 +297,24 @@ |
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)) { |
+ if (!CreatePipe(channel_handle, mode_)) { |
// 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 " << modestr << " mode"; |
+ << "\" in " << (mode_ == MODE_SERVER ? "server" : "client") |
+ << " mode"; |
} |
} |
@@ -324,6 +322,22 @@ |
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 @@ |
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 @@ |
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 @@ |
&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 @@ |
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 @@ |
// 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 @@ |
<< " 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 @@ |
// 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 (!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; |
+ 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 @@ |
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 @@ |
} |
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 @@ |
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 @@ |
fd_pipe_ = descriptor.fd; |
CHECK(descriptor.auto_close); |
} |
-#endif // IPC_USES_READWRITE |
+#endif |
listener_->OnChannelConnected(pid); |
} else { |
listener_->OnMessageReceived(m); |
@@ -750,22 +755,52 @@ |
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 @@ |
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 @@ |
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 @@ |
#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 @@ |
return client_pipe_; |
} |
-bool Channel::ChannelImpl::AcceptsConnections() const { |
- return server_listen_pipe_ != -1; |
-} |
+// 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::HasAcceptedConnection() const { |
- return AcceptsConnections() && 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(); |
-void Channel::ChannelImpl::ResetToAcceptingConnectionState() { |
- if (pipe_ == -1) |
- return; |
+ // Start watching our end of the socket. |
+ MessageLoopForIO::current()->WatchFileDescriptor( |
+ pipe_, |
+ true, |
+ MessageLoopForIO::WATCH_READ, |
+ &read_watcher_, |
+ this); |
- // 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; |
+ 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 (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. |
-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)) { |
+ if (!waiting_connect_ && fd == pipe_) { |
+ if (!ProcessIncomingMessages()) { |
Close(); |
- 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(); |
+ listener_->OnChannelError(); |
+ // The OnChannelError() call may delete this, so we need to exit now. |
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 |
@@ -1011,95 +1007,68 @@ |
// 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(); |
- } |
+ 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. |
- 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 @@ |
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 |